Say where a command line's quote is left open and how a quote is written (hq issue 294)
An apostrophe inside a single-quoted value ends that quote, and the line then failed as a bare 'unclosed quote' with no position and no way out named. The splitter's rules stay the shell's; the refusal now names the character, shows the line from there and gives the two ways to write a quote.
This commit is contained in:
@@ -1366,7 +1366,7 @@ func splitCommandLine(line string) ([]string, error) {
|
||||
var words []string
|
||||
var cur strings.Builder
|
||||
inWord := false
|
||||
quote := rune(0)
|
||||
quote, opened := rune(0), 0
|
||||
runes := []rune(line)
|
||||
for i := 0; i < len(runes); i++ {
|
||||
r := runes[i]
|
||||
@@ -1387,7 +1387,7 @@ func splitCommandLine(line string) ([]string, error) {
|
||||
cur.WriteRune(r)
|
||||
}
|
||||
case r == '\'' || r == '"':
|
||||
quote = r
|
||||
quote, opened = r, i
|
||||
inWord = true
|
||||
case r == '\\' && i+1 < len(runes):
|
||||
i++
|
||||
@@ -1405,7 +1405,7 @@ func splitCommandLine(line string) ([]string, error) {
|
||||
}
|
||||
}
|
||||
if quote != 0 {
|
||||
return nil, fmt.Errorf("command has an unclosed %c quote", quote)
|
||||
return nil, unclosedQuote(runes, quote, opened)
|
||||
}
|
||||
if inWord {
|
||||
words = append(words, cur.String())
|
||||
@@ -1413,6 +1413,22 @@ func splitCommandLine(line string) ([]string, error) {
|
||||
return words, nil
|
||||
}
|
||||
|
||||
// unclosedQuote is the refusal of a line whose quote is never closed. Most often an apostrophe inside
|
||||
// a single-quoted value ended that quote early and a later quote was left open, so the refusal says
|
||||
// where the open quote is, shows the line from there, and says how a quote is written inside a quoted
|
||||
// value — the shell's own two ways, which this splitter already reads (novox/hq issue 294).
|
||||
func unclosedQuote(runes []rune, quote rune, opened int) error {
|
||||
const shown = 40
|
||||
from := runes[opened:]
|
||||
excerpt := string(from)
|
||||
if len(from) > shown {
|
||||
excerpt = string(from[:shown]) + "…"
|
||||
}
|
||||
return fmt.Errorf("command has an unclosed %c quote, opened at character %d: %s — an apostrophe "+
|
||||
"inside a single-quoted value ends it; write a ' inside single quotes as '\\'' (it'\\''s), or use "+
|
||||
"double quotes and write \\\" for a \" and \\\\ for a \\ inside them", quote, opened+1, excerpt)
|
||||
}
|
||||
|
||||
// seatAnnouncement is what the controller says it serves on the bus (novox/hq ADR 0197): the
|
||||
// mesh-controller seat, one endpoint per verb it answers, each with the seat's own description and
|
||||
// argument schema — the same facts `tools` answers from the records, as NATS's services format.
|
||||
|
||||
Reference in New Issue
Block a user