| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133 |
- // Copyright 2015 Light Code Labs, LLC
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package caddy
- import (
- "errors"
- "runtime"
- "unicode"
- "github.com/flynn/go-shlex"
- )
- var runtimeGoos = runtime.GOOS
- // SplitCommandAndArgs takes a command string and parses it shell-style into the
- // command and its separate arguments.
- func SplitCommandAndArgs(command string) (cmd string, args []string, err error) {
- var parts []string
- if runtimeGoos == "windows" {
- parts = parseWindowsCommand(command) // parse it Windows-style
- } else {
- parts, err = parseUnixCommand(command) // parse it Unix-style
- if err != nil {
- err = errors.New("error parsing command: " + err.Error())
- return
- }
- }
- if len(parts) == 0 {
- err = errors.New("no command contained in '" + command + "'")
- return
- }
- cmd = parts[0]
- if len(parts) > 1 {
- args = parts[1:]
- }
- return
- }
- // parseUnixCommand parses a unix style command line and returns the
- // command and its arguments or an error
- func parseUnixCommand(cmd string) ([]string, error) {
- return shlex.Split(cmd)
- }
- // parseWindowsCommand parses windows command lines and
- // returns the command and the arguments as an array. It
- // should be able to parse commonly used command lines.
- // Only basic syntax is supported:
- // - spaces in double quotes are not token delimiters
- // - double quotes are escaped by either backspace or another double quote
- // - except for the above case backspaces are path separators (not special)
- //
- // Many sources point out that escaping quotes using backslash can be unsafe.
- // Use two double quotes when possible. (Source: http://stackoverflow.com/a/31413730/2616179 )
- //
- // This function has to be used on Windows instead
- // of the shlex package because this function treats backslash
- // characters properly.
- func parseWindowsCommand(cmd string) []string {
- const backslash = '\\'
- const quote = '"'
- var parts []string
- var part string
- var inQuotes bool
- var lastRune rune
- for i, ch := range cmd {
- if i != 0 {
- lastRune = rune(cmd[i-1])
- }
- if ch == backslash {
- // put it in the part - for now we don't know if it's an
- // escaping char or path separator
- part += string(ch)
- continue
- }
- if ch == quote {
- if lastRune == backslash {
- // remove the backslash from the part and add the escaped quote instead
- part = part[:len(part)-1]
- part += string(ch)
- continue
- }
- if lastRune == quote {
- // revert the last change of the inQuotes state
- // it was an escaping quote
- inQuotes = !inQuotes
- part += string(ch)
- continue
- }
- // normal escaping quotes
- inQuotes = !inQuotes
- continue
- }
- if unicode.IsSpace(ch) && !inQuotes && len(part) > 0 {
- parts = append(parts, part)
- part = ""
- continue
- }
- part += string(ch)
- }
- if len(part) > 0 {
- parts = append(parts, part)
- }
- return parts
- }
|