-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsplit.go
More file actions
86 lines (76 loc) · 2.16 KB
/
Copy pathsplit.go
File metadata and controls
86 lines (76 loc) · 2.16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
package netargv
import "errors"
// ErrUnclosedQuote is returned when a quoted span in a message header is never closed.
var ErrUnclosedQuote = errors.New("unclosed quote in message header")
// splitWords splits s into shell-style tokens.
//
// Rules:
// - Tokens are separated by ASCII whitespace.
// - A double-quoted span ("...") is a single token; the quotes are stripped.
// - A single-quoted span ('...') is a single token; the quotes are stripped.
// - No escape sequences, variable expansion, or glob expansion.
// - Unclosed quotes are an error.
// - A backslash outside quotes is not special; line continuation is resolved
// by the caller before this function is invoked.
func splitWords(s string) ([]string, error) {
var tokens []string
i := 0
for i < len(s) {
// Skip leading whitespace.
for i < len(s) && isSpace(s[i]) {
i++
}
if i >= len(s) {
break
}
// Fast path: token contains no quotes — track start index and slice s directly.
// Only allocate a []byte buffer when a quoted span forces concatenation.
tokenStart := i
hasQuote := false
// Scan forward to detect whether this token contains any quotes before
// the first whitespace boundary.
for j := i; j < len(s) && !isSpace(s[j]); j++ {
if s[j] == '"' || s[j] == '\'' {
hasQuote = true
break
}
}
if !hasQuote {
// No quote in this token: find the end and slice — zero alloc.
for i < len(s) && !isSpace(s[i]) {
i++
}
tokens = append(tokens, s[tokenStart:i])
continue
}
// Quoted token: build into a []byte buffer so we can strip quote chars
// and merge bare + quoted spans into one token.
var cur []byte
for i < len(s) && !isSpace(s[i]) {
ch := s[i]
if ch == '"' || ch == '\'' {
quote := ch
i++ // skip opening quote
for {
if i >= len(s) {
return nil, ErrUnclosedQuote
}
if s[i] == quote {
i++ // skip closing quote
break
}
cur = append(cur, s[i])
i++
}
continue
}
cur = append(cur, ch)
i++
}
tokens = append(tokens, string(cur))
}
return tokens, nil
}
func isSpace(c byte) bool {
return c == ' ' || c == '\t' || c == '\r' || c == '\n'
}