UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

91 lines • 3.93 kB
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. ========= // 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. // ========= Copyright 2026 @ Strukto.AI All Rights Reserved. ========= import { byteView } from "../shell/bytes.js"; // The seven C escapes gnulib spells by name, plus the two printable // characters it still escapes because they would otherwise close or // confuse the quotes it wrapped the word in. Every other byte outside // 0x20-0x7e becomes three octal digits. const QUOTE_ESCAPES = new Map([ ['\x07', '\\a'], ['\x08', '\\b'], ['\t', '\\t'], ['\n', '\\n'], ['\x0b', '\\v'], ['\f', '\\f'], ['\r', '\\r'], ["'", "\\'"], ['\\', '\\\\'], ]); // One word as gnulib's `quote()` renders it inside a diagnostic. // // Every GNU coreutils diagnostic that names a word passes it through // gnulib's quotearg in `locale_quoting_style`, which in the C locale wraps // it in single quotes and escapes what would be unreadable inside them. // This is the body only; the quotes belong to the message templates, // because a few clauses spell them differently (`cut` writes // `invalid field value '<w>'` with no colon before the quote). // // ONE rule, not one per command. It was derived from all 255 reachable // byte values in each of `nl`, `expand`, `shuf`, `cut` and `expr`, and all // five agree byte for byte, which is what makes this a shared leaf rather // than a copy in each command. // // It sits directly under `commands/` because both halves of the tree need // it: every builtin that refuses a flag value, and the shared word-choice // renderer in `commands/spec/usage.ts`. A leaf beside `commands/errors.ts` // is reachable from `spec` and from `builtin` alike, where the old home // under `commands/builtin/utils/` would have made `spec` import `builtin` // -- the one direction nothing in this tree takes. // // The rule is per byte, which is why this takes a byte view: a two-byte // character is two octal escapes (`expr '(' <e-acute>` names `\303\251`, // not the character), and a byte above 0x7f is not printable in the C // locale. Callers holding an ordinary string want `quoteText`. // // Not every quoted word goes through this. getopt's own // `unrecognized option '<w>'` prints `argv[optind]` with a plain `%s`, so // it carries the raw bytes and must NOT be routed here. // // `utf8` means the word is text, as a command under a UTF-8 locale holds it; // it is quoted per byte all the same, as the C locale quotes it. // // `quote_word` in quote.py is the twin. export function quoteWord(view, utf8 = false) { let out = ''; for (const ch of utf8 ? byteView(view) : view) { const named = QUOTE_ESCAPES.get(ch); if (named !== undefined) { out += named; } else if (ch >= ' ' && ch <= '~') { out += ch; } else { out += '\\' + ch.charCodeAt(0).toString(8).padStart(3, '0'); } } return out; } // `quoteWord` for a caller holding an ordinary string rather than the byte // view expr's parser runs on. // // The commands that refuse a flag value hold it as a plain string, so they // need the byte view first: the rule counts bytes, and `é` must render as // two octal escapes rather than one. // // `quote_text` in quote.py is the twin. export function quoteText(text) { return quoteWord(byteView(text)); } //# sourceMappingURL=quote.js.map