1 | 'use strict';
|
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 |
|
17 |
|
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 + ')',
|
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 |
|
73 |
|
74 |
|
75 |
|
76 |
|
77 |
|
78 |
|
79 |
|
80 |
|
81 |
|
82 |
|
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 |
|
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 {
|
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) {
|
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 | };
|