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

     1  package eval
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"os"
     9  	"strconv"
    10  	"strings"
    11  
    12  	"src.elv.sh/pkg/diag"
    13  	"src.elv.sh/pkg/eval/vals"
    14  	"src.elv.sh/pkg/strutil"
    15  )
    16  
    17  // Input and output.
    18  
    19  func init() {
    20  	addBuiltinFns(map[string]interface{}{
    21  		// Value output
    22  		"put": put,
    23  
    24  		// Bytes input
    25  		"read-upto": readUpto,
    26  		"read-line": readLine,
    27  
    28  		// Bytes output
    29  		"print":  print,
    30  		"echo":   echo,
    31  		"pprint": pprint,
    32  		"repr":   repr,
    33  		"show":   show,
    34  		"printf": printf,
    35  
    36  		// Only bytes or values
    37  		//
    38  		// These are now implemented as commands forwarding one part of input to
    39  		// output and discarding the other. A future optimization the evaler can
    40  		// do is to connect the relevant parts directly together without any
    41  		// kind of forwarding.
    42  		"only-bytes":  onlyBytes,
    43  		"only-values": onlyValues,
    44  
    45  		// Bytes to value
    46  		"slurp":      slurp,
    47  		"from-lines": fromLines,
    48  		"from-json":  fromJSON,
    49  
    50  		// Value to bytes
    51  		"to-lines": toLines,
    52  		"to-json":  toJSON,
    53  
    54  		// File and pipe
    55  		"fopen":   fopen,
    56  		"fclose":  fclose,
    57  		"pipe":    pipe,
    58  		"prclose": prclose,
    59  		"pwclose": pwclose,
    60  	})
    61  }
    62  
    63  //elvdoc:fn put
    64  //
    65  // ```elvish
    66  // put $value...
    67  // ```
    68  //
    69  // Takes arbitrary arguments and write them to the structured stdout.
    70  //
    71  // Examples:
    72  //
    73  // ```elvish-transcript
    74  // ~> put a
    75  // ▶ a
    76  // ~> put lorem ipsum [a b] { ls }
    77  // ▶ lorem
    78  // ▶ ipsum
    79  // ▶ [a b]
    80  // ▶ <closure 0xc4202607e0>
    81  // ```
    82  //
    83  // Etymology: Various languages, in particular
    84  // [C](https://manpages.debian.org/stretch/manpages-dev/puts.3.en.html) and
    85  // [Ruby](https://ruby-doc.org/core-2.2.2/IO.html#method-i-puts) as `puts`.
    86  
    87  func put(fm *Frame, args ...interface{}) {
    88  	out := fm.OutputChan()
    89  	for _, a := range args {
    90  		out <- a
    91  	}
    92  }
    93  
    94  //elvdoc:fn read-upto
    95  //
    96  // ```elvish
    97  // read-upto $delim
    98  // ```
    99  //
   100  // Reads byte input until `$delim` or end-of-file is encountered, and outputs
   101  // the part of the input read as a string value. The output contains the
   102  // trailing `$delim`, unless `read-upto` terminated at end-of-file.
   103  //
   104  // The `$delim` argument must be a single rune in the ASCII range.
   105  //
   106  // Examples:
   107  //
   108  // ```elvish-transcript
   109  // ~> echo "a,b,c" | read-upto ","
   110  // ▶ 'a,'
   111  // ~> echo "foo\nbar" | read-upto "\n"
   112  // ▶ "foo\n"
   113  // ~> echo "a.elv\x00b.elv" | read-upto "\x00"
   114  // ▶ "a.elv\x00"
   115  // ~> print "foobar" | read-upto "\n"
   116  // ▶ foobar
   117  // ```
   118  
   119  func readUpto(fm *Frame, last string) (string, error) {
   120  	if len(last) != 1 {
   121  		return "", ErrArgs
   122  	}
   123  	in := fm.InputFile()
   124  	var buf []byte
   125  	for {
   126  		var b [1]byte
   127  		_, err := in.Read(b[:])
   128  		if err != nil {
   129  			if err == io.EOF {
   130  				break
   131  			}
   132  			return "", err
   133  		}
   134  		buf = append(buf, b[0])
   135  		if b[0] == last[0] {
   136  			break
   137  		}
   138  	}
   139  	return string(buf), nil
   140  }
   141  
   142  //elvdoc:fn read-line
   143  //
   144  // ```elvish
   145  // read-line
   146  // ```
   147  //
   148  // Reads a single line from byte input, and writes the line to the value output,
   149  // stripping the line ending. A line can end with `"\r\n"`, `"\n"`, or end of
   150  // file. Examples:
   151  //
   152  // ```elvish-transcript
   153  // ~> print line | read-line
   154  // ▶ line
   155  // ~> print "line\n" | read-line
   156  // ▶ line
   157  // ~> print "line\r\n" | read-line
   158  // ▶ line
   159  // ~> print "line-with-extra-cr\r\r\n" | read-line
   160  // ▶ "line-with-extra-cr\r"
   161  // ```
   162  
   163  func readLine(fm *Frame) (string, error) {
   164  	s, err := readUpto(fm, "\n")
   165  	if err != nil {
   166  		return "", err
   167  	}
   168  	return strutil.ChopLineEnding(s), nil
   169  }
   170  
   171  //elvdoc:fn print
   172  //
   173  // ```elvish
   174  // print &sep=' ' $value...
   175  // ```
   176  //
   177  // Like `echo`, just without the newline.
   178  //
   179  // @cf echo
   180  //
   181  // Etymology: Various languages, in particular
   182  // [Perl](https://perldoc.perl.org/functions/print.html) and
   183  // [zsh](http://zsh.sourceforge.net/Doc/Release/Shell-Builtin-Commands.html), whose
   184  // `print`s do not print a trailing newline.
   185  
   186  type printOpts struct{ Sep string }
   187  
   188  func (o *printOpts) SetDefaultOptions() { o.Sep = " " }
   189  
   190  func print(fm *Frame, opts printOpts, args ...interface{}) {
   191  	out := fm.OutputFile()
   192  	for i, arg := range args {
   193  		if i > 0 {
   194  			out.WriteString(opts.Sep)
   195  		}
   196  		out.WriteString(vals.ToString(arg))
   197  	}
   198  }
   199  
   200  //elvdoc:fn printf
   201  //
   202  // ```elvish
   203  // printf $template $value...
   204  // ```
   205  //
   206  // Prints values to the byte stream according to a template.
   207  //
   208  // Like [`print`](#print), this command does not add an implicit newline; use
   209  // an explicit `"\n"` in the formatting template instead.
   210  //
   211  // See Go's [`fmt`](https://golang.org/pkg/fmt/#hdr-Printing) package for
   212  // details about the formatting verbs and the various flags that modify the
   213  // default behavior, such as padding and justification.
   214  //
   215  // Unlike Go, each formatting verb has a single associated internal type, and
   216  // accepts any argument that can reasonably be converted to that type:
   217  //
   218  // - The verbs `%s`, `%q` and `%v` convert the corresponding argument to a
   219  //   string in different ways:
   220  //
   221  //     - `%s` uses [to-string](#to-string) to convert a value to string.
   222  //
   223  //     - `%q` uses [repr](#repr) to convert a value to string.
   224  //
   225  //     - `%v` is equivalent to `%s`, and `%#v` is equivalent to `%q`.
   226  //
   227  // - The verb `%t` first convert the corresponding argument to a boolean using
   228  //   [bool](#bool), and then uses its Go counterpart to format the boolean.
   229  //
   230  // - The verbs `%b`, `%c`, `%d`, `%o`, `%O`, `%x`, `%X` and `%U` first convert
   231  //   the corresponding argument to an integer using an internal algorithm, and
   232  //   use their Go counterparts to format the integer.
   233  //
   234  // - The verbs `%e`, `%E`, `%f`, `%F`, `%g` and `%G` first convert the
   235  //   corresponding argument to a floating-point number using
   236  //   [float64](#float64), and then use their Go counterparts to format the
   237  //   number.
   238  //
   239  // The special verb `%%` prints a literal `%` and consumes no argument.
   240  //
   241  // Verbs not documented above are not supported.
   242  //
   243  // Examples:
   244  //
   245  // ```elvish-transcript
   246  // ~> printf "%10s %.2f\n" Pi $math:pi
   247  //         Pi 3.14
   248  // ~> printf "%-10s %.2f %s\n" Pi $math:pi $math:pi
   249  // Pi         3.14 3.141592653589793
   250  // ~> printf "%d\n" 0b11100111
   251  // 231
   252  // ~> printf "%08b\n" 231
   253  // 11100111
   254  // ~> printf "list is: %q\n" [foo bar 'foo bar']
   255  // list is: [foo bar 'foo bar']
   256  // ```
   257  //
   258  // **Note**: Compared to the [POSIX `printf`
   259  // command](https://pubs.opengroup.org/onlinepubs/007908799/xcu/printf.html)
   260  // found in other shells, there are 3 key differences:
   261  //
   262  // - The behavior of the formatting verbs are based on Go's
   263  //   [`fmt`](https://golang.org/pkg/fmt/) package instead of the POSIX
   264  //   specification.
   265  //
   266  // - The number of arguments after the formatting template must match the number
   267  //   of formatting verbs. The POSIX command will repeat the template string to
   268  //   consume excess values; this command does not have that behavior.
   269  //
   270  // - This command does not interpret escape sequences such as `\n`; just use
   271  //   [double-quoted strings](language.html#double-quoted-string).
   272  //
   273  // @cf print echo pprint repr
   274  
   275  func printf(fm *Frame, template string, args ...interface{}) {
   276  	wrappedArgs := make([]interface{}, len(args))
   277  	for i, arg := range args {
   278  		wrappedArgs[i] = formatter{arg}
   279  	}
   280  
   281  	fmt.Fprintf(fm.OutputFile(), template, wrappedArgs...)
   282  }
   283  
   284  type formatter struct {
   285  	wrapped interface{}
   286  }
   287  
   288  func (f formatter) Format(state fmt.State, r rune) {
   289  	wrapped := f.wrapped
   290  	switch r {
   291  	case 's':
   292  		writeFmt(state, 's', vals.ToString(wrapped))
   293  	case 'q':
   294  		// TODO: Support using the precision flag to specify indentation.
   295  		writeFmt(state, 's', vals.Repr(wrapped, vals.NoPretty))
   296  	case 'v':
   297  		var s string
   298  		if state.Flag('#') {
   299  			s = vals.Repr(wrapped, vals.NoPretty)
   300  		} else {
   301  			s = vals.ToString(wrapped)
   302  		}
   303  		writeFmt(state, 's', s)
   304  	case 't':
   305  		writeFmt(state, 't', vals.Bool(wrapped))
   306  	case 'b', 'c', 'd', 'o', 'O', 'x', 'X', 'U':
   307  		var i int
   308  		if err := vals.ScanToGo(wrapped, &i); err != nil {
   309  			fmt.Fprintf(state, "%%!%c(%s)", r, err.Error())
   310  			return
   311  		}
   312  		writeFmt(state, r, i)
   313  	case 'e', 'E', 'f', 'F', 'g', 'G':
   314  		var f float64
   315  		if err := vals.ScanToGo(wrapped, &f); err != nil {
   316  			fmt.Fprintf(state, "%%!%c(%s)", r, err.Error())
   317  			return
   318  		}
   319  		writeFmt(state, r, f)
   320  	default:
   321  		fmt.Fprintf(state, "%%!%c(unsupported formatting verb)", r)
   322  	}
   323  }
   324  
   325  // Writes to State using the flag it stores, but with a potentially different
   326  // verb and value.
   327  func writeFmt(state fmt.State, v rune, val interface{}) {
   328  	// Reconstruct the verb string.
   329  	var sb strings.Builder
   330  	sb.WriteRune('%')
   331  	for _, f := range "+-# 0" {
   332  		if state.Flag(int(f)) {
   333  			sb.WriteRune(f)
   334  		}
   335  	}
   336  	if w, ok := state.Width(); ok {
   337  		sb.WriteString(strconv.Itoa(w))
   338  	}
   339  	if p, ok := state.Precision(); ok {
   340  		sb.WriteRune('.')
   341  		sb.WriteString(strconv.Itoa(p))
   342  	}
   343  	sb.WriteRune(v)
   344  
   345  	fmt.Fprintf(state, sb.String(), val)
   346  }
   347  
   348  //elvdoc:fn echo
   349  //
   350  // ```elvish
   351  // echo &sep=' ' $value...
   352  // ```
   353  //
   354  // Print all arguments, joined by the `sep` option, and followed by a newline.
   355  //
   356  // Examples:
   357  //
   358  // ```elvish-transcript
   359  // ~> echo Hello   elvish
   360  // Hello elvish
   361  // ~> echo "Hello   elvish"
   362  // Hello   elvish
   363  // ~> echo &sep=, lorem ipsum
   364  // lorem,ipsum
   365  // ```
   366  //
   367  // Notes: The `echo` builtin does not treat `-e` or `-n` specially. For instance,
   368  // `echo -n` just prints `-n`. Use double-quoted strings to print special
   369  // characters, and `print` to suppress the trailing newline.
   370  //
   371  // @cf print
   372  //
   373  // Etymology: Bourne sh.
   374  
   375  func echo(fm *Frame, opts printOpts, args ...interface{}) {
   376  	print(fm, opts, args...)
   377  	fm.OutputFile().WriteString("\n")
   378  }
   379  
   380  //elvdoc:fn pprint
   381  //
   382  // ```elvish
   383  // pprint $value...
   384  // ```
   385  //
   386  // Pretty-print representations of Elvish values. Examples:
   387  //
   388  // ```elvish-transcript
   389  // ~> pprint [foo bar]
   390  // [
   391  // foo
   392  // bar
   393  // ]
   394  // ~> pprint [&k1=v1 &k2=v2]
   395  // [
   396  // &k2=
   397  // v2
   398  // &k1=
   399  // v1
   400  // ]
   401  // ```
   402  //
   403  // The output format is subject to change.
   404  //
   405  // @cf repr
   406  
   407  func pprint(fm *Frame, args ...interface{}) {
   408  	out := fm.OutputFile()
   409  	for _, arg := range args {
   410  		out.WriteString(vals.Repr(arg, 0))
   411  		out.WriteString("\n")
   412  	}
   413  }
   414  
   415  //elvdoc:fn repr
   416  //
   417  // ```elvish
   418  // repr $value...
   419  // ```
   420  //
   421  // Writes representation of `$value`s, separated by space and followed by a
   422  // newline. Example:
   423  //
   424  // ```elvish-transcript
   425  // ~> repr [foo 'lorem ipsum'] "aha\n"
   426  // [foo 'lorem ipsum'] "aha\n"
   427  // ```
   428  //
   429  // @cf pprint
   430  //
   431  // Etymology: [Python](https://docs.python.org/3/library/functions.html#repr).
   432  
   433  func repr(fm *Frame, args ...interface{}) {
   434  	out := fm.OutputFile()
   435  	for i, arg := range args {
   436  		if i > 0 {
   437  			out.WriteString(" ")
   438  		}
   439  		out.WriteString(vals.Repr(arg, vals.NoPretty))
   440  	}
   441  	out.WriteString("\n")
   442  }
   443  
   444  //elvdoc:fn show
   445  //
   446  // ```elvish
   447  // show $e
   448  // ```
   449  //
   450  // Shows the value to the output, which is assumed to be a VT-100-compatible
   451  // terminal.
   452  //
   453  // Currently, the only type of value that can be showed is exceptions, but this
   454  // will likely expand in future.
   455  //
   456  // Example:
   457  //
   458  // ```elvish-transcript
   459  // ~> e = ?(fail lorem-ipsum)
   460  // ~> show $e
   461  // Exception: lorem-ipsum
   462  // [tty 3], line 1: e = ?(fail lorem-ipsum)
   463  // ```
   464  
   465  func show(fm *Frame, v diag.Shower) {
   466  	fm.OutputFile().WriteString(v.Show(""))
   467  	fm.OutputFile().WriteString("\n")
   468  }
   469  
   470  const bytesReadBufferSize = 512
   471  
   472  //elvdoc:fn only-bytes
   473  //
   474  // ```elvish
   475  // only-bytes
   476  // ```
   477  //
   478  // Passes byte input to output, and discards value inputs.
   479  //
   480  // Example:
   481  //
   482  // ```elvish-transcript
   483  // ~> { put value; echo bytes } | only-bytes
   484  // bytes
   485  // ```
   486  
   487  func onlyBytes(fm *Frame) error {
   488  	// Discard values in a goroutine.
   489  	valuesDone := make(chan struct{})
   490  	go func() {
   491  		for range fm.InputChan() {
   492  		}
   493  		close(valuesDone)
   494  	}()
   495  	// Make sure the goroutine has finished before returning.
   496  	defer func() { <-valuesDone }()
   497  
   498  	// Forward bytes.
   499  	buf := make([]byte, bytesReadBufferSize)
   500  	for {
   501  		nr, errRead := fm.InputFile().Read(buf[:])
   502  		if nr > 0 {
   503  			// Even when there are write errors, we will continue reading. So we
   504  			// ignore the error.
   505  			fm.OutputFile().Write(buf[:nr])
   506  		}
   507  		if errRead != nil {
   508  			if errRead == io.EOF {
   509  				return nil
   510  			}
   511  			return errRead
   512  		}
   513  	}
   514  }
   515  
   516  //elvdoc:fn only-values
   517  //
   518  // ```elvish
   519  // only-values
   520  // ```
   521  //
   522  // Passes value input to output, and discards byte inputs.
   523  //
   524  // Example:
   525  //
   526  // ```elvish-transcript
   527  // ~> { put value; echo bytes } | only-values
   528  // ▶ value
   529  // ```
   530  
   531  func onlyValues(fm *Frame) error {
   532  	// Forward values in a goroutine.
   533  	valuesDone := make(chan struct{})
   534  	go func() {
   535  		for v := range fm.InputChan() {
   536  			fm.OutputChan() <- v
   537  		}
   538  		close(valuesDone)
   539  	}()
   540  	// Make sure the goroutine has finished before returning.
   541  	defer func() { <-valuesDone }()
   542  
   543  	// Discard bytes.
   544  	buf := make([]byte, bytesReadBufferSize)
   545  	for {
   546  		_, errRead := fm.InputFile().Read(buf[:])
   547  		if errRead != nil {
   548  			if errRead == io.EOF {
   549  				return nil
   550  			}
   551  			return errRead
   552  		}
   553  	}
   554  }
   555  
   556  //elvdoc:fn slurp
   557  //
   558  // ```elvish
   559  // slurp
   560  // ```
   561  //
   562  // Reads bytes input into a single string, and put this string on structured
   563  // stdout.
   564  //
   565  // Example:
   566  //
   567  // ```elvish-transcript
   568  // ~> echo "a\nb" | slurp
   569  // ▶ "a\nb\n"
   570  // ```
   571  //
   572  // Etymology: Perl, as
   573  // [`File::Slurp`](http://search.cpan.org/~uri/File-Slurp-9999.19/lib/File/Slurp.pm).
   574  
   575  func slurp(fm *Frame) (string, error) {
   576  	b, err := ioutil.ReadAll(fm.InputFile())
   577  	return string(b), err
   578  }
   579  
   580  //elvdoc:fn from-lines
   581  //
   582  // ```elvish
   583  // from-lines
   584  // ```
   585  //
   586  // Splits byte input into lines, and writes them to the value output. Value
   587  // input is ignored.
   588  //
   589  // ```elvish-transcript
   590  // ~> { echo a; echo b } | from-lines
   591  // ▶ a
   592  // ▶ b
   593  // ~> { echo a; put b } | from-lines
   594  // ▶ a
   595  // ```
   596  //
   597  // @cf to-lines
   598  
   599  func fromLines(fm *Frame) {
   600  	linesToChan(fm.InputFile(), fm.OutputChan())
   601  }
   602  
   603  //elvdoc:fn from-json
   604  //
   605  // ```elvish
   606  // from-json
   607  // ```
   608  //
   609  // Takes bytes stdin, parses it as JSON and puts the result on structured stdout.
   610  // The input can contain multiple JSONs, which can, but do not have to, be
   611  // separated with whitespaces.
   612  //
   613  // Examples:
   614  //
   615  // ```elvish-transcript
   616  // ~> echo '"a"' | from-json
   617  // ▶ a
   618  // ~> echo '["lorem", "ipsum"]' | from-json
   619  // ▶ [lorem ipsum]
   620  // ~> echo '{"lorem": "ipsum"}' | from-json
   621  // ▶ [&lorem=ipsum]
   622  // ~> # multiple JSONs running together
   623  // echo '"a""b"["x"]' | from-json
   624  // ▶ a
   625  // ▶ b
   626  // ▶ [x]
   627  // ~> # multiple JSONs separated by newlines
   628  // echo '"a"
   629  // {"k": "v"}' | from-json
   630  // ▶ a
   631  // ▶ [&k=v]
   632  // ```
   633  //
   634  // @cf to-json
   635  
   636  func fromJSON(fm *Frame) error {
   637  	in := fm.InputFile()
   638  	out := fm.OutputChan()
   639  
   640  	dec := json.NewDecoder(in)
   641  	for {
   642  		var v interface{}
   643  		err := dec.Decode(&v)
   644  		if err != nil {
   645  			if err == io.EOF {
   646  				return nil
   647  			}
   648  			return err
   649  		}
   650  		converted, err := fromJSONInterface(v)
   651  		if err != nil {
   652  			return err
   653  		}
   654  		out <- converted
   655  	}
   656  }
   657  
   658  // Converts a interface{} that results from json.Unmarshal to an Elvish value.
   659  func fromJSONInterface(v interface{}) (interface{}, error) {
   660  	switch v := v.(type) {
   661  	case nil, bool, string:
   662  		return v, nil
   663  	case float64:
   664  		return v, nil
   665  	case []interface{}:
   666  		vec := vals.EmptyList
   667  		for _, elem := range v {
   668  			converted, err := fromJSONInterface(elem)
   669  			if err != nil {
   670  				return nil, err
   671  			}
   672  			vec = vec.Cons(converted)
   673  		}
   674  		return vec, nil
   675  	case map[string]interface{}:
   676  		m := vals.EmptyMap
   677  		for key, val := range v {
   678  			convertedVal, err := fromJSONInterface(val)
   679  			if err != nil {
   680  				return nil, err
   681  			}
   682  			m = m.Assoc(key, convertedVal)
   683  		}
   684  		return m, nil
   685  	default:
   686  		return nil, fmt.Errorf("unexpected json type: %T", v)
   687  	}
   688  }
   689  
   690  //elvdoc:fn to-lines
   691  //
   692  // ```elvish
   693  // to-lines $input?
   694  // ```
   695  //
   696  // Writes each value input to a separate line in the byte output. Byte input is
   697  // ignored.
   698  //
   699  // ```elvish-transcript
   700  // ~> put a b | to-lines
   701  // a
   702  // b
   703  // ~> to-lines [a b]
   704  // a
   705  // b
   706  // ~> { put a; echo b } | to-lines
   707  // b
   708  // a
   709  // ```
   710  //
   711  // @cf from-lines
   712  
   713  func toLines(fm *Frame, inputs Inputs) {
   714  	out := fm.OutputFile()
   715  
   716  	inputs(func(v interface{}) {
   717  		fmt.Fprintln(out, vals.ToString(v))
   718  	})
   719  }
   720  
   721  //elvdoc:fn to-json
   722  //
   723  // ```elvish
   724  // to-json
   725  // ```
   726  //
   727  // Takes structured stdin, convert it to JSON and puts the result on bytes stdout.
   728  //
   729  // ```elvish-transcript
   730  // ~> put a | to-json
   731  // "a"
   732  // ~> put [lorem ipsum] | to-json
   733  // ["lorem","ipsum"]
   734  // ~> put [&lorem=ipsum] | to-json
   735  // {"lorem":"ipsum"}
   736  // ```
   737  //
   738  // @cf from-json
   739  
   740  func toJSON(fm *Frame, inputs Inputs) error {
   741  	encoder := json.NewEncoder(fm.OutputFile())
   742  
   743  	var errEncode error
   744  	inputs(func(v interface{}) {
   745  		if errEncode != nil {
   746  			return
   747  		}
   748  		errEncode = encoder.Encode(v)
   749  	})
   750  	return errEncode
   751  }
   752  
   753  //elvdoc:fn fopen
   754  //
   755  // ```elvish
   756  // fopen $filename
   757  // ```
   758  //
   759  // Open a file. Currently, `fopen` only supports opening a file for reading. File
   760  // must be closed with `fclose` explicitly. Example:
   761  //
   762  // ```elvish-transcript
   763  // ~> cat a.txt
   764  // This is
   765  // a file.
   766  // ~> f = (fopen a.txt)
   767  // ~> cat < $f
   768  // This is
   769  // a file.
   770  // ~> fclose $f
   771  // ```
   772  //
   773  // This function is deprecated; use [file:open](./file.html#open) instead.
   774  //
   775  // @cf fclose
   776  
   777  func fopen(name string) (vals.File, error) {
   778  	// TODO support opening files for writing etc as well.
   779  	return os.Open(name)
   780  }
   781  
   782  //elvdoc:fn fclose
   783  //
   784  // ```elvish
   785  // fclose $file
   786  // ```
   787  //
   788  // Close a file opened with `fopen`.
   789  //
   790  // This function is deprecated; use [file:close](./file.html#close) instead.
   791  //
   792  // @cf fopen
   793  
   794  func fclose(f vals.File) error {
   795  	return f.Close()
   796  }
   797  
   798  //elvdoc:fn pipe
   799  //
   800  // ```elvish
   801  // pipe
   802  // ```
   803  //
   804  // Create a new Unix pipe that can be used in redirections.
   805  //
   806  // A pipe contains both the read FD and the write FD. When redirecting command
   807  // input to a pipe with `<`, the read FD is used. When redirecting command output
   808  // to a pipe with `>`, the write FD is used. It is not supported to redirect both
   809  // input and output with `<>` to a pipe.
   810  //
   811  // Pipes have an OS-dependent buffer, so writing to a pipe without an active reader
   812  // does not necessarily block. Pipes **must** be explicitly closed with `prclose`
   813  // and `pwclose`.
   814  //
   815  // Putting values into pipes will cause those values to be discarded.
   816  //
   817  // Examples (assuming the pipe has a large enough buffer):
   818  //
   819  // ```elvish-transcript
   820  // ~> p = (pipe)
   821  // ~> echo 'lorem ipsum' > $p
   822  // ~> head -n1 < $p
   823  // lorem ipsum
   824  // ~> put 'lorem ipsum' > $p
   825  // ~> head -n1 < $p
   826  // # blocks
   827  // # $p should be closed with prclose and pwclose afterwards
   828  // ```
   829  //
   830  // This function is deprecated; use [file:pipe](./file.html#pipe) instead.
   831  //
   832  // @cf prclose pwclose
   833  
   834  func pipe() (vals.Pipe, error) {
   835  	r, w, err := os.Pipe()
   836  	return vals.NewPipe(r, w), err
   837  }
   838  
   839  //elvdoc:fn prclose
   840  //
   841  // ```elvish
   842  // prclose $pipe
   843  // ```
   844  //
   845  // Close the read end of a pipe.
   846  //
   847  // This function is deprecated; use [file:prclose](./file.html#prclose) instead.
   848  //
   849  // @cf pwclose pipe
   850  
   851  func prclose(p vals.Pipe) error {
   852  	return p.ReadEnd.Close()
   853  }
   854  
   855  //elvdoc:fn pwclose
   856  //
   857  // ```elvish
   858  // pwclose $pipe
   859  // ```
   860  //
   861  // Close the write end of a pipe.
   862  //
   863  // This function is deprecated; use [file:pwclose](./file.html#pwclose) instead.
   864  //
   865  // @cf prclose pipe
   866  
   867  func pwclose(p vals.Pipe) error {
   868  	return p.WriteEnd.Close()
   869  }