Documentation
¶
Overview ¶
Package nodespawn builds the Windows command line that Node's child_process.spawn(file, args) passes to CreateProcessW when neither shell nor windowsVerbatimArguments is set. Node prepends file to args as argv[0], and libuv's make_program_args (src/win/process.c, libuv 1.52.1 as bundled by Node 24.19.0) quotes every element with quote_cmd_arg and joins them with single spaces.
The quoting differs from Go's syscall.EscapeArg: libuv quotes an argument that contains a double quote even when it has no space or tab. A receiver that does not parse its command line with the Microsoft C runtime rules, such as an MSYS2 or Cygwin program like Git Bash, then sees different argv.
Index ¶
- func CommandLine(args []string) string
- func HideWindow(*exec.Cmd, ...Stdio)
- func LookPath(file, cwd string, env []string) (string, error)
- func ProcessEnv() []string
- func QuoteCmdArg(source string) string
- func SetCommandLine(*exec.Cmd)
- func SetDirectory(*exec.Cmd)
- func SetEnv(cmd *exec.Cmd, env []string)
- func SetEnvProperties(cmd *exec.Cmd, env []EnvProperty)
- func SetProgram(cmd *exec.Cmd)
- func Start(cmd *exec.Cmd) error
- type EnvProperty
- type Error
- type Stdio
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func CommandLine ¶
CommandLine is libuv's make_program_args with verbatim_arguments off: args, argv[0] included, each quoted by QuoteCmdArg and joined by single spaces.
func HideWindow ¶
HideWindow does nothing outside Windows, where libuv ignores windowsHide.
func LookPath ¶
LookPath returns the program that Node's child_process.spawn(file, args) starts after the argument checks of Node's spawn for file, cwd, and env, where "" is PiG's working directory and env is the spawn's env option. A nil env is PiG's environment. A file with a slash is the file itself; otherwise libuv searches the PATH of the environment, as lookPathIn describes.
func ProcessEnv ¶
func ProcessEnv() []string
ProcessEnv returns the entries of {...process.env} as Node builds it, in its key order: the names of PiG's environment block in block order, each once, with process.env's value for it. On Windows process.env skips the hidden names that start with "=", such as the per-drive "=C:", and reads a value case-insensitively (src/node_env_var.cc), so a name that appears in two casings has the first one's value.
func QuoteCmdArg ¶
QuoteCmdArg is libuv's quote_cmd_arg. An empty argument becomes "". An argument without space, tab, or double quote is unchanged. One without a double quote or backslash is wrapped in double quotes. Otherwise it is wrapped in double quotes, each double quote is preceded by a backslash, and each backslash run that precedes a double quote or the end is doubled.
libuv works on UTF-16 code units. Every character it tests is ASCII, and UTF-8 continuation bytes are never ASCII, so the same scan over UTF-8 bytes produces the same line once the result is converted to UTF-16.
func SetCommandLine ¶
SetCommandLine does nothing outside Windows, where Node's spawn and os/exec both pass cmd.Args to execve unchanged.
func SetDirectory ¶
SetDirectory does nothing outside Windows, where libuv passes the cwd option to chdir unchanged.
func SetEnv ¶
SetEnv is SetEnvProperties for entries of the form name=value, where the name ends at the first "=": an entry cannot describe a property whose name contains one.
func SetEnvProperties ¶
func SetEnvProperties(cmd *exec.Cmd, env []EnvProperty)
SetEnvProperties gives cmd the environment block of a child that Node's spawn starts with the env option whose properties env holds, in the object's key order: a later property of the same name replaces the value in place, as an object property assignment does. Call it before SetProgram, which validates and searches that environment, and start cmd with Start.
libuv writes each property into the block as "name=value", so two names that share their text up to the first "=" both reach the child. os/exec keeps only the last of such entries, comparing the text regardless of case on Windows (sharesEnvKey). Outside Windows, SetProgram starts such a child as os/exec cannot, as useTrampoline describes. On Windows, Start does.
On Windows, Node's normalizeSpawnArguments (lib/child_process.js) sorts the names by UTF-16 code unit and keeps the first of the names whose toUpperCase values are equal, so "PATH" wins over "Path" and "Path" over "path". toUpperCase applies Unicode's full, locale-independent case mapping, so "SS" also wins over "ß". libuv's make_program_env (src/win/process.c) then orders the entries and adds libuv's required variables, as programEnv describes. cmd.Env holds that block. Elsewhere the child gets the entries in order.
On Windows the child still gets the block in os/exec's order, which Start gives cmd.Env (createEnvBlockOrder). os/exec compares the text before each entry's first "=" byte by byte in UTF-8, with ASCII letters uppercased; libuv compares it by UTF-16 code unit after CompareStringOrdinal uppercases every letter. The two orders agree on names made of ASCII, and Start keeps libuv's order among the entries that os/exec compares equal, such as "A=one" and "A=B=two", so the child's getenv reads the value it reads under Node for every ASCII name. They disagree on some names with other letters: libuv puts PIG_é (U+00E9, which it uppercases to U+00C9) before PIG_Ê (U+00CA), and PIG_😀 (UTF-16 D83D DE00) before PIG_A (U+FF21), while os/exec puts both pairs the other way round. Such variables reach the child in os/exec's order. A child sees that difference only when it lists its environment, or when two entries share a name only through the case of a letter outside ASCII, such as the names "PIG_é=1" and "PIG_É=2", and getenv reads the first of them.
func SetProgram ¶
SetProgram makes Start return the *Error that Node's spawn(cmd.Args[0], cmd.Args[1:], { cwd: cmd.Dir, env: cmd.Env }) throws for its arguments, and makes cmd start what libuv's execvp starts: the program it finds in the PATH of cmd.Env (PiG's PATH when cmd.Env is nil), or the file with a slash, run with /bin/sh when the kernel does not recognize it, as lookPathIn describes. Call it after the last change to cmd.Args, cmd.Dir, and cmd.Env.
func Start ¶
Start starts cmd, which this package's functions prepared, as Node's spawn starts it, and closes the parent's copies of the files it gave the child. Wait for the child with cmd.Wait.
os/exec drops an entry of cmd.Env that shares the text before its first "=" with a later entry (sharesEnvKey), where Node's spawn passes both. Outside Windows SetProgram has already arranged such a start (useTrampoline). On Windows, Start first puts cmd.Env in the order in which os/exec writes the block, keeping libuv's order among the entries os/exec compares equal (SetEnvProperties). It then starts cmd.Path itself with os.StartProcess and that block, or calls cmd.Start when os/exec would drop no entry.
So that both ways start cmd alike, Start takes only a command whose Stdin, Stdout, and Stderr are each nil, which gives the child the NUL device, or an *os.File, and that has no Cancel and no WaitDelay; it returns an error for any other. Whether the start succeeds or not, Start then closes each of those files other than os.Stdin, os.Stdout, and os.Stderr, as Node's spawn closes the child's ends of the pipes it creates and cmd.Start closes those of its Pipe methods.
Types ¶
type EnvProperty ¶
type EnvProperty struct {
Name, Value string
}
EnvProperty is one property of the object that Node's spawn takes as its env option. Unlike an entry of SetEnv, its name may contain "=".
func EnvProperties ¶
func EnvProperties(entries []string) []EnvProperty
EnvProperties is the properties that entries describe, each entry split at its first "=" (after a leading one) as SetEnv splits it.
type Error ¶
type Error struct {
// Code is Node's error.code, such as "ENOENT", "EINVAL", or
// "ERR_INVALID_ARG_VALUE".
Code string
// Syscall is Node's error.syscall: "spawn " + file for an emitted error
// and "spawn" for a thrown one. An argument error has none.
Syscall string
// Thrown reports that spawn throws the error. Otherwise spawn returns the
// child, which emits the error as its 'error' event.
Thrown bool
// contains filtered or unexported fields
}
Error is the error Node's child_process.spawn(file, args) reports for a program it does not start.