Advertisement
kosx

Google cmd exec.go

Dec 30th, 2020
1,784
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Go 22.82 KB | None | 0 0
  1. // Copyright 2009 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4.  
  5. // Package exec runs external commands. It wraps os.StartProcess to make it
  6. // easier to remap stdin and stdout, connect I/O with pipes, and do other
  7. // adjustments.
  8. //
  9. // Unlike the "system" library call from C and other languages, the
  10. // os/exec package intentionally does not invoke the system shell and
  11. // does not expand any glob patterns or handle other expansions,
  12. // pipelines, or redirections typically done by shells. The package
  13. // behaves more like C's "exec" family of functions. To expand glob
  14. // patterns, either call the shell directly, taking care to escape any
  15. // dangerous input, or use the path/filepath package's Glob function.
  16. // To expand environment variables, use package os's ExpandEnv.
  17. //
  18. // Note that the examples in this package assume a Unix system.
  19. // They may not run on Windows, and they do not run in the Go Playground
  20. // used by golang.org and godoc.org.
  21. package exec
  22.  
  23. import (
  24.     "bytes"
  25.     "context"
  26.     "errors"
  27.     "internal/syscall/execenv"
  28.     "io"
  29.     "os"
  30.     "path/filepath"
  31.     "runtime"
  32.     "strconv"
  33.     "strings"
  34.     "sync"
  35.     "syscall"
  36. )
  37.  
  38. // Error is returned by LookPath when it fails to classify a file as an
  39. // executable.
  40. type Error struct {
  41.     // Name is the file name for which the error occurred.
  42.     Name string
  43.     // Err is the underlying error.
  44.     Err error
  45. }
  46.  
  47. func (e *Error) Error() string {
  48.     return "exec: " + strconv.Quote(e.Name) + ": " + e.Err.Error()
  49. }
  50.  
  51. func (e *Error) Unwrap() error { return e.Err }
  52.  
  53. // Cmd represents an external command being prepared or run.
  54. //
  55. // A Cmd cannot be reused after calling its Run, Output or CombinedOutput
  56. // methods.
  57. type Cmd struct {
  58.     // Path is the path of the command to run.
  59.     //
  60.     // This is the only field that must be set to a non-zero
  61.     // value. If Path is relative, it is evaluated relative
  62.     // to Dir.
  63.     Path string
  64.  
  65.     // Args holds command line arguments, including the command as Args[0].
  66.     // If the Args field is empty or nil, Run uses {Path}.
  67.     //
  68.     // In typical use, both Path and Args are set by calling Command.
  69.     Args []string
  70.  
  71.     // Env specifies the environment of the process.
  72.     // Each entry is of the form "key=value".
  73.     // If Env is nil, the new process uses the current process's
  74.     // environment.
  75.     // If Env contains duplicate environment keys, only the last
  76.     // value in the slice for each duplicate key is used.
  77.     // As a special case on Windows, SYSTEMROOT is always added if
  78.     // missing and not explicitly set to the empty string.
  79.     Env []string
  80.  
  81.     // Dir specifies the working directory of the command.
  82.     // If Dir is the empty string, Run runs the command in the
  83.     // calling process's current directory.
  84.     Dir string
  85.  
  86.     // Stdin specifies the process's standard input.
  87.     //
  88.     // If Stdin is nil, the process reads from the null device (os.DevNull).
  89.     //
  90.     // If Stdin is an *os.File, the process's standard input is connected
  91.     // directly to that file.
  92.     //
  93.     // Otherwise, during the execution of the command a separate
  94.     // goroutine reads from Stdin and delivers that data to the command
  95.     // over a pipe. In this case, Wait does not complete until the goroutine
  96.     // stops copying, either because it has reached the end of Stdin
  97.     // (EOF or a read error) or because writing to the pipe returned an error.
  98.     Stdin io.Reader
  99.  
  100.     // Stdout and Stderr specify the process's standard output and error.
  101.     //
  102.     // If either is nil, Run connects the corresponding file descriptor
  103.     // to the null device (os.DevNull).
  104.     //
  105.     // If either is an *os.File, the corresponding output from the process
  106.     // is connected directly to that file.
  107.     //
  108.     // Otherwise, during the execution of the command a separate goroutine
  109.     // reads from the process over a pipe and delivers that data to the
  110.     // corresponding Writer. In this case, Wait does not complete until the
  111.     // goroutine reaches EOF or encounters an error.
  112.     //
  113.     // If Stdout and Stderr are the same writer, and have a type that can
  114.     // be compared with ==, at most one goroutine at a time will call Write.
  115.     Stdout io.Writer
  116.     Stderr io.Writer
  117.  
  118.     // ExtraFiles specifies additional open files to be inherited by the
  119.     // new process. It does not include standard input, standard output, or
  120.     // standard error. If non-nil, entry i becomes file descriptor 3+i.
  121.     //
  122.     // ExtraFiles is not supported on Windows.
  123.     ExtraFiles []*os.File
  124.  
  125.     // SysProcAttr holds optional, operating system-specific attributes.
  126.     // Run passes it to os.StartProcess as the os.ProcAttr's Sys field.
  127.     SysProcAttr *syscall.SysProcAttr
  128.  
  129.     // Process is the underlying process, once started.
  130.     Process *os.Process
  131.  
  132.     // ProcessState contains information about an exited process,
  133.     // available after a call to Wait or Run.
  134.     ProcessState *os.ProcessState
  135.  
  136.     ctx             context.Context // nil means none
  137.     lookPathErr     error           // LookPath error, if any.
  138.     finished        bool            // when Wait was called
  139.     childFiles      []*os.File
  140.     closeAfterStart []io.Closer
  141.     closeAfterWait  []io.Closer
  142.     goroutine       []func() error
  143.     errch           chan error // one send per goroutine
  144.     waitDone        chan struct{}
  145. }
  146.  
  147. // Command returns the Cmd struct to execute the named program with
  148. // the given arguments.
  149. //
  150. // It sets only the Path and Args in the returned structure.
  151. //
  152. // If name contains no path separators, Command uses LookPath to
  153. // resolve name to a complete path if possible. Otherwise it uses name
  154. // directly as Path.
  155. //
  156. // The returned Cmd's Args field is constructed from the command name
  157. // followed by the elements of arg, so arg should not include the
  158. // command name itself. For example, Command("echo", "hello").
  159. // Args[0] is always name, not the possibly resolved Path.
  160. //
  161. // On Windows, processes receive the whole command line as a single string
  162. // and do their own parsing. Command combines and quotes Args into a command
  163. // line string with an algorithm compatible with applications using
  164. // CommandLineToArgvW (which is the most common way). Notable exceptions are
  165. // msiexec.exe and cmd.exe (and thus, all batch files), which have a different
  166. // unquoting algorithm. In these or other similar cases, you can do the
  167. // quoting yourself and provide the full command line in SysProcAttr.CmdLine,
  168. // leaving Args empty.
  169. func Command(name string, arg ...string) *Cmd {
  170.     cmd := &Cmd{
  171.         Path: name,
  172.         Args: append([]string{name}, arg...),
  173.     }
  174.     if filepath.Base(name) == name {
  175.         if lp, err := LookPath(name); err != nil {
  176.             cmd.lookPathErr = err
  177.         } else {
  178.             cmd.Path = lp
  179.         }
  180.     }
  181.     return cmd
  182. }
  183.  
  184. // CommandContext is like Command but includes a context.
  185. //
  186. // The provided context is used to kill the process (by calling
  187. // os.Process.Kill) if the context becomes done before the command
  188. // completes on its own.
  189. func CommandContext(ctx context.Context, name string, arg ...string) *Cmd {
  190.     if ctx == nil {
  191.         panic("nil Context")
  192.     }
  193.     cmd := Command(name, arg...)
  194.     cmd.ctx = ctx
  195.     return cmd
  196. }
  197.  
  198. // String returns a human-readable description of c.
  199. // It is intended only for debugging.
  200. // In particular, it is not suitable for use as input to a shell.
  201. // The output of String may vary across Go releases.
  202. func (c *Cmd) String() string {
  203.     if c.lookPathErr != nil {
  204.         // failed to resolve path; report the original requested path (plus args)
  205.         return strings.Join(c.Args, " ")
  206.     }
  207.     // report the exact executable path (plus args)
  208.     b := new(strings.Builder)
  209.     b.WriteString(c.Path)
  210.     for _, a := range c.Args[1:] {
  211.         b.WriteByte(' ')
  212.         b.WriteString(a)
  213.     }
  214.     return b.String()
  215. }
  216.  
  217. // interfaceEqual protects against panics from doing equality tests on
  218. // two interfaces with non-comparable underlying types.
  219. func interfaceEqual(a, b interface{}) bool {
  220.     defer func() {
  221.         recover()
  222.     }()
  223.     return a == b
  224. }
  225.  
  226. func (c *Cmd) envv() ([]string, error) {
  227.     if c.Env != nil {
  228.         return c.Env, nil
  229.     }
  230.     return execenv.Default(c.SysProcAttr)
  231. }
  232.  
  233. func (c *Cmd) argv() []string {
  234.     if len(c.Args) > 0 {
  235.         return c.Args
  236.     }
  237.     return []string{c.Path}
  238. }
  239.  
  240. // skipStdinCopyError optionally specifies a function which reports
  241. // whether the provided stdin copy error should be ignored.
  242. var skipStdinCopyError func(error) bool
  243.  
  244. func (c *Cmd) stdin() (f *os.File, err error) {
  245.     if c.Stdin == nil {
  246.         f, err = os.Open(os.DevNull)
  247.         if err != nil {
  248.             return
  249.         }
  250.         c.closeAfterStart = append(c.closeAfterStart, f)
  251.         return
  252.     }
  253.  
  254.     if f, ok := c.Stdin.(*os.File); ok {
  255.         return f, nil
  256.     }
  257.  
  258.     pr, pw, err := os.Pipe()
  259.     if err != nil {
  260.         return
  261.     }
  262.  
  263.     c.closeAfterStart = append(c.closeAfterStart, pr)
  264.     c.closeAfterWait = append(c.closeAfterWait, pw)
  265.     c.goroutine = append(c.goroutine, func() error {
  266.         _, err := io.Copy(pw, c.Stdin)
  267.         if skip := skipStdinCopyError; skip != nil && skip(err) {
  268.             err = nil
  269.         }
  270.         if err1 := pw.Close(); err == nil {
  271.             err = err1
  272.         }
  273.         return err
  274.     })
  275.     return pr, nil
  276. }
  277.  
  278. func (c *Cmd) stdout() (f *os.File, err error) {
  279.     return c.writerDescriptor(c.Stdout)
  280. }
  281.  
  282. func (c *Cmd) stderr() (f *os.File, err error) {
  283.     if c.Stderr != nil && interfaceEqual(c.Stderr, c.Stdout) {
  284.         return c.childFiles[1], nil
  285.     }
  286.     return c.writerDescriptor(c.Stderr)
  287. }
  288.  
  289. func (c *Cmd) writerDescriptor(w io.Writer) (f *os.File, err error) {
  290.     if w == nil {
  291.         f, err = os.OpenFile(os.DevNull, os.O_WRONLY, 0)
  292.         if err != nil {
  293.             return
  294.         }
  295.         c.closeAfterStart = append(c.closeAfterStart, f)
  296.         return
  297.     }
  298.  
  299.     if f, ok := w.(*os.File); ok {
  300.         return f, nil
  301.     }
  302.  
  303.     pr, pw, err := os.Pipe()
  304.     if err != nil {
  305.         return
  306.     }
  307.  
  308.     c.closeAfterStart = append(c.closeAfterStart, pw)
  309.     c.closeAfterWait = append(c.closeAfterWait, pr)
  310.     c.goroutine = append(c.goroutine, func() error {
  311.         _, err := io.Copy(w, pr)
  312.         pr.Close() // in case io.Copy stopped due to write error
  313.         return err
  314.     })
  315.     return pw, nil
  316. }
  317.  
  318. func (c *Cmd) closeDescriptors(closers []io.Closer) {
  319.     for _, fd := range closers {
  320.         fd.Close()
  321.     }
  322. }
  323.  
  324. // Run starts the specified command and waits for it to complete.
  325. //
  326. // The returned error is nil if the command runs, has no problems
  327. // copying stdin, stdout, and stderr, and exits with a zero exit
  328. // status.
  329. //
  330. // If the command starts but does not complete successfully, the error is of
  331. // type *ExitError. Other error types may be returned for other situations.
  332. //
  333. // If the calling goroutine has locked the operating system thread
  334. // with runtime.LockOSThread and modified any inheritable OS-level
  335. // thread state (for example, Linux or Plan 9 name spaces), the new
  336. // process will inherit the caller's thread state.
  337. func (c *Cmd) Run() error {
  338.     if err := c.Start(); err != nil {
  339.         return err
  340.     }
  341.     return c.Wait()
  342. }
  343.  
  344. // lookExtensions finds windows executable by its dir and path.
  345. // It uses LookPath to try appropriate extensions.
  346. // lookExtensions does not search PATH, instead it converts `prog` into `.\prog`.
  347. func lookExtensions(path, dir string) (string, error) {
  348.     if filepath.Base(path) == path {
  349.         path = filepath.Join(".", path)
  350.     }
  351.     if dir == "" {
  352.         return LookPath(path)
  353.     }
  354.     if filepath.VolumeName(path) != "" {
  355.         return LookPath(path)
  356.     }
  357.     if len(path) > 1 && os.IsPathSeparator(path[0]) {
  358.         return LookPath(path)
  359.     }
  360.     dirandpath := filepath.Join(dir, path)
  361.     // We assume that LookPath will only add file extension.
  362.     lp, err := LookPath(dirandpath)
  363.     if err != nil {
  364.         return "", err
  365.     }
  366.     ext := strings.TrimPrefix(lp, dirandpath)
  367.     return path + ext, nil
  368. }
  369.  
  370. // Start starts the specified command but does not wait for it to complete.
  371. //
  372. // If Start returns successfully, the c.Process field will be set.
  373. //
  374. // The Wait method will return the exit code and release associated resources
  375. // once the command exits.
  376. func (c *Cmd) Start() error {
  377.     if c.lookPathErr != nil {
  378.         c.closeDescriptors(c.closeAfterStart)
  379.         c.closeDescriptors(c.closeAfterWait)
  380.         return c.lookPathErr
  381.     }
  382.     if runtime.GOOS == "windows" {
  383.         lp, err := lookExtensions(c.Path, c.Dir)
  384.         if err != nil {
  385.             c.closeDescriptors(c.closeAfterStart)
  386.             c.closeDescriptors(c.closeAfterWait)
  387.             return err
  388.         }
  389.         c.Path = lp
  390.     }
  391.     if c.Process != nil {
  392.         return errors.New("exec: already started")
  393.     }
  394.     if c.ctx != nil {
  395.         select {
  396.         case <-c.ctx.Done():
  397.             c.closeDescriptors(c.closeAfterStart)
  398.             c.closeDescriptors(c.closeAfterWait)
  399.             return c.ctx.Err()
  400.         default:
  401.         }
  402.     }
  403.  
  404.     c.childFiles = make([]*os.File, 0, 3+len(c.ExtraFiles))
  405.     type F func(*Cmd) (*os.File, error)
  406.     for _, setupFd := range []F{(*Cmd).stdin, (*Cmd).stdout, (*Cmd).stderr} {
  407.         fd, err := setupFd(c)
  408.         if err != nil {
  409.             c.closeDescriptors(c.closeAfterStart)
  410.             c.closeDescriptors(c.closeAfterWait)
  411.             return err
  412.         }
  413.         c.childFiles = append(c.childFiles, fd)
  414.     }
  415.     c.childFiles = append(c.childFiles, c.ExtraFiles...)
  416.  
  417.     envv, err := c.envv()
  418.     if err != nil {
  419.         return err
  420.     }
  421.  
  422.     c.Process, err = os.StartProcess(c.Path, c.argv(), &os.ProcAttr{
  423.         Dir:   c.Dir,
  424.         Files: c.childFiles,
  425.         Env:   addCriticalEnv(dedupEnv(envv)),
  426.         Sys:   c.SysProcAttr,
  427.     })
  428.     if err != nil {
  429.         c.closeDescriptors(c.closeAfterStart)
  430.         c.closeDescriptors(c.closeAfterWait)
  431.         return err
  432.     }
  433.  
  434.     c.closeDescriptors(c.closeAfterStart)
  435.  
  436.     // Don't allocate the channel unless there are goroutines to fire.
  437.     if len(c.goroutine) > 0 {
  438.         c.errch = make(chan error, len(c.goroutine))
  439.         for _, fn := range c.goroutine {
  440.             go func(fn func() error) {
  441.                 c.errch <- fn()
  442.             }(fn)
  443.         }
  444.     }
  445.  
  446.     if c.ctx != nil {
  447.         c.waitDone = make(chan struct{})
  448.         go func() {
  449.             select {
  450.             case <-c.ctx.Done():
  451.                 c.Process.Kill()
  452.             case <-c.waitDone:
  453.             }
  454.         }()
  455.     }
  456.  
  457.     return nil
  458. }
  459.  
  460. // An ExitError reports an unsuccessful exit by a command.
  461. type ExitError struct {
  462.     *os.ProcessState
  463.  
  464.     // Stderr holds a subset of the standard error output from the
  465.     // Cmd.Output method if standard error was not otherwise being
  466.     // collected.
  467.     //
  468.     // If the error output is long, Stderr may contain only a prefix
  469.     // and suffix of the output, with the middle replaced with
  470.     // text about the number of omitted bytes.
  471.     //
  472.     // Stderr is provided for debugging, for inclusion in error messages.
  473.     // Users with other needs should redirect Cmd.Stderr as needed.
  474.     Stderr []byte
  475. }
  476.  
  477. func (e *ExitError) Error() string {
  478.     return e.ProcessState.String()
  479. }
  480.  
  481. // Wait waits for the command to exit and waits for any copying to
  482. // stdin or copying from stdout or stderr to complete.
  483. //
  484. // The command must have been started by Start.
  485. //
  486. // The returned error is nil if the command runs, has no problems
  487. // copying stdin, stdout, and stderr, and exits with a zero exit
  488. // status.
  489. //
  490. // If the command fails to run or doesn't complete successfully, the
  491. // error is of type *ExitError. Other error types may be
  492. // returned for I/O problems.
  493. //
  494. // If any of c.Stdin, c.Stdout or c.Stderr are not an *os.File, Wait also waits
  495. // for the respective I/O loop copying to or from the process to complete.
  496. //
  497. // Wait releases any resources associated with the Cmd.
  498. func (c *Cmd) Wait() error {
  499.     if c.Process == nil {
  500.         return errors.New("exec: not started")
  501.     }
  502.     if c.finished {
  503.         return errors.New("exec: Wait was already called")
  504.     }
  505.     c.finished = true
  506.  
  507.     state, err := c.Process.Wait()
  508.     if c.waitDone != nil {
  509.         close(c.waitDone)
  510.     }
  511.     c.ProcessState = state
  512.  
  513.     var copyError error
  514.     for range c.goroutine {
  515.         if err := <-c.errch; err != nil && copyError == nil {
  516.             copyError = err
  517.         }
  518.     }
  519.  
  520.     c.closeDescriptors(c.closeAfterWait)
  521.  
  522.     if err != nil {
  523.         return err
  524.     } else if !state.Success() {
  525.         return &ExitError{ProcessState: state}
  526.     }
  527.  
  528.     return copyError
  529. }
  530.  
  531. // Output runs the command and returns its standard output.
  532. // Any returned error will usually be of type *ExitError.
  533. // If c.Stderr was nil, Output populates ExitError.Stderr.
  534. func (c *Cmd) Output() ([]byte, error) {
  535.     if c.Stdout != nil {
  536.         return nil, errors.New("exec: Stdout already set")
  537.     }
  538.     var stdout bytes.Buffer
  539.     c.Stdout = &stdout
  540.  
  541.     captureErr := c.Stderr == nil
  542.     if captureErr {
  543.         c.Stderr = &prefixSuffixSaver{N: 32 << 10}
  544.     }
  545.  
  546.     err := c.Run()
  547.     if err != nil && captureErr {
  548.         if ee, ok := err.(*ExitError); ok {
  549.             ee.Stderr = c.Stderr.(*prefixSuffixSaver).Bytes()
  550.         }
  551.     }
  552.     return stdout.Bytes(), err
  553. }
  554.  
  555. // CombinedOutput runs the command and returns its combined standard
  556. // output and standard error.
  557. func (c *Cmd) CombinedOutput() ([]byte, error) {
  558.     if c.Stdout != nil {
  559.         return nil, errors.New("exec: Stdout already set")
  560.     }
  561.     if c.Stderr != nil {
  562.         return nil, errors.New("exec: Stderr already set")
  563.     }
  564.     var b bytes.Buffer
  565.     c.Stdout = &b
  566.     c.Stderr = &b
  567.     err := c.Run()
  568.     return b.Bytes(), err
  569. }
  570.  
  571. // StdinPipe returns a pipe that will be connected to the command's
  572. // standard input when the command starts.
  573. // The pipe will be closed automatically after Wait sees the command exit.
  574. // A caller need only call Close to force the pipe to close sooner.
  575. // For example, if the command being run will not exit until standard input
  576. // is closed, the caller must close the pipe.
  577. func (c *Cmd) StdinPipe() (io.WriteCloser, error) {
  578.     if c.Stdin != nil {
  579.         return nil, errors.New("exec: Stdin already set")
  580.     }
  581.     if c.Process != nil {
  582.         return nil, errors.New("exec: StdinPipe after process started")
  583.     }
  584.     pr, pw, err := os.Pipe()
  585.     if err != nil {
  586.         return nil, err
  587.     }
  588.     c.Stdin = pr
  589.     c.closeAfterStart = append(c.closeAfterStart, pr)
  590.     wc := &closeOnce{File: pw}
  591.     c.closeAfterWait = append(c.closeAfterWait, wc)
  592.     return wc, nil
  593. }
  594.  
  595. type closeOnce struct {
  596.     *os.File
  597.  
  598.     once sync.Once
  599.     err  error
  600. }
  601.  
  602. func (c *closeOnce) Close() error {
  603.     c.once.Do(c.close)
  604.     return c.err
  605. }
  606.  
  607. func (c *closeOnce) close() {
  608.     c.err = c.File.Close()
  609. }
  610.  
  611. // StdoutPipe returns a pipe that will be connected to the command's
  612. // standard output when the command starts.
  613. //
  614. // Wait will close the pipe after seeing the command exit, so most callers
  615. // need not close the pipe themselves. It is thus incorrect to call Wait
  616. // before all reads from the pipe have completed.
  617. // For the same reason, it is incorrect to call Run when using StdoutPipe.
  618. // See the example for idiomatic usage.
  619. func (c *Cmd) StdoutPipe() (io.ReadCloser, error) {
  620.     if c.Stdout != nil {
  621.         return nil, errors.New("exec: Stdout already set")
  622.     }
  623.     if c.Process != nil {
  624.         return nil, errors.New("exec: StdoutPipe after process started")
  625.     }
  626.     pr, pw, err := os.Pipe()
  627.     if err != nil {
  628.         return nil, err
  629.     }
  630.     c.Stdout = pw
  631.     c.closeAfterStart = append(c.closeAfterStart, pw)
  632.     c.closeAfterWait = append(c.closeAfterWait, pr)
  633.     return pr, nil
  634. }
  635.  
  636. // StderrPipe returns a pipe that will be connected to the command's
  637. // standard error when the command starts.
  638. //
  639. // Wait will close the pipe after seeing the command exit, so most callers
  640. // need not close the pipe themselves. It is thus incorrect to call Wait
  641. // before all reads from the pipe have completed.
  642. // For the same reason, it is incorrect to use Run when using StderrPipe.
  643. // See the StdoutPipe example for idiomatic usage.
  644. func (c *Cmd) StderrPipe() (io.ReadCloser, error) {
  645.     if c.Stderr != nil {
  646.         return nil, errors.New("exec: Stderr already set")
  647.     }
  648.     if c.Process != nil {
  649.         return nil, errors.New("exec: StderrPipe after process started")
  650.     }
  651.     pr, pw, err := os.Pipe()
  652.     if err != nil {
  653.         return nil, err
  654.     }
  655.     c.Stderr = pw
  656.     c.closeAfterStart = append(c.closeAfterStart, pw)
  657.     c.closeAfterWait = append(c.closeAfterWait, pr)
  658.     return pr, nil
  659. }
  660.  
  661. // prefixSuffixSaver is an io.Writer which retains the first N bytes
  662. // and the last N bytes written to it. The Bytes() methods reconstructs
  663. // it with a pretty error message.
  664. type prefixSuffixSaver struct {
  665.     N         int // max size of prefix or suffix
  666.     prefix    []byte
  667.     suffix    []byte // ring buffer once len(suffix) == N
  668.     suffixOff int    // offset to write into suffix
  669.     skipped   int64
  670.  
  671.     // TODO(bradfitz): we could keep one large []byte and use part of it for
  672.     // the prefix, reserve space for the '... Omitting N bytes ...' message,
  673.     // then the ring buffer suffix, and just rearrange the ring buffer
  674.     // suffix when Bytes() is called, but it doesn't seem worth it for
  675.     // now just for error messages. It's only ~64KB anyway.
  676. }
  677.  
  678. func (w *prefixSuffixSaver) Write(p []byte) (n int, err error) {
  679.     lenp := len(p)
  680.     p = w.fill(&w.prefix, p)
  681.  
  682.     // Only keep the last w.N bytes of suffix data.
  683.     if overage := len(p) - w.N; overage > 0 {
  684.         p = p[overage:]
  685.         w.skipped += int64(overage)
  686.     }
  687.     p = w.fill(&w.suffix, p)
  688.  
  689.     // w.suffix is full now if p is non-empty. Overwrite it in a circle.
  690.     for len(p) > 0 { // 0, 1, or 2 iterations.
  691.         n := copy(w.suffix[w.suffixOff:], p)
  692.         p = p[n:]
  693.         w.skipped += int64(n)
  694.         w.suffixOff += n
  695.         if w.suffixOff == w.N {
  696.             w.suffixOff = 0
  697.         }
  698.     }
  699.     return lenp, nil
  700. }
  701.  
  702. // fill appends up to len(p) bytes of p to *dst, such that *dst does not
  703. // grow larger than w.N. It returns the un-appended suffix of p.
  704. func (w *prefixSuffixSaver) fill(dst *[]byte, p []byte) (pRemain []byte) {
  705.     if remain := w.N - len(*dst); remain > 0 {
  706.         add := minInt(len(p), remain)
  707.         *dst = append(*dst, p[:add]...)
  708.         p = p[add:]
  709.     }
  710.     return p
  711. }
  712.  
  713. func (w *prefixSuffixSaver) Bytes() []byte {
  714.     if w.suffix == nil {
  715.         return w.prefix
  716.     }
  717.     if w.skipped == 0 {
  718.         return append(w.prefix, w.suffix...)
  719.     }
  720.     var buf bytes.Buffer
  721.     buf.Grow(len(w.prefix) + len(w.suffix) + 50)
  722.     buf.Write(w.prefix)
  723.     buf.WriteString("\n... omitting ")
  724.     buf.WriteString(strconv.FormatInt(w.skipped, 10))
  725.     buf.WriteString(" bytes ...\n")
  726.     buf.Write(w.suffix[w.suffixOff:])
  727.     buf.Write(w.suffix[:w.suffixOff])
  728.     return buf.Bytes()
  729. }
  730.  
  731. func minInt(a, b int) int {
  732.     if a < b {
  733.         return a
  734.     }
  735.     return b
  736. }
  737.  
  738. // dedupEnv returns a copy of env with any duplicates removed, in favor of
  739. // later values.
  740. // Items not of the normal environment "key=value" form are preserved unchanged.
  741. func dedupEnv(env []string) []string {
  742.     return dedupEnvCase(runtime.GOOS == "windows", env)
  743. }
  744.  
  745. // dedupEnvCase is dedupEnv with a case option for testing.
  746. // If caseInsensitive is true, the case of keys is ignored.
  747. func dedupEnvCase(caseInsensitive bool, env []string) []string {
  748.     out := make([]string, 0, len(env))
  749.     saw := make(map[string]int, len(env)) // key => index into out
  750.     for _, kv := range env {
  751.         eq := strings.Index(kv, "=")
  752.         if eq < 0 {
  753.             out = append(out, kv)
  754.             continue
  755.         }
  756.         k := kv[:eq]
  757.         if caseInsensitive {
  758.             k = strings.ToLower(k)
  759.         }
  760.         if dupIdx, isDup := saw[k]; isDup {
  761.             out[dupIdx] = kv
  762.             continue
  763.         }
  764.         saw[k] = len(out)
  765.         out = append(out, kv)
  766.     }
  767.     return out
  768. }
  769.  
  770. // addCriticalEnv adds any critical environment variables that are required
  771. // (or at least almost always required) on the operating system.
  772. // Currently this is only used for Windows.
  773. func addCriticalEnv(env []string) []string {
  774.     if runtime.GOOS != "windows" {
  775.         return env
  776.     }
  777.     for _, kv := range env {
  778.         eq := strings.Index(kv, "=")
  779.         if eq < 0 {
  780.             continue
  781.         }
  782.         k := kv[:eq]
  783.         if strings.EqualFold(k, "SYSTEMROOT") {
  784.             // We already have it.
  785.             return env
  786.         }
  787.     }
  788.     return append(env, "SYSTEMROOT="+os.Getenv("SYSTEMROOT"))
  789. }
  790.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement