Blame view

node_modules/cross-spawn/lib/parse.js 3.46 KB
aaac7fed   liuqimichale   add
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
  'use strict';
  
  var fs = require('fs');
  var LRU = require('lru-cache');
  var resolveCommand = require('./resolveCommand');
  
  var isWin = process.platform === 'win32';
  var shebangCache = new LRU({ max: 50, maxAge: 30 * 1000 });  // Cache just for 30sec
  
  function readShebang(command) {
      var buffer;
      var fd;
      var match;
      var shebang;
  
      // Check if it is in the cache first
      if (shebangCache.has(command)) {
          return shebangCache.get(command);
      }
  
      // Read the first 150 bytes from the file
      buffer = new Buffer(150);
  
      try {
          fd = fs.openSync(command, 'r');
          fs.readSync(fd, buffer, 0, 150, 0);
          fs.closeSync(fd);
      } catch (e) { /* empty */ }
  
      // Check if it is a shebang
      match = buffer.toString().trim().match(/#!(.+)/i);
  
      if (match) {
          shebang = match[1].replace(/\/usr\/bin\/env\s+/i, '');   // Remove /usr/bin/env
      }
  
      // Store the shebang in the cache
      shebangCache.set(command, shebang);
  
      return shebang;
  }
  
  function escapeArg(arg, quote) {
      // Convert to string
      arg = '' + arg;
  
      // If we are not going to quote the argument,
      // escape shell metacharacters, including double and single quotes:
      if (!quote) {
          arg = arg.replace(/([\(\)%!\^<>&|;,"'\s])/g, '^$1');
      } else {
          // Sequence of backslashes followed by a double quote:
          // double up all the backslashes and escape the double quote
          arg = arg.replace(/(\\*)"/g, '$1$1\\"');
  
          // Sequence of backslashes followed by the end of the string
          // (which will become a double quote later):
          // double up all the backslashes
          arg = arg.replace(/(\\*)$/, '$1$1');
  
          // All other backslashes occur literally
  
          // Quote the whole thing:
          arg = '"' + arg + '"';
      }
  
      return arg;
  }
  
  function escapeCommand(command) {
      // Do not escape if this command is not dangerous..
      // We do this so that commands like "echo" or "ifconfig" work
      // Quoting them, will make them unaccessible
      return /^[a-z0-9_-]+$/i.test(command) ? command : escapeArg(command, true);
  }
  
  function parse(command, args, options) {
      var shebang;
      var applyQuotes;
      var file;
      var original;
  
      // Normalize arguments, similar to nodejs
      if (args && !Array.isArray(args)) {
          options = args;
          args = null;
      }
  
      args = args ? args.slice(0) : [];  // Clone array to avoid changing the original
      options = options || {};
      original = command;
  
      if (isWin) {
          // Detect & add support for shebangs
          file = resolveCommand(command);
          file = file || resolveCommand(command, true);
          shebang = file && readShebang(file);
  
          if (shebang) {
              args.unshift(file);
              command = shebang;
          }
  
          // Escape command & arguments
          applyQuotes = command !== 'echo';  // Do not quote arguments for the special "echo" command
          command = escapeCommand(command);
          args = args.map(function (arg) {
              return escapeArg(arg, applyQuotes);
          });
  
          // Use cmd.exe
          args = ['/s', '/c', '"' + command + (args.length ? ' ' + args.join(' ') : '') + '"'];
          command = process.env.comspec || 'cmd.exe';
  
          // Tell node's spawn that the arguments are already escaped
          options.windowsVerbatimArguments = true;
      }
  
      return {
          command: command,
          args: args,
          options: options,
          file: file,
          original: original,
      };
  }
  
  module.exports = parse;