Initial commit
[yaffs-website] / node_modules / cross-spawn / lib / parse.js
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 });  // Cache just for 30sec
10
11 function readShebang(command) {
12     var buffer;
13     var fd;
14     var match;
15     var shebang;
16
17     // Check if it is in the cache first
18     if (shebangCache.has(command)) {
19         return shebangCache.get(command);
20     }
21
22     // Read the first 150 bytes from the file
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) { /* empty */ }
30
31     // Check if it is a shebang
32     match = buffer.toString().trim().match(/#!(.+)/i);
33
34     if (match) {
35         shebang = match[1].replace(/\/usr\/bin\/env\s+/i, '');   // Remove /usr/bin/env
36     }
37
38     // Store the shebang in the cache
39     shebangCache.set(command, shebang);
40
41     return shebang;
42 }
43
44 function escapeArg(arg, quote) {
45     // Convert to string
46     arg = '' + arg;
47
48     // If we are not going to quote the argument,
49     // escape shell metacharacters, including double and single quotes:
50     if (!quote) {
51         arg = arg.replace(/([\(\)%!\^<>&|;,"'\s])/g, '^$1');
52     } else {
53         // Sequence of backslashes followed by a double quote:
54         // double up all the backslashes and escape the double quote
55         arg = arg.replace(/(\\*)"/g, '$1$1\\"');
56
57         // Sequence of backslashes followed by the end of the string
58         // (which will become a double quote later):
59         // double up all the backslashes
60         arg = arg.replace(/(\\*)$/, '$1$1');
61
62         // All other backslashes occur literally
63
64         // Quote the whole thing:
65         arg = '"' + arg + '"';
66     }
67
68     return arg;
69 }
70
71 function escapeCommand(command) {
72     // Do not escape if this command is not dangerous..
73     // We do this so that commands like "echo" or "ifconfig" work
74     // Quoting them, will make them unaccessible
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     // Normalize arguments, similar to nodejs
90     if (args && !Array.isArray(args)) {
91         options = args;
92         args = null;
93     }
94
95     args = args ? args.slice(0) : [];  // Clone array to avoid changing the original
96     options = options || {};
97     original = command;
98
99     if (isWin) {
100         // Detect & add support for shebangs
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             // Escape command & arguments
116             applyQuotes = (command !== 'echo');  // Do not quote arguments for the special "echo" command
117             command = escapeCommand(command);
118             args = args.map(function (arg) {
119                 return escapeArg(arg, applyQuotes);
120             });
121
122             // Use cmd.exe
123             args = ['/s', '/c', '"' + command + (args.length ? ' ' + args.join(' ') : '') + '"'];
124             command = process.env.comspec || 'cmd.exe';
125
126             // Tell node's spawn that the arguments are already escaped
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;