11cb0ef41Sopenharmony_ci'use strict';
21cb0ef41Sopenharmony_ci
31cb0ef41Sopenharmony_ciconst path = require('path');
41cb0ef41Sopenharmony_ciconst resolveCommand = require('./util/resolveCommand');
51cb0ef41Sopenharmony_ciconst escape = require('./util/escape');
61cb0ef41Sopenharmony_ciconst readShebang = require('./util/readShebang');
71cb0ef41Sopenharmony_ci
81cb0ef41Sopenharmony_ciconst isWin = process.platform === 'win32';
91cb0ef41Sopenharmony_ciconst isExecutableRegExp = /\.(?:com|exe)$/i;
101cb0ef41Sopenharmony_ciconst isCmdShimRegExp = /node_modules[\\/].bin[\\/][^\\/]+\.cmd$/i;
111cb0ef41Sopenharmony_ci
121cb0ef41Sopenharmony_cifunction detectShebang(parsed) {
131cb0ef41Sopenharmony_ci    parsed.file = resolveCommand(parsed);
141cb0ef41Sopenharmony_ci
151cb0ef41Sopenharmony_ci    const shebang = parsed.file && readShebang(parsed.file);
161cb0ef41Sopenharmony_ci
171cb0ef41Sopenharmony_ci    if (shebang) {
181cb0ef41Sopenharmony_ci        parsed.args.unshift(parsed.file);
191cb0ef41Sopenharmony_ci        parsed.command = shebang;
201cb0ef41Sopenharmony_ci
211cb0ef41Sopenharmony_ci        return resolveCommand(parsed);
221cb0ef41Sopenharmony_ci    }
231cb0ef41Sopenharmony_ci
241cb0ef41Sopenharmony_ci    return parsed.file;
251cb0ef41Sopenharmony_ci}
261cb0ef41Sopenharmony_ci
271cb0ef41Sopenharmony_cifunction parseNonShell(parsed) {
281cb0ef41Sopenharmony_ci    if (!isWin) {
291cb0ef41Sopenharmony_ci        return parsed;
301cb0ef41Sopenharmony_ci    }
311cb0ef41Sopenharmony_ci
321cb0ef41Sopenharmony_ci    // Detect & add support for shebangs
331cb0ef41Sopenharmony_ci    const commandFile = detectShebang(parsed);
341cb0ef41Sopenharmony_ci
351cb0ef41Sopenharmony_ci    // We don't need a shell if the command filename is an executable
361cb0ef41Sopenharmony_ci    const needsShell = !isExecutableRegExp.test(commandFile);
371cb0ef41Sopenharmony_ci
381cb0ef41Sopenharmony_ci    // If a shell is required, use cmd.exe and take care of escaping everything correctly
391cb0ef41Sopenharmony_ci    // Note that `forceShell` is an hidden option used only in tests
401cb0ef41Sopenharmony_ci    if (parsed.options.forceShell || needsShell) {
411cb0ef41Sopenharmony_ci        // Need to double escape meta chars if the command is a cmd-shim located in `node_modules/.bin/`
421cb0ef41Sopenharmony_ci        // The cmd-shim simply calls execute the package bin file with NodeJS, proxying any argument
431cb0ef41Sopenharmony_ci        // Because the escape of metachars with ^ gets interpreted when the cmd.exe is first called,
441cb0ef41Sopenharmony_ci        // we need to double escape them
451cb0ef41Sopenharmony_ci        const needsDoubleEscapeMetaChars = isCmdShimRegExp.test(commandFile);
461cb0ef41Sopenharmony_ci
471cb0ef41Sopenharmony_ci        // Normalize posix paths into OS compatible paths (e.g.: foo/bar -> foo\bar)
481cb0ef41Sopenharmony_ci        // This is necessary otherwise it will always fail with ENOENT in those cases
491cb0ef41Sopenharmony_ci        parsed.command = path.normalize(parsed.command);
501cb0ef41Sopenharmony_ci
511cb0ef41Sopenharmony_ci        // Escape command & arguments
521cb0ef41Sopenharmony_ci        parsed.command = escape.command(parsed.command);
531cb0ef41Sopenharmony_ci        parsed.args = parsed.args.map((arg) => escape.argument(arg, needsDoubleEscapeMetaChars));
541cb0ef41Sopenharmony_ci
551cb0ef41Sopenharmony_ci        const shellCommand = [parsed.command].concat(parsed.args).join(' ');
561cb0ef41Sopenharmony_ci
571cb0ef41Sopenharmony_ci        parsed.args = ['/d', '/s', '/c', `"${shellCommand}"`];
581cb0ef41Sopenharmony_ci        parsed.command = process.env.comspec || 'cmd.exe';
591cb0ef41Sopenharmony_ci        parsed.options.windowsVerbatimArguments = true; // Tell node's spawn that the arguments are already escaped
601cb0ef41Sopenharmony_ci    }
611cb0ef41Sopenharmony_ci
621cb0ef41Sopenharmony_ci    return parsed;
631cb0ef41Sopenharmony_ci}
641cb0ef41Sopenharmony_ci
651cb0ef41Sopenharmony_cifunction parse(command, args, options) {
661cb0ef41Sopenharmony_ci    // Normalize arguments, similar to nodejs
671cb0ef41Sopenharmony_ci    if (args && !Array.isArray(args)) {
681cb0ef41Sopenharmony_ci        options = args;
691cb0ef41Sopenharmony_ci        args = null;
701cb0ef41Sopenharmony_ci    }
711cb0ef41Sopenharmony_ci
721cb0ef41Sopenharmony_ci    args = args ? args.slice(0) : []; // Clone array to avoid changing the original
731cb0ef41Sopenharmony_ci    options = Object.assign({}, options); // Clone object to avoid changing the original
741cb0ef41Sopenharmony_ci
751cb0ef41Sopenharmony_ci    // Build our parsed object
761cb0ef41Sopenharmony_ci    const parsed = {
771cb0ef41Sopenharmony_ci        command,
781cb0ef41Sopenharmony_ci        args,
791cb0ef41Sopenharmony_ci        options,
801cb0ef41Sopenharmony_ci        file: undefined,
811cb0ef41Sopenharmony_ci        original: {
821cb0ef41Sopenharmony_ci            command,
831cb0ef41Sopenharmony_ci            args,
841cb0ef41Sopenharmony_ci        },
851cb0ef41Sopenharmony_ci    };
861cb0ef41Sopenharmony_ci
871cb0ef41Sopenharmony_ci    // Delegate further parsing to shell or non-shell
881cb0ef41Sopenharmony_ci    return options.shell ? parsed : parseNonShell(parsed);
891cb0ef41Sopenharmony_ci}
901cb0ef41Sopenharmony_ci
911cb0ef41Sopenharmony_cimodule.exports = parse;
92