package windows import ( "errors" "strings" "unicode/utf8" ) var ErrInvalidCommandLineArgument = errors.New("Windows command-line argument contains NUL or invalid UTF-8") // BuildCommandLine constructs the lpCommandLine string for an explicit // lpApplicationName. It implements the CommandLineToArgvW-compatible escaping // convention: quotes are escaped by a preceding run of backslashes and trailing // backslashes inside a quoted argument are doubled. func BuildCommandLine(arguments []string) (string, error) { if len(arguments) == 0 { return "", nil } quoted := make([]string, len(arguments)) for index, argument := range arguments { if strings.IndexByte(argument, 0) >= 0 || !utf8.ValidString(argument) { return "", ErrInvalidCommandLineArgument } quoted[index] = quoteArgument(argument) } return strings.Join(quoted, " "), nil } func quoteArgument(argument string) string { if argument != "" && !strings.ContainsAny(argument, " \t\n\v\"") { return argument } var output strings.Builder output.Grow(len(argument) + 2) output.WriteByte('"') backslashes := 0 for _, character := range argument { switch character { case '\\': backslashes++ case '"': output.WriteString(strings.Repeat("\\", backslashes*2+1)) output.WriteRune(character) backslashes = 0 default: output.WriteString(strings.Repeat("\\", backslashes)) output.WriteRune(character) backslashes = 0 } } output.WriteString(strings.Repeat("\\", backslashes*2)) output.WriteByte('"') return output.String() }