summaryrefslogtreecommitdiff
path: root/deps/npm/node_modules/libnpx/node_modules/yargs/node_modules/os-locale/node_modules/execa/node_modules/cross-spawn/lib/parse.js
blob: 77cbb83d2db792c66f9e0957a5eac345befc486a (plain)
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
129
130
131
132
133
134
135
136
137
138
139
140
'use strict';

var fs = require('fs');
var LRU = require('lru-cache');
var resolveCommand = require('./resolveCommand');
var hasBrokenSpawn = require('./hasBrokenSpawn');

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 requiresShell(command) {
    return !/\.(?:com|exe)$/i.test(command);
}

function parse(command, args, options) {
    var shebang;
    var applyQuotes;
    var file;
    var original;
    var shell;

    // 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);
        shell = options.shell || hasBrokenSpawn;

        if (shebang) {
            args.unshift(file);
            command = shebang;
            shell = shell || requiresShell(resolveCommand(shebang) || resolveCommand(shebang, true));
        } else {
            shell = shell || requiresShell(file);
        }

        if (shell) {
            // 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;