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