///|
/// Quote exactly one argument for a POSIX shell. Not valid for Windows shells.
/// Reject NUL because real process argv cannot contain it.
pub fn quote_argument(argument : String) -> String raise DocoptError {
guard argument.length() <= 1048576 else {
raise Limit("argument too large to quote")
}
if argument == "" {
return "''"
}
let mut safe = true
for c in argument {
guard c != '\u0000' else {
raise Conversion("cannot quote an argv containing NUL")
}
if !((
(c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z') ||
(c >= '0' && c <= '9')
) ||
c == '_' ||
c == '@' ||
c == '%' ||
c == '+' ||
c == '=' ||
c == ':' ||
c == ',' ||
c == '.' ||
c == '/' ||
c == '-') {
safe = false
}
}
if safe {
argument
} else {
"'" + argument.replace_all(old="'", new="'\"'\"'") + "'"
}
}
///|
/// Display or export an argv vector without losing empty strings or boundaries.
/// This never runs a shell; callers remain responsible for process invocation.
pub fn join_command(argv : Array[String]) -> String raise DocoptError {
guard argv.length() <= 4096 else {
raise Limit("too many arguments to quote")
}
let out = StringBuilder()
let mut units = 0
for i, arg in argv {
guard arg.length() <= 1048576 - units else {
raise Limit("argv too large to quote")
}
units += arg.length()
if i > 0 {
out.write_char(' ')
}
out.write_string(quote_argument(arg))
}
out.to_string()
}