github.com/kolbycrouch/elvish@v0.14.1-0.20210614162631-215b9ac1c423/pkg/eval/mods/file/file.go (about)

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