|
1 | 1 | 'use strict';
|
2 | 2 |
|
3 |
| -exports.quote = function (xs) { |
4 |
| - return xs.map(function (s) { |
5 |
| - if (s && typeof s === 'object') { |
6 |
| - return s.op.replace(/(.)/g, '\\$1'); |
7 |
| - } else if ((/["\s]/).test(s) && !(/'/).test(s)) { |
8 |
| - return "'" + s.replace(/(['\\])/g, '\\$1') + "'"; |
9 |
| - } else if ((/["'\s]/).test(s)) { |
10 |
| - return '"' + s.replace(/(["\\$`!])/g, '\\$1') + '"'; |
11 |
| - } |
12 |
| - return String(s).replace(/([A-Za-z]:)?([#!"$&'()*,:;<=>?@[\\\]^`{|}])/g, '$1\\$2'); |
13 |
| - }).join(' '); |
14 |
| -}; |
15 |
| - |
16 |
| -// '<(' is process substitution operator and |
17 |
| -// can be parsed the same as control operator |
18 |
| -var CONTROL = '(?:' + [ |
19 |
| - '\\|\\|', '\\&\\&', ';;', '\\|\\&', '\\<\\(', '>>', '>\\&', '[&;()|<>]' |
20 |
| -].join('|') + ')'; |
21 |
| -var META = '|&;()<> \\t'; |
22 |
| -var BAREWORD = '(\\\\[\'"' + META + ']|[^\\s\'"' + META + '])+'; |
23 |
| -var SINGLE_QUOTE = '"((\\\\"|[^"])*?)"'; |
24 |
| -var DOUBLE_QUOTE = '\'((\\\\\'|[^\'])*?)\''; |
25 |
| - |
26 |
| -var TOKEN = ''; |
27 |
| -for (var i = 0; i < 4; i++) { |
28 |
| - TOKEN += (Math.pow(16, 8) * Math.random()).toString(16); |
29 |
| -} |
30 |
| - |
31 |
| -function parse(s, env, opts) { |
32 |
| - var chunker = new RegExp([ |
33 |
| - '(' + CONTROL + ')', // control chars |
34 |
| - '(' + BAREWORD + '|' + SINGLE_QUOTE + '|' + DOUBLE_QUOTE + ')*' |
35 |
| - ].join('|'), 'g'); |
36 |
| - var match = s.match(chunker).filter(Boolean); |
37 |
| - |
38 |
| - if (!match) { |
39 |
| - return []; |
40 |
| - } |
41 |
| - if (!env) { |
42 |
| - env = {}; |
43 |
| - } |
44 |
| - if (!opts) { |
45 |
| - opts = {}; |
46 |
| - } |
47 |
| - |
48 |
| - var commented = false; |
49 |
| - |
50 |
| - function getVar(_, pre, key) { |
51 |
| - var r = typeof env === 'function' ? env(key) : env[key]; |
52 |
| - if (r === undefined && key != '') { |
53 |
| - r = ''; |
54 |
| - } else if (r === undefined) { |
55 |
| - r = '$'; |
56 |
| - } |
57 |
| - |
58 |
| - if (typeof r === 'object') { |
59 |
| - return pre + TOKEN + JSON.stringify(r) + TOKEN; |
60 |
| - } |
61 |
| - return pre + r; |
62 |
| - } |
63 |
| - |
64 |
| - return match.map(function (s, j) { |
65 |
| - if (commented) { |
66 |
| - return void undefined; |
67 |
| - } |
68 |
| - if (RegExp('^' + CONTROL + '$').test(s)) { |
69 |
| - return { op: s }; |
70 |
| - } |
71 |
| - |
72 |
| - // Hand-written scanner/parser for Bash quoting rules: |
73 |
| - // |
74 |
| - // 1. inside single quotes, all characters are printed literally. |
75 |
| - // 2. inside double quotes, all characters are printed literally |
76 |
| - // except variables prefixed by '$' and backslashes followed by |
77 |
| - // either a double quote or another backslash. |
78 |
| - // 3. outside of any quotes, backslashes are treated as escape |
79 |
| - // characters and not printed (unless they are themselves escaped) |
80 |
| - // 4. quote context can switch mid-token if there is no whitespace |
81 |
| - // between the two quote contexts (e.g. all'one'"token" parses as |
82 |
| - // "allonetoken") |
83 |
| - var SQ = "'"; |
84 |
| - var DQ = '"'; |
85 |
| - var DS = '$'; |
86 |
| - var BS = opts.escape || '\\'; |
87 |
| - var quote = false; |
88 |
| - var esc = false; |
89 |
| - var out = ''; |
90 |
| - var isGlob = false; |
91 |
| - var i; |
92 |
| - |
93 |
| - function parseEnvVar() { |
94 |
| - i += 1; |
95 |
| - var varend; |
96 |
| - var varname; |
97 |
| - // debugger |
98 |
| - if (s.charAt(i) === '{') { |
99 |
| - i += 1; |
100 |
| - if (s.charAt(i) === '}') { |
101 |
| - throw new Error('Bad substitution: ' + s.substr(i - 2, 3)); |
102 |
| - } |
103 |
| - varend = s.indexOf('}', i); |
104 |
| - if (varend < 0) { |
105 |
| - throw new Error('Bad substitution: ' + s.substr(i)); |
106 |
| - } |
107 |
| - varname = s.substr(i, varend - i); |
108 |
| - i = varend; |
109 |
| - } else if ((/[*@#?$!_-]/).test(s.charAt(i))) { |
110 |
| - varname = s.charAt(i); |
111 |
| - i += 1; |
112 |
| - } else { |
113 |
| - varend = s.substr(i).match(/[^\w\d_]/); |
114 |
| - if (!varend) { |
115 |
| - varname = s.substr(i); |
116 |
| - i = s.length; |
117 |
| - } else { |
118 |
| - varname = s.substr(i, varend.index); |
119 |
| - i += varend.index - 1; |
120 |
| - } |
121 |
| - } |
122 |
| - return getVar(null, '', varname); |
123 |
| - } |
124 |
| - |
125 |
| - for (i = 0; i < s.length; i++) { |
126 |
| - var c = s.charAt(i); |
127 |
| - isGlob = isGlob || (!quote && (c === '*' || c === '?')); |
128 |
| - if (esc) { |
129 |
| - out += c; |
130 |
| - esc = false; |
131 |
| - } else if (quote) { |
132 |
| - if (c === quote) { |
133 |
| - quote = false; |
134 |
| - } else if (quote == SQ) { |
135 |
| - out += c; |
136 |
| - } else { // Double quote |
137 |
| - if (c === BS) { |
138 |
| - i += 1; |
139 |
| - c = s.charAt(i); |
140 |
| - if (c === DQ || c === BS || c === DS) { |
141 |
| - out += c; |
142 |
| - } else { |
143 |
| - out += BS + c; |
144 |
| - } |
145 |
| - } else if (c === DS) { |
146 |
| - out += parseEnvVar(); |
147 |
| - } else { |
148 |
| - out += c; |
149 |
| - } |
150 |
| - } |
151 |
| - } else if (c === DQ || c === SQ) { |
152 |
| - quote = c; |
153 |
| - } else if (RegExp('^' + CONTROL + '$').test(c)) { |
154 |
| - return { op: s }; |
155 |
| - } else if ((/^#$/).test(c)) { |
156 |
| - commented = true; |
157 |
| - if (out.length) { |
158 |
| - return [out, { comment: s.slice(i + 1) + match.slice(j + 1).join(' ') }]; |
159 |
| - } |
160 |
| - return [{ comment: s.slice(i + 1) + match.slice(j + 1).join(' ') }]; |
161 |
| - } else if (c === BS) { |
162 |
| - esc = true; |
163 |
| - } else if (c === DS) { |
164 |
| - out += parseEnvVar(); |
165 |
| - } else { |
166 |
| - out += c; |
167 |
| - } |
168 |
| - } |
169 |
| - |
170 |
| - if (isGlob) { |
171 |
| - return { op: 'glob', pattern: out }; |
172 |
| - } |
173 |
| - |
174 |
| - return out; |
175 |
| - }).reduce(function (prev, arg) { // finalize parsed aruments |
176 |
| - if (arg === undefined) { |
177 |
| - return prev; |
178 |
| - } |
179 |
| - return prev.concat(arg); |
180 |
| - }, []); |
181 |
| -} |
182 |
| - |
183 |
| -exports.parse = function (s, env, opts) { |
184 |
| - var mapped = parse(s, env, opts); |
185 |
| - if (typeof env !== 'function') { |
186 |
| - return mapped; |
187 |
| - } |
188 |
| - return mapped.reduce(function (acc, s) { |
189 |
| - if (typeof s === 'object') { |
190 |
| - return acc.concat(s); |
191 |
| - } |
192 |
| - var xs = s.split(RegExp('(' + TOKEN + '.*?' + TOKEN + ')', 'g')); |
193 |
| - if (xs.length === 1) { |
194 |
| - return acc.concat(xs[0]); |
195 |
| - } |
196 |
| - return acc.concat(xs.filter(Boolean).map(function (x) { |
197 |
| - if (RegExp('^' + TOKEN).test(x)) { |
198 |
| - return JSON.parse(x.split(TOKEN)[1]); |
199 |
| - } |
200 |
| - return x; |
201 |
| - })); |
202 |
| - }, []); |
203 |
| -}; |
| 3 | +exports.quote = require('./quote'); |
| 4 | +exports.parse = require('./parse'); |
0 commit comments