escape.ha (1240B)
1 // SPDX-License-Identifier: MPL-2.0 2 // (c) Hare authors <https://harelang.org> 3 4 use ascii; 5 use encoding::utf8; 6 use io; 7 use memio; 8 use strings; 9 10 fn is_safe(s: str) bool = { 11 const iter = strings::iter(s); 12 for (let rn => strings::next(&iter)) { 13 switch (rn) { 14 case '@', '%', '+', '=', ':', ',', '.', '/', '-' => 15 void; 16 case => 17 if (!ascii::isalnum(rn) || ascii::isspace(rn)) { 18 return false; 19 }; 20 }; 21 }; 22 return true; 23 }; 24 25 // Quotes a shell string and writes it to the provided I/O handle. 26 export fn quote(sink: io::handle, s: str) (size | io::error) = { 27 if (len(s) == 0) { 28 return io::writeall(sink, strings::toutf8(`''`))?; 29 }; 30 if (is_safe(s)) { 31 return io::writeall(sink, strings::toutf8(s))?; 32 }; 33 34 let z = io::writeall(sink, ['\''])?; 35 36 const iter = strings::iter(s); 37 for (let rn => strings::next(&iter)) { 38 if (rn == '\'') { 39 z += io::writeall(sink, strings::toutf8(`'"'"'`))?; 40 } else { 41 z += io::writeall(sink, utf8::encoderune(rn))?; 42 }; 43 }; 44 45 z += io::writeall(sink, ['\''])?; 46 return z; 47 }; 48 49 // Quotes a shell string and returns a new string. The caller must free the 50 // return value. 51 export fn quotestr(s: str) str = { 52 const sink = memio::dynamic(); 53 quote(&sink, s)!; 54 return memio::string(&sink)!; 55 };