github.com/markusbkk/elvish@v0.0.0-20231204143114-91dc52438621/pkg/mods/file/file.go (about)

     1  package file
     2  
     3  import (
     4  	"math/big"
     5  	"os"
     6  	"strconv"
     7  
     8  	"github.com/markusbkk/elvish/pkg/eval"
     9  	"github.com/markusbkk/elvish/pkg/eval/errs"
    10  	"github.com/markusbkk/elvish/pkg/eval/vals"
    11  )
    12  
    13  var Ns = eval.BuildNsNamed("file").
    14  	AddGoFns(map[string]interface{}{
    15  		"close":    close,
    16  		"open":     open,
    17  		"pipe":     pipe,
    18  		"truncate": truncate,
    19  	}).Ns()
    20  
    21  //elvdoc:fn open
    22  //
    23  // ```elvish
    24  // file:open $filename
    25  // ```
    26  //
    27  // Opens a file. Currently, `open` only supports opening a file for reading.
    28  // File must be closed with `close` explicitly. Example:
    29  //
    30  // ```elvish-transcript
    31  // ~> cat a.txt
    32  // This is
    33  // a file.
    34  // ~> use file
    35  // ~> var f = (file:open a.txt)
    36  // ~> cat < $f
    37  // This is
    38  // a file.
    39  // ~> close $f
    40  // ```
    41  //
    42  // @cf file:close
    43  
    44  func open(name string) (vals.File, error) {
    45  	return os.Open(name)
    46  }
    47  
    48  //elvdoc:fn close
    49  //
    50  // ```elvish
    51  // file:close $file
    52  // ```
    53  //
    54  // Closes a file opened with `open`.
    55  //
    56  // @cf file:open
    57  
    58  func close(f vals.File) error {
    59  	return f.Close()
    60  }
    61  
    62  //elvdoc:fn pipe
    63  //
    64  // ```elvish
    65  // file:pipe
    66  // ```
    67  //
    68  // Create a new pipe that can be used in redirections. A pipe contains a read-end and write-end.
    69  // Each pipe object is a [pseudo-map](language.html#pseudo-map) with fields `r` (the read-end [file
    70  // object](./language.html#file)) and `w` (the write-end).
    71  //
    72  // When redirecting command input from a pipe with `<`, the read-end is used. When redirecting
    73  // command output to a pipe with `>`, the write-end is used. Redirecting both input and output with
    74  // `<>` to a pipe is not supported.
    75  //
    76  // Pipes have an OS-dependent buffer, so writing to a pipe without an active reader
    77  // does not necessarily block. Pipes **must** be explicitly closed with `file:close`.
    78  //
    79  // Putting values into pipes will cause those values to be discarded.
    80  //
    81  // Examples (assuming the pipe has a large enough buffer):
    82  //
    83  // ```elvish-transcript
    84  // ~> var p = (file:pipe)
    85  // ~> echo 'lorem ipsum' > $p
    86  // ~> head -n1 < $p
    87  // lorem ipsum
    88  // ~> put 'lorem ipsum' > $p
    89  // ~> file:close $p[w] # close the write-end
    90  // ~> head -n1 < $p # blocks unless the write-end is closed
    91  // ~> file:close $p[r] # close the read-end
    92  // ```
    93  //
    94  // @cf file:close
    95  
    96  func pipe() (vals.Pipe, error) {
    97  	r, w, err := os.Pipe()
    98  	return vals.NewPipe(r, w), err
    99  }
   100  
   101  //elvdoc:fn truncate
   102  //
   103  // ```elvish
   104  // file:truncate $filename $size
   105  // ```
   106  //
   107  // changes the size of the named file. If the file is a symbolic link, it
   108  // changes the size of the link's target. The size must be an integer between 0
   109  // and 2^64-1.
   110  
   111  func truncate(name string, rawSize vals.Num) error {
   112  	var size int64
   113  	switch rawSize := rawSize.(type) {
   114  	case int:
   115  		size = int64(rawSize)
   116  	case *big.Int:
   117  		if rawSize.IsInt64() {
   118  			size = rawSize.Int64()
   119  		} else {
   120  			return truncateSizeOutOfRange(rawSize.String())
   121  		}
   122  	default:
   123  		return errs.BadValue{
   124  			What:  "size argument to file:truncate",
   125  			Valid: "integer", Actual: "non-integer",
   126  		}
   127  	}
   128  	if size < 0 {
   129  		return truncateSizeOutOfRange(strconv.FormatInt(size, 10))
   130  	}
   131  	return os.Truncate(name, size)
   132  }
   133  
   134  func truncateSizeOutOfRange(size string) error {
   135  	return errs.OutOfRange{
   136  		What:      "size argument to file:truncate",
   137  		ValidLow:  "0",
   138  		ValidHigh: "2^64-1",
   139  		Actual:    size,
   140  	}
   141  }