file.go 1.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. package file
  2. import (
  3. "math/big"
  4. "os"
  5. "strconv"
  6. "src.elv.sh/pkg/eval"
  7. "src.elv.sh/pkg/eval/errs"
  8. "src.elv.sh/pkg/eval/vals"
  9. "src.elv.sh/pkg/parse"
  10. "src.elv.sh/pkg/sys"
  11. )
  12. var Ns = eval.BuildNsNamed("file").
  13. AddGoFns(map[string]any{
  14. "close": close,
  15. "is-tty": isTTY,
  16. "open": open,
  17. "pipe": pipe,
  18. "truncate": truncate,
  19. }).Ns()
  20. func isTTY(fm *eval.Frame, file any) (bool, error) {
  21. switch file := file.(type) {
  22. case *os.File:
  23. return sys.IsATTY(file.Fd()), nil
  24. case int:
  25. return isTTYPort(fm, file), nil
  26. case string:
  27. var fd int
  28. if err := vals.ScanToGo(file, &fd); err != nil {
  29. return false, errs.BadValue{What: "argument to file:is-tty",
  30. Valid: "file value or numerical FD", Actual: parse.Quote(file)}
  31. }
  32. return isTTYPort(fm, fd), nil
  33. default:
  34. return false, errs.BadValue{What: "argument to file:is-tty",
  35. Valid: "file value or numerical FD", Actual: vals.ToString(file)}
  36. }
  37. }
  38. func isTTYPort(fm *eval.Frame, portNum int) bool {
  39. p := fm.Port(portNum)
  40. return p != nil && sys.IsATTY(p.File.Fd())
  41. }
  42. func open(name string) (vals.File, error) {
  43. return os.Open(name)
  44. }
  45. func close(f vals.File) error {
  46. return f.Close()
  47. }
  48. func pipe() (vals.Pipe, error) {
  49. r, w, err := os.Pipe()
  50. return vals.NewPipe(r, w), err
  51. }
  52. func truncate(name string, rawSize vals.Num) error {
  53. var size int64
  54. switch rawSize := rawSize.(type) {
  55. case int:
  56. size = int64(rawSize)
  57. case *big.Int:
  58. if rawSize.IsInt64() {
  59. size = rawSize.Int64()
  60. } else {
  61. return truncateSizeOutOfRange(rawSize.String())
  62. }
  63. default:
  64. return errs.BadValue{
  65. What: "size argument to file:truncate",
  66. Valid: "integer", Actual: "non-integer",
  67. }
  68. }
  69. if size < 0 {
  70. return truncateSizeOutOfRange(strconv.FormatInt(size, 10))
  71. }
  72. return os.Truncate(name, size)
  73. }
  74. func truncateSizeOutOfRange(size string) error {
  75. return errs.OutOfRange{
  76. What: "size argument to file:truncate",
  77. ValidLow: "0",
  78. ValidHigh: "2^64-1",
  79. Actual: size,
  80. }
  81. }