1 | 'use strict';
|
2 |
|
3 | var fs = require('fs');
|
4 | var LRU = require('lru-cache');
|
5 | var resolveCommand = require('./resolveCommand');
|
6 | var hasBrokenSpawn = require('./hasBrokenSpawn');
|
7 |
|
8 | var isWin = process.platform === 'win32';
|
9 | var shebangCache = new LRU({ max: 50, maxAge: 30 * 1000 });
|
10 |
|
11 | function readShebang(command) {
|
12 | var buffer;
|
13 | var fd;
|
14 | var match;
|
15 | var shebang;
|
16 |
|
17 |
|
18 | if (shebangCache.has(command)) {
|
19 | return shebangCache.get(command);
|
20 | }
|
21 |
|
22 |
|
23 | buffer = new Buffer(150);
|
24 |
|
25 | try {
|
26 | fd = fs.openSync(command, 'r');
|
27 | fs.readSync(fd, buffer, 0, 150, 0);
|
28 | fs.closeSync(fd);
|
29 | } catch (e) { }
|
30 |
|
31 |
|
32 | match = buffer.toString().trim().match(/#!(.+)/i);
|
33 |
|
34 | if (match) {
|
35 | shebang = match[1].replace(/\/usr\/bin\/env\s+/i, '');
|
36 | }
|
37 |
|
38 |
|
39 | shebangCache.set(command, shebang);
|
40 |
|
41 | return shebang;
|
42 | }
|
43 |
|
44 | function escapeArg(arg, quote) {
|
45 |
|
46 | arg = '' + arg;
|
47 |
|
48 |
|
49 |
|
50 | if (!quote) {
|
51 | arg = arg.replace(/([\(\)%!\^<>&|;,"'\s])/g, '^$1');
|
52 | } else {
|
53 |
|
54 |
|
55 | arg = arg.replace(/(\\*)"/g, '$1$1\\"');
|
56 |
|
57 |
|
58 |
|
59 |
|
60 | arg = arg.replace(/(\\*)$/, '$1$1');
|
61 |
|
62 |
|
63 |
|
64 |
|
65 | arg = '"' + arg + '"';
|
66 | }
|
67 |
|
68 | return arg;
|
69 | }
|
70 |
|
71 | function escapeCommand(command) {
|
72 |
|
73 |
|
74 |
|
75 | return /^[a-z0-9_-]+$/i.test(command) ? command : escapeArg(command, true);
|
76 | }
|
77 |
|
78 | function requiresShell(command) {
|
79 | return !/\.(?:com|exe)$/i.test(command);
|
80 | }
|
81 |
|
82 | function parse(command, args, options) {
|
83 | var shebang;
|
84 | var applyQuotes;
|
85 | var file;
|
86 | var original;
|
87 | var shell;
|
88 |
|
89 |
|
90 | if (args && !Array.isArray(args)) {
|
91 | options = args;
|
92 | args = null;
|
93 | }
|
94 |
|
95 | args = args ? args.slice(0) : [];
|
96 | options = options || {};
|
97 | original = command;
|
98 |
|
99 | if (isWin) {
|
100 |
|
101 | file = resolveCommand(command);
|
102 | file = file || resolveCommand(command, true);
|
103 | shebang = file && readShebang(file);
|
104 | shell = options.shell || hasBrokenSpawn;
|
105 |
|
106 | if (shebang) {
|
107 | args.unshift(file);
|
108 | command = shebang;
|
109 | shell = shell || requiresShell(resolveCommand(shebang) || resolveCommand(shebang, true));
|
110 | } else {
|
111 | shell = shell || requiresShell(file);
|
112 | }
|
113 |
|
114 | if (shell) {
|
115 |
|
116 | applyQuotes = (command !== 'echo');
|
117 | command = escapeCommand(command);
|
118 | args = args.map(function (arg) {
|
119 | return escapeArg(arg, applyQuotes);
|
120 | });
|
121 |
|
122 |
|
123 | args = ['/s', '/c', '"' + command + (args.length ? ' ' + args.join(' ') : '') + '"'];
|
124 | command = process.env.comspec || 'cmd.exe';
|
125 |
|
126 |
|
127 | options.windowsVerbatimArguments = true;
|
128 | }
|
129 | }
|
130 |
|
131 | return {
|
132 | command: command,
|
133 | args: args,
|
134 | options: options,
|
135 | file: file,
|
136 | original: original,
|
137 | };
|
138 | }
|
139 |
|
140 | module.exports = parse;
|