OILS / stdlib / ysh / quote.ysh View on Github | oils.pub

203 lines, 90 significant
1const __provide__ = :| sh shell ninja make |
2
3# Issues for quoting:
4#
5# 1. What is the alphabet we're quoting to?
6# - Output can be "all bytes", "all unicode strings", or "ASCII"
7# 2. Start with a simple algorithm to quote everything
8# - POSIX shell may take ' to ''\'''
9# 3. Heuristic that may avoid quotes, to make it more readable
10# - But really it should be \'
11# - If the ' appears at the beginning or the end, we could have a different
12# algorithm. Or we could strip leading and trailing ''
13# 4. Are there any byte strings / unicode strings that can't be quoted?
14# - e.g. NUL bytes?
15# - for JSON/JS, binary strings? Only Unicode strings can be encoded.
16#
17# Builtins:
18# toJson() toJson8()
19# toJ8Line() - does the "maybe unquoted" logic
20#
21# Related functions:
22# encode.base{16,32,64} - Crockford has a base32
23# decode.base{16,32,64}
24# Also base85, and maybe base58 base36
25#
26# In Python: bin() oct() hex() int(i, 9) and %o %x (there is no %b)
27#
28# Other:
29# Punycode for Unicode domain names uses xn-- ?
30# CSS has escapes with \
31# HTTP Cookies have "" and \?
32#
33# Related:
34# demo/url-search-params.ysh is the PARSER / unquoter for quote.urlParam()
35
36func sh(s) {
37 ### Quote POSIX sh string
38
39 # replace ' with sequence ' \' '
40 # Note: the \\ is hard to read - '' doesn't help
41 return ("'" ++ s.replace("'", "'\\''") ++ "'")
42}
43
44func shell(s) {
45 ### Quote shell string, where bash and zsh style $'\n' is allowed
46
47 # TODO: Binding for j8_lite.MaybeShellEncode / ShellEncode
48 return (toJson(s))
49}
50
51func ysh(s) {
52 ### Quote YSH string as b'\n'
53
54 # TODO: Binding for j8_lite.YshEncode(unquoted_ok)
55 return (toJson(s))
56}
57
58#
59# Build Tools
60#
61
62func make(s) {
63 var out = []
64 var n = len(s)
65 for i in (0 ..< n) {
66 var ch = s[i]
67
68 case (ch) {
69 (\r) |
70 (\n) {
71 error "Can't quote newlines for Make?"
72 }
73 ('$') {
74 call out->append('$')
75 call out->append('$')
76 }
77 ('%') |
78 (r'\') |
79 # glob characters
80 ('[') | (']') | ('*') | ('?') {
81 call out->append(r'\')
82 call out->append(ch)
83 }
84 (else) {
85 call out->append(ch)
86 }
87 }
88 }
89 return (join(out, ''))
90}
91
92# https://ninja-build.org/manual.html#ref_lexer
93# $ escapes newline, space, : and $
94# and then the rest is interpreted by /bin/sh
95func ninja(s) {
96 var out = []
97 var n = len(s)
98 for i in (0 ..< n) {
99 var ch = s[i]
100
101 case (ch) {
102 # Subtlety: Ninja allows $ to escape a newline, but it's only for
103 # line continuations - for breaking long lists of files.
104 # - A file itself should not have a newline.
105 # - It strips literal newlines out of shell commands.
106 # So disallow it
107 (\r) |
108 (\n) {
109 error "Can't quote newlines for Ninja"
110 }
111 ('$') |
112 (' ') |
113 #('#') | # Ninja has no way to escape comments!
114 (':') {
115 call out->append('$')
116 call out->append(ch)
117 }
118 (else) {
119 call out->append(ch)
120 }
121 }
122 }
123 return (join(out, ''))
124}
125
126# I don't know the rule here - uses \?
127func cmake(s) {
128 return (s)
129}
130
131#
132# Tables, Objects, Documents
133#
134
135func csv(s) {
136 # double up " etc.
137 return (s)
138}
139
140func sql(s) {
141 # double up ' etc.
142 return (s)
143}
144
145# quote.json is just toJson()
146
147func html(s) {
148 ### Quote shell string, where bash and zsh style $'\n' is allowed
149
150 # Binding for j8_lite.MaybeShellEncode / ShellEncode
151 return (toJson(s))
152}
153
154#
155# Web
156#
157
158func urlParam(s) {
159 # urllib.quote
160 # 'foo bar %' -> 'foo+bar %AB'
161 return (toJson(s))
162}
163
164#
165# Programming Languages
166#
167
168# Python 2 or 3
169func py(s) {
170 return (s)
171}
172
173# C or C++
174# Can it be Unicode?
175func c(s) {
176 return (s)
177}
178
179# quote.js is just toJson() ?
180# But it can't handle binary strings?
181# We can make a table?
182
183#
184# Windows
185#
186
187# We want something that works everywhere -- it should never DOUBLE-ESCAPE, but
188# it can "unnecessarily" ESCAPE certain characters.
189
190# C runtime
191func win32_crt(s) {
192 return (s)
193}
194
195# win32 cmd.exe batch files?
196func win32_cmd(s) {
197 return (s)
198}
199
200# batch different than cmd.exe?
201func win32_batch(s) {
202 return (s)
203}