From fcc0c687cb3db7a17d5b831cc3110f6c9ccf5c9a Mon Sep 17 00:00:00 2001 From: Evan Lucas Date: Tue, 19 May 2015 13:12:26 -0500 Subject: [PATCH] src: add getopt option parser Options have been moved into the NodeOptions class. A new global, node_options now exists and is used to access the options after the command line arguments have been parsed. --- deps/getopt/getopt.gyp | 15 + deps/getopt/getopt.h | 100 ++++ deps/getopt/getopt_long.c | 635 ++++++++++++++++++++++ node.gyp | 6 +- src/node.cc | 314 ++--------- src/node_options.cc | 305 +++++++++++ src/node_options.h | 57 ++ test/parallel/test-cli-debug.js | 19 + test/parallel/test-cli-help.js | 11 + test/parallel/test-cli-v8-options.js | 17 + test/parallel/test-cli-version.js | 12 + test/sequential/test-deprecation-flags.js | 11 + 12 files changed, 1226 insertions(+), 276 deletions(-) create mode 100644 deps/getopt/getopt.gyp create mode 100644 deps/getopt/getopt.h create mode 100644 deps/getopt/getopt_long.c create mode 100644 src/node_options.cc create mode 100644 src/node_options.h create mode 100644 test/parallel/test-cli-debug.js create mode 100644 test/parallel/test-cli-help.js create mode 100644 test/parallel/test-cli-v8-options.js create mode 100644 test/parallel/test-cli-version.js diff --git a/deps/getopt/getopt.gyp b/deps/getopt/getopt.gyp new file mode 100644 index 00000000000000..1260a9306f7a91 --- /dev/null +++ b/deps/getopt/getopt.gyp @@ -0,0 +1,15 @@ +{ + 'targets': [ + { + 'target_name': 'getopt', + 'type': 'static_library', + 'sources': [ + 'getopt.h', + 'getopt_long.c' + ], + 'defines': [ + 'REPLACE_GETOPT' + ] + } + ] +} diff --git a/deps/getopt/getopt.h b/deps/getopt/getopt.h new file mode 100644 index 00000000000000..f2c29ee7fed09d --- /dev/null +++ b/deps/getopt/getopt.h @@ -0,0 +1,100 @@ +/* $NetBSD: getopt.h,v 1.4 2000/07/07 10:43:54 ad Exp $ */ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron and Thomas Klausner. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _GETOPT_H_ +#define _GETOPT_H_ + +#ifdef WIN32 +#ifdef __cplusplus +#define __BEGIN_DECLS extern "C" { +#define __END_DECLS } +#else +#define __BEGIN_DECLS +#define __END_DECLS +#endif + +#ifdef _MSC_VER +/* ignore MSVC++ warnings that are annoying and hard to remove: + 4702 unreachable code + (there is an unreachable assert(0) in case somehow it is reached) + */ +#pragma warning( disable : 4702 ) +#endif + +#else /* WIN32 */ +#include +#endif + +/* + * GNU-like getopt_long()/getopt_long_only() with 4.4BSD optreset extension. + * getopt() is declared here too for GNU programs. + */ +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +struct option { + /* name of long option */ + const char *name; + /* + * one of no_argument, required_argument, and optional_argument: + * whether option takes an argument + */ + int has_arg; + /* if not NULL, set *flag to val when option found */ + int *flag; + /* if flag not NULL, value to set *flag to; else return value */ + int val; + + /* add description for usage */ + const char *desc; +}; + +__BEGIN_DECLS +int getopt_long(int, char * const *, const char *, + const struct option *, int *); +int getopt_long_only(int, char * const *, const char *, + const struct option *, int *); +#ifndef _GETOPT_DECLARED +#define _GETOPT_DECLARED +int getopt(int, char * const [], const char *); + +extern char *optarg; /* getopt(3) external variables */ +extern int optind, opterr, optopt; +#endif +#ifndef _OPTRESET_DECLARED +#define _OPTRESET_DECLARED +extern int optreset; /* getopt(3) external variable */ +#endif +__END_DECLS + +#endif /* !_GETOPT_H_ */ diff --git a/deps/getopt/getopt_long.c b/deps/getopt/getopt_long.c new file mode 100644 index 00000000000000..8280bcbf1c8ffd --- /dev/null +++ b/deps/getopt/getopt_long.c @@ -0,0 +1,635 @@ +/* $OpenBSD: getopt_long.c,v 1.26 2013/06/08 22:47:56 millert Exp $ */ +/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */ + +/* + * Copyright (c) 2002 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + */ +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron and Thomas Klausner. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#if 0 +#if defined(LIBC_SCCS) && !defined(lint) +static char *rcsid = "$OpenBSD: getopt_long.c,v 1.16 2004/02/04 18:17:25 millert Exp $"; +#endif /* LIBC_SCCS and not lint */ +#endif + +#include +#include +#include "getopt.h" +#include +#include + +#ifdef WIN32 +#include +#if !defined(GETOPT_API) +#define GETOPT_API __declspec(dllimport) +#endif + +#ifdef _MSC_VER +/* ignore MSVC++ warnings that are annoying and hard to remove: + 4702 unreachable code + (there is an unreachable assert(0) in case somehow it is reached) + */ +#pragma warning( disable : 4702 ) +#endif + +GETOPT_API extern char opterrmsg[128]; +char opterrmsg[128]; /* last error message is stored here */ + +static void warnx(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + if (fmt != NULL) + _vsnprintf(opterrmsg, 128, fmt, ap); + else + opterrmsg[0]='\0'; + va_end(ap); + fprintf(stderr, opterrmsg); + fprintf(stderr, "\n"); +} + +#else /* WIN32 */ +#include +#include +#endif + +#define GNU_COMPATIBLE /* Be more compatible, configure's use us! */ + +#if 0 /* we prefer to keep our getopt(3) */ +#define REPLACE_GETOPT /* use this getopt as the system getopt(3) */ +#endif + +#ifdef REPLACE_GETOPT +int opterr = 1; /* if error message should be printed */ +int optind = 1; /* index into parent argv vector */ +int optopt = '?'; /* character checked for validity */ +int optreset; /* reset getopt */ +char *optarg; /* argument associated with option */ +#endif + +#define PRINT_ERROR ((opterr) && (*options != ':')) + +#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ +#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ +#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */ + +/* return values */ +#define BADCH (int)'?' +#define BADARG ((*options == ':') ? (int)':' : (int)'?') +#define INORDER (int)1 + +#define EMSG "" + +#ifdef GNU_COMPATIBLE +#define NO_PREFIX (-1) +#define D_PREFIX 0 +#define DD_PREFIX 1 +#define W_PREFIX 2 +#endif + +static int getopt_internal(int, char * const *, const char *, + const struct option *, int *, int); +static int parse_long_options(char * const *, const char *, + const struct option *, int *, int, int); +static int gcd(int, int); +static void permute_args(int, int, int, char * const *); + +static char *place = EMSG; /* option letter processing */ + +/* XXX: set optreset to 1 rather than these two */ +static int nonopt_start = -1; /* first non option argument (for permute) */ +static int nonopt_end = -1; /* first option after non options (for permute) */ + +/* Error messages */ +static const char recargchar[] = "option requires an argument -- %c"; +static const char illoptchar[] = "illegal option -- %c"; /* From P1003.2 */ +#ifdef GNU_COMPATIBLE +static int dash_prefix = NO_PREFIX; +static const char gnuoptchar[] = "invalid option -- %c"; + +static const char recargstring[] = "option `%s%s' requires an argument"; +static const char ambig[] = "option `%s%.*s' is ambiguous"; +static const char noarg[] = "option `%s%.*s' doesn't allow an argument"; +static const char illoptstring[] = "unrecognized option `%s%s'"; +#else +static const char recargstring[] = "option requires an argument -- %s"; +static const char ambig[] = "ambiguous option -- %.*s"; +static const char noarg[] = "option doesn't take an argument -- %.*s"; +static const char illoptstring[] = "unknown option -- %s"; +#endif + +/* + * Compute the greatest common divisor of a and b. + */ +static int +gcd(int a, int b) +{ + int c; + + c = a % b; + while (c != 0) { + a = b; + b = c; + c = a % b; + } + + return (b); +} + +/* + * Exchange the block from nonopt_start to nonopt_end with the block + * from nonopt_end to opt_end (keeping the same order of arguments + * in each block). + */ +static void +permute_args(int panonopt_start, int panonopt_end, int opt_end, + char * const *nargv) +{ + int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; + char *swap; + + /* + * compute lengths of blocks and number and size of cycles + */ + nnonopts = panonopt_end - panonopt_start; + nopts = opt_end - panonopt_end; + ncycle = gcd(nnonopts, nopts); + cyclelen = (opt_end - panonopt_start) / ncycle; + + for (i = 0; i < ncycle; i++) { + cstart = panonopt_end+i; + pos = cstart; + for (j = 0; j < cyclelen; j++) { + if (pos >= panonopt_end) + pos -= nnonopts; + else + pos += nopts; + swap = nargv[pos]; + /* LINTED const cast */ + ((char **) nargv)[pos] = nargv[cstart]; + /* LINTED const cast */ + ((char **)nargv)[cstart] = swap; + } + } +} + +/* + * parse_long_options -- + * Parse long options in argc/argv argument vector. + * Returns -1 if short_too is set and the option does not match long_options. + */ +static int +parse_long_options(char * const *nargv, const char *options, + const struct option *long_options, int *idx, int short_too, int flags) +{ + char *current_argv, *has_equal; +#ifdef GNU_COMPATIBLE + char *current_dash; +#endif + size_t current_argv_len; + int i, match, exact_match, second_partial_match; + + current_argv = place; +#ifdef GNU_COMPATIBLE + switch (dash_prefix) { + case D_PREFIX: + current_dash = "-"; + break; + case DD_PREFIX: + current_dash = "--"; + break; + case W_PREFIX: + current_dash = "-W "; + break; + default: + current_dash = ""; + break; + } +#endif + match = -1; + exact_match = 0; + second_partial_match = 0; + + optind++; + + if ((has_equal = strchr(current_argv, '=')) != NULL) { + /* argument found (--option=arg) */ + current_argv_len = has_equal - current_argv; + has_equal++; + } else + current_argv_len = strlen(current_argv); + + for (i = 0; long_options[i].name; i++) { + /* find matching long option */ + if (strncmp(current_argv, long_options[i].name, + current_argv_len)) + continue; + + if (strlen(long_options[i].name) == current_argv_len) { + /* exact match */ + match = i; + exact_match = 1; + break; + } + /* + * If this is a known short option, don't allow + * a partial match of a single character. + */ + if (short_too && current_argv_len == 1) + continue; + + if (match == -1) /* first partial match */ + match = i; + else if ((flags & FLAG_LONGONLY) || + long_options[i].has_arg != + long_options[match].has_arg || + long_options[i].flag != long_options[match].flag || + long_options[i].val != long_options[match].val) + second_partial_match = 1; + } + if (!exact_match && second_partial_match) { + /* ambiguous abbreviation */ + if (PRINT_ERROR) + warnx(ambig, +#ifdef GNU_COMPATIBLE + current_dash, +#endif + (int)current_argv_len, + current_argv); + optopt = 0; + return (BADCH); + } + if (match != -1) { /* option found */ + if (long_options[match].has_arg == no_argument + && has_equal) { + if (PRINT_ERROR) + warnx(noarg, +#ifdef GNU_COMPATIBLE + current_dash, +#endif + (int)current_argv_len, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; +#ifdef GNU_COMPATIBLE + return (BADCH); +#else + return (BADARG); +#endif + } + if (long_options[match].has_arg == required_argument || + long_options[match].has_arg == optional_argument) { + if (has_equal) + optarg = has_equal; + else if (long_options[match].has_arg == + required_argument) { + /* + * optional argument doesn't use next nargv + */ + optarg = nargv[optind++]; + } + } + if ((long_options[match].has_arg == required_argument) + && (optarg == NULL)) { + /* + * Missing argument; leading ':' indicates no error + * should be generated. + */ + if (PRINT_ERROR) + warnx(recargstring, +#ifdef GNU_COMPATIBLE + current_dash, +#endif + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + --optind; + return (BADARG); + } + } else { /* unknown option */ + if (short_too) { + --optind; + return (-1); + } + if (PRINT_ERROR) + warnx(illoptstring, +#ifdef GNU_COMPATIBLE + current_dash, +#endif + current_argv); + optopt = 0; + return (BADCH); + } + if (idx) + *idx = match; + if (long_options[match].flag) { + *long_options[match].flag = long_options[match].val; + return (0); + } else + return (long_options[match].val); +} + +/* + * getopt_internal -- + * Parse argc/argv argument vector. Called by user level routines. + */ +static int +getopt_internal(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx, int flags) +{ + char *oli; /* option letter list index */ + int optchar, short_too; + + if (options == NULL) + return (-1); + + /* + * XXX Some GNU programs (like cvs) set optind to 0 instead of + * XXX using optreset. Work around this braindamage. + */ + if (optind == 0) + optind = optreset = 1; + + if (*options == '-') + flags |= FLAG_ALLARGS; + if (*options == '+' || *options == '-') + options++; + + optarg = NULL; + if (optreset) + nonopt_start = nonopt_end = -1; +start: + if (optreset || !*place) { /* update scanning pointer */ + optreset = 0; + if (optind >= nargc) { /* end of argument vector */ + place = EMSG; + if (nonopt_end != -1) { + /* do permutation, if we have to */ + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + else if (nonopt_start != -1) { + /* + * If we skipped non-options, set optind + * to the first of them. + */ + optind = nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + if (*(place = nargv[optind]) != '-' || +#ifdef GNU_COMPATIBLE + place[1] == '\0') { +#else + (place[1] == '\0' && strchr(options, '-') == NULL)) { +#endif + place = EMSG; /* found non-option */ + if (flags & FLAG_ALLARGS) { + /* + * GNU extension: + * return non-option as argument to option 1 + */ + optarg = nargv[optind++]; + return (INORDER); + } + if (!(flags & FLAG_PERMUTE)) { + /* + * If no permutation wanted, stop parsing + * at first non-option. + */ + return (-1); + } + /* do permutation */ + if (nonopt_start == -1) + nonopt_start = optind; + else if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + nonopt_start = optind - + (nonopt_end - nonopt_start); + nonopt_end = -1; + } + optind++; + /* process next argument */ + goto start; + } + if (nonopt_start != -1 && nonopt_end == -1) + nonopt_end = optind; + + /* + * If we have "-" do nothing, if "--" we are done. + */ + if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { + optind++; + place = EMSG; + /* + * We found an option (--), so if we skipped + * non-options, we have to permute. + */ + if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + } + + /* + * Check long options if: + * 1) we were passed some + * 2) the arg is not just "-" + * 3) either the arg starts with -- we are getopt_long_only() + */ + if (long_options != NULL && place != nargv[optind] && + (*place == '-' || (flags & FLAG_LONGONLY))) { + short_too = 0; +#ifdef GNU_COMPATIBLE + dash_prefix = D_PREFIX; +#endif + if (*place == '-') { + place++; /* --foo long option */ +#ifdef GNU_COMPATIBLE + dash_prefix = DD_PREFIX; +#endif + } else if (*place != ':' && strchr(options, *place) != NULL) + short_too = 1; /* could be short option too */ + + optchar = parse_long_options(nargv, options, long_options, + idx, short_too, flags); + if (optchar != -1) { + place = EMSG; + return (optchar); + } + } + + if ((optchar = (int)*place++) == (int)':' || + (optchar == (int)'-' && *place != '\0') || + (oli = strchr(options, optchar)) == NULL) { + /* + * If the user specified "-" and '-' isn't listed in + * options, return -1 (non-option) as per POSIX. + * Otherwise, it is an unknown option character (or ':'). + */ + if (optchar == (int)'-' && *place == '\0') + return (-1); + if (!*place) + ++optind; +#ifdef GNU_COMPATIBLE + if (PRINT_ERROR) + warnx(gnuoptchar, optchar); +#else + if (PRINT_ERROR) + warnx(illoptchar, optchar); +#endif + optopt = optchar; + return (BADCH); + } + if (long_options != NULL && optchar == 'W' && oli[1] == ';') { + /* -W long-option */ + if (*place) /* no space */ + /* NOTHING */; + else if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return (BADARG); + } else /* white space */ + place = nargv[optind]; +#ifdef GNU_COMPATIBLE + dash_prefix = W_PREFIX; +#endif + optchar = parse_long_options(nargv, options, long_options, + idx, 0, flags); + place = EMSG; + return (optchar); + } + if (*++oli != ':') { /* doesn't take argument */ + if (!*place) + ++optind; + } else { /* takes (optional) argument */ + optarg = NULL; + if (*place) /* no white space */ + optarg = place; + else if (oli[1] != ':') { /* arg not optional */ + if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return (BADARG); + } else + optarg = nargv[optind]; + } + place = EMSG; + ++optind; + } + /* dump back option letter */ + return (optchar); +} + +#ifdef REPLACE_GETOPT +/* + * getopt -- + * Parse argc/argv argument vector. + * + * [eventually this will replace the BSD getopt] + */ +int +getopt(int nargc, char * const *nargv, const char *options) +{ + + /* + * We don't pass FLAG_PERMUTE to getopt_internal() since + * the BSD getopt(3) (unlike GNU) has never done this. + * + * Furthermore, since many privileged programs call getopt() + * before dropping privileges it makes sense to keep things + * as simple (and bug-free) as possible. + */ + return (getopt_internal(nargc, nargv, options, NULL, NULL, 0)); +} +#endif /* REPLACE_GETOPT */ + +/* + * getopt_long -- + * Parse argc/argv argument vector. + */ +int +getopt_long(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx) +{ + + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE)); +} + +/* + * getopt_long_only -- + * Parse argc/argv argument vector. + */ +int +getopt_long_only(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx) +{ + + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE|FLAG_LONGONLY)); +} diff --git a/node.gyp b/node.gyp index 70c9841a89a176..1b6b30e489734f 100644 --- a/node.gyp +++ b/node.gyp @@ -84,6 +84,7 @@ 'dependencies': [ 'node_js2c#host', 'deps/cares/cares.gyp:cares', + 'deps/getopt/getopt.gyp:getopt', 'deps/v8/tools/gyp/v8.gyp:v8', 'deps/v8/tools/gyp/v8.gyp:v8_libplatform' ], @@ -92,6 +93,7 @@ 'src', 'tools/msvs/genfiles', 'deps/uv/src/ares', + 'deps/getopt', '<(SHARED_INTERMEDIATE_DIR)', # for node_natives.h 'deps/v8' # include/v8_platform.h ], @@ -112,6 +114,7 @@ 'src/node_http_parser.cc', 'src/node_javascript.cc', 'src/node_main.cc', + 'src/node_options.cc', 'src/node_os.cc', 'src/node_v8.cc', 'src/node_stat_watcher.cc', @@ -148,6 +151,7 @@ 'src/node_http_parser.h', 'src/node_internals.h', 'src/node_javascript.h', + 'src/node_options.h', 'src/node_root_certs.h', 'src/node_version.h', 'src/node_watchdog.h', @@ -628,7 +632,7 @@ { 'target_name': 'cctest', 'type': 'executable', - 'dependencies': [ + 'dependencies': [ 'deps/gtest/gtest.gyp:gtest', 'deps/v8/tools/gyp/v8.gyp:v8', 'deps/v8/tools/gyp/v8.gyp:v8_libplatform' diff --git a/src/node.cc b/src/node.cc index f47dd722056a8d..01136890ab9268 100644 --- a/src/node.cc +++ b/src/node.cc @@ -4,6 +4,7 @@ #include "node_file.h" #include "node_http_parser.h" #include "node_javascript.h" +#include "node_options.h" #include "node_version.h" #if defined HAVE_PERFCTR @@ -111,18 +112,6 @@ using v8::V8; using v8::Value; using v8::kExternalUint32Array; -static bool print_eval = false; -static bool force_repl = false; -static bool trace_deprecation = false; -static bool throw_deprecation = false; -static bool abort_on_uncaught_exception = false; -static bool trace_sync_io = false; -static const char* eval_string = nullptr; -static unsigned int preload_module_count = 0; -static const char** preload_modules = nullptr; -static bool use_debug_agent = false; -static bool debug_wait_connect = false; -static int debug_port = 5858; static bool v8_is_profiling = false; static bool node_is_initialized = false; static node_module* modpending; @@ -130,10 +119,8 @@ static node_module* modlist_builtin; static node_module* modlist_linked; static node_module* modlist_addon; -#if defined(NODE_HAVE_I18N_SUPPORT) -// Path to ICU data (for i18n / Intl) -static const char* icu_data_dir = nullptr; -#endif +// cli options +NodeOptions node_options; // used by C++ modules as well bool no_deprecation = false; @@ -1266,13 +1253,13 @@ enum encoding ParseEncoding(const char* encoding, } else if (strcasecmp(encoding, "hex") == 0) { return HEX; } else if (strcasecmp(encoding, "raw") == 0) { - if (!no_deprecation) { + if (!node_options.no_deprecation) { fprintf(stderr, "'raw' (array of integers) has been removed. " "Use 'binary'.\n"); } return BINARY; } else if (strcasecmp(encoding, "raws") == 0) { - if (!no_deprecation) { + if (!node_options.no_deprecation) { fprintf(stderr, "'raws' encoding has been renamed to 'binary'. " "Please update your code.\n"); } @@ -2548,14 +2535,14 @@ static Handle GetFeatures(Environment* env) { static void DebugPortGetter(Local property, const PropertyCallbackInfo& info) { - info.GetReturnValue().Set(debug_port); + info.GetReturnValue().Set(node_options.debug_port); } static void DebugPortSetter(Local property, Local value, const PropertyCallbackInfo& info) { - debug_port = value->Int32Value(); + node_options.debug_port = value->Int32Value(); } @@ -2789,56 +2776,58 @@ void SetupProcessObject(Environment* env, env->as_external()); // -e, --eval - if (eval_string) { + if (node_options.eval_string) { READONLY_PROPERTY(process, "_eval", - String::NewFromUtf8(env->isolate(), eval_string)); + String::NewFromUtf8(env->isolate(), + node_options.eval_string)); } // -p, --print - if (print_eval) { + if (node_options.print_eval) { READONLY_PROPERTY(process, "_print_eval", True(env->isolate())); } // -i, --interactive - if (force_repl) { + if (node_options.force_repl) { READONLY_PROPERTY(process, "_forceRepl", True(env->isolate())); } - if (preload_module_count) { - CHECK(preload_modules); + if (node_options.preload_module_count) { + CHECK(node_options.preload_modules); Local array = Array::New(env->isolate()); - for (unsigned int i = 0; i < preload_module_count; ++i) { - Local module = String::NewFromUtf8(env->isolate(), - preload_modules[i]); + for (unsigned int i = 0; i < node_options.preload_module_count; ++i) { + Local module = String::NewFromUtf8( + env->isolate(), + node_options.preload_modules[i]); array->Set(i, module); } READONLY_PROPERTY(process, "_preload_modules", array); - delete[] preload_modules; - preload_modules = nullptr; - preload_module_count = 0; + delete[] node_options.preload_modules; + node_options.preload_modules = nullptr; + node_options.preload_module_count = 0; } // --no-deprecation - if (no_deprecation) { + if (node_options.no_deprecation) { READONLY_PROPERTY(process, "noDeprecation", True(env->isolate())); } // --throw-deprecation - if (throw_deprecation) { + if (node_options.throw_deprecation) { READONLY_PROPERTY(process, "throwDeprecation", True(env->isolate())); } // --trace-deprecation - if (trace_deprecation) { + if (node_options.trace_deprecation) { READONLY_PROPERTY(process, "traceDeprecation", True(env->isolate())); } // --trace-sync-io - if (trace_sync_io) { + if (node_options.trace_sync_io) { READONLY_PROPERTY(process, "traceSyncIO", True(env->isolate())); // Don't env->set_trace_sync_io(true) because it will be enabled // after LoadEnvironment() has run. @@ -3020,235 +3009,6 @@ void LoadEnvironment(Environment* env) { f->Call(global, 1, &arg); } -static void PrintHelp(); - -static bool ParseDebugOpt(const char* arg) { - const char* port = nullptr; - - if (!strcmp(arg, "--debug")) { - use_debug_agent = true; - } else if (!strncmp(arg, "--debug=", sizeof("--debug=") - 1)) { - use_debug_agent = true; - port = arg + sizeof("--debug=") - 1; - } else if (!strcmp(arg, "--debug-brk")) { - use_debug_agent = true; - debug_wait_connect = true; - } else if (!strncmp(arg, "--debug-brk=", sizeof("--debug-brk=") - 1)) { - use_debug_agent = true; - debug_wait_connect = true; - port = arg + sizeof("--debug-brk=") - 1; - } else if (!strncmp(arg, "--debug-port=", sizeof("--debug-port=") - 1)) { - port = arg + sizeof("--debug-port=") - 1; - } else { - return false; - } - - if (port != nullptr) { - debug_port = atoi(port); - if (debug_port < 1024 || debug_port > 65535) { - fprintf(stderr, "Debug port must be in range 1024 to 65535.\n"); - PrintHelp(); - exit(12); - } - } - - return true; -} - -static void PrintHelp() { - printf("Usage: iojs [options] [ -e script | script.js ] [arguments] \n" - " iojs debug script.js [arguments] \n" - "\n" - "Options:\n" - " -v, --version print io.js version\n" - " -e, --eval script evaluate script\n" - " -p, --print evaluate script and print result\n" - " -i, --interactive always enter the REPL even if stdin\n" - " does not appear to be a terminal\n" - " -r, --require module to preload (option can be repeated)\n" - " --no-deprecation silence deprecation warnings\n" - " --throw-deprecation throw an exception anytime a deprecated " - "function is used\n" - " --trace-deprecation show stack traces on deprecations\n" - " --trace-sync-io show stack trace when use of sync IO\n" - " is detected after the first tick\n" - " --v8-options print v8 command line options\n" -#if defined(NODE_HAVE_I18N_SUPPORT) - " --icu-data-dir=dir set ICU data load path to dir\n" - " (overrides NODE_ICU_DATA)\n" -#if !defined(NODE_HAVE_SMALL_ICU) - " Note: linked-in ICU data is\n" - " present.\n" -#endif -#endif - "\n" - "Environment variables:\n" -#ifdef _WIN32 - "NODE_PATH ';'-separated list of directories\n" -#else - "NODE_PATH ':'-separated list of directories\n" -#endif - " prefixed to the module search path.\n" - "NODE_DISABLE_COLORS Set to 1 to disable colors in the REPL\n" -#if defined(NODE_HAVE_I18N_SUPPORT) - "NODE_ICU_DATA Data path for ICU (Intl object) data\n" -#if !defined(NODE_HAVE_SMALL_ICU) - " (will extend linked-in data)\n" -#endif -#endif - "\n" - "Documentation can be found at https://iojs.org/\n"); -} - - -// Parse command line arguments. -// -// argv is modified in place. exec_argv and v8_argv are out arguments that -// ParseArgs() allocates memory for and stores a pointer to the output -// vector in. The caller should free them with delete[]. -// -// On exit: -// -// * argv contains the arguments with node and V8 options filtered out. -// * exec_argv contains both node and V8 options and nothing else. -// * v8_argv contains argv[0] plus any V8 options -static void ParseArgs(int* argc, - const char** argv, - int* exec_argc, - const char*** exec_argv, - int* v8_argc, - const char*** v8_argv) { - const unsigned int nargs = static_cast(*argc); - const char** new_exec_argv = new const char*[nargs]; - const char** new_v8_argv = new const char*[nargs]; - const char** new_argv = new const char*[nargs]; - const char** local_preload_modules = new const char*[nargs]; - - for (unsigned int i = 0; i < nargs; ++i) { - new_exec_argv[i] = nullptr; - new_v8_argv[i] = nullptr; - new_argv[i] = nullptr; - local_preload_modules[i] = nullptr; - } - - // exec_argv starts with the first option, the other two start with argv[0]. - unsigned int new_exec_argc = 0; - unsigned int new_v8_argc = 1; - unsigned int new_argc = 1; - new_v8_argv[0] = argv[0]; - new_argv[0] = argv[0]; - - unsigned int index = 1; - while (index < nargs && argv[index][0] == '-') { - const char* const arg = argv[index]; - unsigned int args_consumed = 1; - - if (ParseDebugOpt(arg)) { - // Done, consumed by ParseDebugOpt(). - } else if (strcmp(arg, "--version") == 0 || strcmp(arg, "-v") == 0) { - printf("%s\n", NODE_VERSION); - exit(0); - } else if (strcmp(arg, "--help") == 0 || strcmp(arg, "-h") == 0) { - PrintHelp(); - exit(0); - } else if (strcmp(arg, "--eval") == 0 || - strcmp(arg, "-e") == 0 || - strcmp(arg, "--print") == 0 || - strcmp(arg, "-pe") == 0 || - strcmp(arg, "-p") == 0) { - bool is_eval = strchr(arg, 'e') != nullptr; - bool is_print = strchr(arg, 'p') != nullptr; - print_eval = print_eval || is_print; - // --eval, -e and -pe always require an argument. - if (is_eval == true) { - args_consumed += 1; - eval_string = argv[index + 1]; - if (eval_string == nullptr) { - fprintf(stderr, "%s: %s requires an argument\n", argv[0], arg); - exit(9); - } - } else if ((index + 1 < nargs) && - argv[index + 1] != nullptr && - argv[index + 1][0] != '-') { - args_consumed += 1; - eval_string = argv[index + 1]; - if (strncmp(eval_string, "\\-", 2) == 0) { - // Starts with "\\-": escaped expression, drop the backslash. - eval_string += 1; - } - } - } else if (strcmp(arg, "--require") == 0 || - strcmp(arg, "-r") == 0) { - const char* module = argv[index + 1]; - if (module == nullptr) { - fprintf(stderr, "%s: %s requires an argument\n", argv[0], arg); - exit(9); - } - args_consumed += 1; - local_preload_modules[preload_module_count++] = module; - } else if (strcmp(arg, "--interactive") == 0 || strcmp(arg, "-i") == 0) { - force_repl = true; - } else if (strcmp(arg, "--no-deprecation") == 0) { - no_deprecation = true; - } else if (strcmp(arg, "--trace-deprecation") == 0) { - trace_deprecation = true; - } else if (strcmp(arg, "--trace-sync-io") == 0) { - trace_sync_io = true; - } else if (strcmp(arg, "--throw-deprecation") == 0) { - throw_deprecation = true; - } else if (strcmp(arg, "--abort-on-uncaught-exception") == 0 || - strcmp(arg, "--abort_on_uncaught_exception") == 0) { - abort_on_uncaught_exception = true; - } else if (strcmp(arg, "--v8-options") == 0) { - new_v8_argv[new_v8_argc] = "--help"; - new_v8_argc += 1; -#if defined(NODE_HAVE_I18N_SUPPORT) - } else if (strncmp(arg, "--icu-data-dir=", 15) == 0) { - icu_data_dir = arg + 15; -#endif - } else if (strcmp(arg, "--expose-internals") == 0 || - strcmp(arg, "--expose_internals") == 0) { - // consumed in js - } else { - // V8 option. Pass through as-is. - new_v8_argv[new_v8_argc] = arg; - new_v8_argc += 1; - } - - memcpy(new_exec_argv + new_exec_argc, - argv + index, - args_consumed * sizeof(*argv)); - - new_exec_argc += args_consumed; - index += args_consumed; - } - - // Copy remaining arguments. - const unsigned int args_left = nargs - index; - memcpy(new_argv + new_argc, argv + index, args_left * sizeof(*argv)); - new_argc += args_left; - - *exec_argc = new_exec_argc; - *exec_argv = new_exec_argv; - *v8_argc = new_v8_argc; - *v8_argv = new_v8_argv; - - // Copy new_argv over argv and update argc. - memcpy(argv, new_argv, new_argc * sizeof(*argv)); - delete[] new_argv; - *argc = static_cast(new_argc); - - // Copy the preload_modules from the local array to an appropriately sized - // global array. - if (preload_module_count > 0) { - CHECK(!preload_modules); - preload_modules = new const char*[preload_module_count]; - memcpy(preload_modules, local_preload_modules, - preload_module_count * sizeof(*preload_modules)); - } - delete[] local_preload_modules; -} - // Called from V8 Debug Agent TCP thread. static void DispatchMessagesDebugAgentCallback(Environment* env) { @@ -3262,9 +3022,12 @@ static void StartDebug(Environment* env, bool wait) { env->debugger_agent()->set_dispatch_handler( DispatchMessagesDebugAgentCallback); - debugger_running = env->debugger_agent()->Start(debug_port, wait); + debugger_running = env->debugger_agent()->Start(node_options.debug_port, + wait); if (debugger_running == false) { - fprintf(stderr, "Starting debugger on port %d failed\n", debug_port); + fprintf(stderr, + "Starting debugger on port %d failed\n", + node_options.debug_port); fflush(stderr); return; } @@ -3634,8 +3397,8 @@ void Init(int* argc, // Parse a few arguments which are specific to Node. int v8_argc; const char** v8_argv; - ParseArgs(argc, argv, exec_argc, exec_argv, &v8_argc, &v8_argv); - + node_options.ParseArgs(argc, argv, exec_argc, exec_argv, &v8_argc, &v8_argv); + no_deprecation = node_options.no_deprecation; // TODO(bnoordhuis) Intercept --prof arguments and start the CPU profiler // manually? That would give us a little more control over its runtime // behavior but it could also interfere with the user's intentions in ways @@ -3674,14 +3437,14 @@ void Init(int* argc, exit(9); } - if (debug_wait_connect) { + if (node_options.debug_wait_connect) { const char expose_debug_as[] = "--expose_debug_as=v8debug"; V8::SetFlagsFromString(expose_debug_as, sizeof(expose_debug_as) - 1); } V8::SetArrayBufferAllocator(&ArrayBufferAllocator::the_singleton); - if (!use_debug_agent) { + if (!node_options.use_debug_agent) { RegisterDebugSignalHandler(); } @@ -3894,14 +3657,15 @@ static void StartNodeInstance(void* arg) { Environment* env = CreateEnvironment(isolate, context, instance_data); Context::Scope context_scope(context); if (instance_data->is_main()) - env->set_using_abort_on_uncaught_exc(abort_on_uncaught_exception); + env->set_using_abort_on_uncaught_exc( + node_options.abort_on_uncaught_exception); // Start debug agent when argv has --debug if (instance_data->use_debug_agent()) - StartDebug(env, debug_wait_connect); + StartDebug(env, node_options.debug_wait_connect); LoadEnvironment(env); - env->set_trace_sync_io(trace_sync_io); + env->set_trace_sync_io(node_options.trace_sync_io); // Enable debugger if (instance_data->use_debug_agent()) @@ -3978,7 +3742,7 @@ int Start(int argc, char** argv) { const_cast(argv), exec_argc, exec_argv, - use_debug_agent); + node_options.use_debug_agent); StartNodeInstance(&instance_data); exit_code = instance_data.exit_code(); } diff --git a/src/node_options.cc b/src/node_options.cc new file mode 100644 index 00000000000000..276c5cd3272917 --- /dev/null +++ b/src/node_options.cc @@ -0,0 +1,305 @@ +#include "node_options.h" +#include "node_version.h" +#include "node_internals.h" +#include "getopt.h" +#include +#include +#include + +namespace node { + +enum { + OPT_NO_DEPRECATION = 1000, + OPT_THROW_DEPRECATION, + OPT_TRACE_DEPRECATION, + OPT_TRACE_SYNC_IO, + OPT_V8_OPTIONS, + OPT_ABORT_UNCAUGHT, + OPT_EXPOSE_INTERNALS, + OPT_DEBUG, + OPT_DEBUG_BRK, + OPT_DEBUG_PORT, +#if defined(NODE_HAVE_I18N_SUPPORT) + OPT_ICU_DATA_DIR +#endif +}; + +static struct option longopts[] = { + { "help", no_argument, nullptr, 'h', "show help and usage" }, + { "version", no_argument, nullptr, 'v', "print io.js version" }, + { "eval", optional_argument, nullptr, 'e', "evaluate script" }, + { "print", optional_argument, nullptr, 'p', + "evaluate script and print result" }, + { "interactive", no_argument, nullptr, 'i', + "always enter the REPL even if stdin " + "does not appear to be a terminal" }, + { "require", required_argument, nullptr, 'r', "module to preload" }, + { "no-deprecation", no_argument, nullptr, OPT_NO_DEPRECATION, + "silence deprecation warnings" }, + { "throw-deprecation", no_argument, nullptr, OPT_THROW_DEPRECATION, + "throw an exception anytime a deprecated function is used" }, + { "trace-deprecation", no_argument, nullptr, OPT_TRACE_DEPRECATION, + "show stack traces on deprecations" }, + { "trace-sync-io", no_argument, nullptr, OPT_TRACE_SYNC_IO, + "show stake trace when use of sync IO " + "is detected after the first tick" }, + { "v8-options", no_argument, nullptr, OPT_V8_OPTIONS, + "print v8 command line options" }, + { "v8_options", no_argument, nullptr, OPT_V8_OPTIONS, + "print v8 command line options" }, + { "abort-on-uncaught-exception", no_argument, nullptr, OPT_ABORT_UNCAUGHT, + "abort on uncaught exception" }, + { "abort_on_uncaught_exception", no_argument, nullptr, OPT_ABORT_UNCAUGHT, + "abort on uncaught exception" }, + { "expose-internals", no_argument, nullptr, OPT_EXPOSE_INTERNALS, + "expose internal modules" }, + { "expose_internals", no_argument, nullptr, OPT_EXPOSE_INTERNALS, + "expose internal modules" }, + { "debug", optional_argument, nullptr, OPT_DEBUG, "enable debug mode" }, + { "debug-brk", optional_argument, nullptr, OPT_DEBUG_BRK, + "break before starting" }, + { "debug-port", required_argument, nullptr, OPT_DEBUG_PORT, + "specify debug port (defaults to 5858)" }, +#if defined(NODE_HAVE_I18N_SUPPORT) + { "icu-data-dir", required_argument, nullptr, OPT_ICU_DATA_DIR }, +#endif + { nullptr, 0, nullptr, 0, "" } +}; + +void NodeOptions::PrintHelp() { + printf("Usage: iojs [options] [ -e script | script.js ] [arguments] \n" + " iojs debug script.js [arguments] \n" + "\n" + "Options:\n"); + for (size_t i = 0; i < ARRAY_SIZE(longopts); i++) { + if (longopts[i].name == nullptr) + continue; + if (longopts[i].val < 1000) { + printf("\t-%c, --%-30s %-50s\n", + longopts[i].val, + longopts[i].name, + longopts[i].desc); + } else { + printf("\t --%-30s %-50s\n", longopts[i].name, longopts[i].desc); + } + } + + printf("\n"); + + printf("Environment variables:\n" +#ifdef _WIN32 + "\t NODE_PATH ';'-separated list of directories\n" +#else + "\t NODE_PATH ':'-separated list of directories\n" +#endif + "\t prefixed to the module search path.\n" + "\t NODE_DISABLE_COLORS Set to 1 to disable colors in the REPL\n" +#if defined(NODE_HAVE_I18N_SUPPORT) + "\t NODE_ICU_DATA Data path for ICU (Intl object) data\n" +#if !defined(NODE_HAVE_SMALL_ICU) + "\t (will extend linked-in data)\n" +#endif +#endif + "\n" + "Documentation can be found at https://iojs.org/\n"); +} + +void NodeOptions::ParseArgs(int* argc, + const char** argv, + int* exec_argc, + const char*** exec_argv, + int* v8_argc, + const char*** v8_argv) { + const unsigned int nargs = static_cast(*argc); + const char** new_exec_argv = new const char*[nargs]; + const char** new_v8_argv = new const char*[nargs]; + const char** new_argv = new const char*[nargs]; + const char** local_preload_modules = new const char*[nargs]; + + // we are mutating the strings vector but not the strings themselves + char** largv = const_cast(argv); + for (unsigned int i = 0; i < nargs; ++i) { + new_exec_argv[i] = nullptr; + new_v8_argv[i] = nullptr; + new_argv[i] = nullptr; + local_preload_modules[i] = nullptr; + } + + const char* port = nullptr; + + // exec_argv starts with the first option, the other two start with argv[0]. + unsigned int new_exec_argc = 0; + unsigned int new_v8_argc = 1; + unsigned int new_argc = 1; + new_v8_argv[0] = argv[0]; + new_argv[0] = argv[0]; + int rc = 0; + unsigned int index = 1; + bool is_eval = false; + bool is_print = false; + const char optstring[] = ":hve:p::ir:d::b::x:"; + while ((rc = getopt_long(*argc, largv, optstring, longopts, NULL)) != -1 && + argv[index][0] == '-') { + unsigned int args_consumed = 1; + const char* const arg = argv[index]; + switch (rc) { + case 'h': + PrintHelp(); + exit(0); + break; + case 'v': + printf("%s\n", NODE_VERSION); + exit(0); + break; + case 'e': + case 'p': + { + if (!is_eval) + is_eval = (rc == 'e'); + + if (!is_print) + is_print = (rc == 'p'); + const char* name = is_eval ? "eval" : "print"; + print_eval = print_eval || is_print; + if (is_eval == true) { + eval_string = argv[index + 1]; + args_consumed += 1; + if (eval_string == nullptr) { + fprintf(stderr, "%s: %s requires an argument\n", argv[0], name); + exit(9); + } + } else if ((index + 1 < nargs) && + argv[index + 1] != nullptr && + argv[index + 1][0] != '-') { + eval_string = argv[index + 1]; + args_consumed += 1; + if (strncmp(eval_string, "\\-", 2) == 0) { + // Starts with "\\-": escaped expression, drop the backslash. + eval_string += 1; + } + } + break; + } + case 'i': + force_repl = true; + break; + case 'r': + { + const char* module = argv[index + 1]; + args_consumed += 1; + if (module == nullptr) { + fprintf(stderr, "%s: %s requires an argument\n", argv[0], arg); + exit(9); + } + local_preload_modules[preload_module_count++] = module; + break; + } + case OPT_NO_DEPRECATION: + no_deprecation = true; + break; + case OPT_THROW_DEPRECATION: + throw_deprecation = true; + break; + case OPT_TRACE_DEPRECATION: + trace_deprecation = true; + break; + case OPT_TRACE_SYNC_IO: + trace_sync_io = true; + break; + case OPT_V8_OPTIONS: + new_v8_argv[new_v8_argc] = "--help"; + new_v8_argc += 1; + break; + case OPT_ABORT_UNCAUGHT: + abort_on_uncaught_exception = true; + break; + case OPT_EXPOSE_INTERNALS: + // pass through + break; + case OPT_DEBUG: + { + use_debug_agent = true; + if (optarg != nullptr) { + port = const_cast(optarg); + } + break; + } + case OPT_DEBUG_BRK: + { + use_debug_agent = true; + debug_wait_connect = true; + if (optarg != nullptr) { + port = const_cast(optarg); + } + break; + } + case OPT_DEBUG_PORT: + { + port = optarg; + break; + } +#if defined(NODE_HAVE_I18N_SUPPORT) + case OPT_ICU_DATA_DIR: + { + if (optarg != nullptr) { + icu_data_dir = const_cast(optarg); + } + break; + } +#endif + case '?': + { + if (arg[0] == '-') { + // V8 option. Pass through as-is. + new_v8_argv[new_v8_argc] = arg; + new_v8_argc += 1; + } + break; + } + } + + memcpy(new_exec_argv + new_exec_argc, + largv + index, + args_consumed * sizeof(*largv)); + + new_exec_argc += args_consumed; + index += args_consumed; + } + + if (port != nullptr) { + debug_port = atoi(port); + if (debug_port < 1024 || debug_port > 65535) { + fprintf(stderr, "Debug port must be in range 1024 to 65535.\n"); + PrintHelp(); + exit(12); + } + } + + // Copy remaining arguments. + const unsigned int args_left = nargs - index; + memcpy(new_argv + new_argc, argv + index, args_left * sizeof(*argv)); + new_argc += args_left; + + *exec_argc = new_exec_argc; + *exec_argv = new_exec_argv; + *v8_argc = new_v8_argc; + *v8_argv = new_v8_argv; + + // Copy new_argv over argv and update argc. + memcpy(argv, new_argv, new_argc * sizeof(*argv)); + delete[] new_argv; + *argc = static_cast(new_argc); + + // Copy the preload_modules from the local array to an appropriately sized + // global array. + if (preload_module_count > 0) { + CHECK(!preload_modules); + preload_modules = new const char*[preload_module_count]; + memcpy(preload_modules, + local_preload_modules, + preload_module_count * sizeof(*preload_modules)); + } + delete[] local_preload_modules; +} + +} // namespace node diff --git a/src/node_options.h b/src/node_options.h new file mode 100644 index 00000000000000..149db444cb3263 --- /dev/null +++ b/src/node_options.h @@ -0,0 +1,57 @@ +#ifndef SRC_NODE_OPTIONS_H_ +#define SRC_NODE_OPTIONS_H_ + +#include "node_version.h" +#include "util.h" +#include "getopt.h" +#include +#include +#include + +namespace node { + +class NodeOptions { + public: + bool print_eval = false; + bool force_repl = false; + bool trace_deprecation = false; + bool throw_deprecation = false; + bool abort_on_uncaught_exception = false; + bool trace_sync_io = false; + const char* eval_string = nullptr; + unsigned int preload_module_count = 0; + const char** preload_modules = nullptr; + bool use_debug_agent = false; + bool debug_wait_connect = false; + int debug_port = 5858; + bool no_deprecation = false; +#if defined(NODE_HAVE_I18N_SUPPORT) + // Path to ICU data (for i18n / Intl) + const char* icu_data_dir = nullptr; +#endif + + // Print help to stdout + void PrintHelp(); + + // Parse command line arguments. + // + // argv is modified in place. exec_argv and v8_argv are out arguments that + // ParseArgs() allocates memory for and stores a pointer to the output + // vector in. The caller should free them with delete[]. + // + // On exit: + // + // * argv contains the arguments with node and V8 options filtered out. + // * exec_argv contains both node and V8 options and nothing else. + // * v8_argv contains argv[0] plus any V8 options + void ParseArgs(int* argc, + const char** argv, + int* exec_argc, + const char*** exec_argv, + int* v8_argc, + const char*** v8_argv); +}; + +} // namespace node + +#endif // SRC_NODE_OPTIONS_H_ diff --git a/test/parallel/test-cli-debug.js b/test/parallel/test-cli-debug.js new file mode 100644 index 00000000000000..9607d84546dc68 --- /dev/null +++ b/test/parallel/test-cli-debug.js @@ -0,0 +1,19 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const execFile = require('child_process').execFile; +var depmod = require.resolve('../fixtures/printA.js'); +var node = process.execPath; + +var debug = ['--debug', depmod]; +var debugPort = ['--debug=5859', depmod]; + +function handle(port) { + return function(er, stdout, stderr) { + assert.equal(er, null); + assert.equal(stderr, `Debugger listening on port ${port}\n`); + } +} + +execFile(node, debug, handle(5858)); +execFile(node, debugPort, handle(5859)); diff --git a/test/parallel/test-cli-help.js b/test/parallel/test-cli-help.js new file mode 100644 index 00000000000000..20e29c5002a37e --- /dev/null +++ b/test/parallel/test-cli-help.js @@ -0,0 +1,11 @@ +'use strict'; +const assert = require('assert'); +const spawn = require('child_process').spawn; + +// test --help +var child = spawn(process.execPath, ['--help']); +child.stdout.on('data', function(chunk) { + assert.equal(/Usage/.test(chunk.toString()), true); +}); + +child.stderr.on('data', assert.fail); diff --git a/test/parallel/test-cli-v8-options.js b/test/parallel/test-cli-v8-options.js new file mode 100644 index 00000000000000..7dcbd09e1e994e --- /dev/null +++ b/test/parallel/test-cli-v8-options.js @@ -0,0 +1,17 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const execFile = require('child_process').execFile; + +// test --v8-options and --v8_options +(function runTest(flags) { + var flag = flags.pop(); + execFile(process.execPath, [flag], function(err, stdout, stderr) { + assert.equal(err, null); + assert.equal(stderr, ''); + var out = stdout.split('\n'); + assert.equal(out[1], 'Usage:'); + if (flags.length > 0) + setImmediate(runTest, flags); + }); +})(['--v8-options', '--v8_options']); diff --git a/test/parallel/test-cli-version.js b/test/parallel/test-cli-version.js new file mode 100644 index 00000000000000..337178018eda2c --- /dev/null +++ b/test/parallel/test-cli-version.js @@ -0,0 +1,12 @@ +'use strict'; +const assert = require('assert'); +const spawn = require('child_process').spawn; + +// test --version +var child = spawn(process.execPath, ['--version']); +child.stdout.on('data', function(chunk) { + // just in case the version ever has anything appended to it (nightlies?) + assert.equal(/v([\d]+).([\d]+).([\d]+)(.*)/.test(chunk.toString()), true); +}); + +child.stderr.on('data', assert.fail); diff --git a/test/sequential/test-deprecation-flags.js b/test/sequential/test-deprecation-flags.js index 2af60a17364999..6abdc49403316e 100644 --- a/test/sequential/test-deprecation-flags.js +++ b/test/sequential/test-deprecation-flags.js @@ -7,6 +7,7 @@ var node = process.execPath; var normal = [depmod]; var noDep = ['--no-deprecation', depmod]; var traceDep = ['--trace-deprecation', depmod]; +var throwDep = ['--throw-deprecation', depmod]; execFile(node, normal, function(er, stdout, stderr) { console.error('normal: show deprecation warning'); @@ -34,3 +35,13 @@ execFile(node, traceDep, function(er, stdout, stderr) { assert.equal(stack.pop(), '\'This is deprecated\''); console.log('trace ok'); }); + +execFile(node, throwDep, function(er, stdout, stderr) { + console.error('--throw-deprecation: show stack'); + assert.equal(/Error: Command failed/.test(er), true); + assert.equal(stdout, ''); + var stack = stderr.trim().split('\n'); + assert.equal(stack[0], 'util.js:66'); + assert.equal(stack[1], ' throw new Error(msg);'); + console.log('throw ok'); +});