From 92340a6827cf4118ece99cc31c84cd7298e30587 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 4 Apr 2026 13:44:23 -0400 Subject: [PATCH] consolidate some arg processing Unix and Windows had diverged significantly for command line options handling. This: 1. uses the the Unix processing as a baseline. 2. consolidates the code in earlyarg.c, where it can be a common copy to be shared. 3. start converting the Windows command line argument processing to the Unix code that now resides in earlyarg.c. --- include/extern.h | 8 +- include/hack.h | 1 + src/cfgfiles.c | 4 +- src/earlyarg.c | 389 ++++++++++++++++++++++++++++++++++++++- sys/share/pcmain.c | 2 +- sys/unix/unixmain.c | 369 +------------------------------------ sys/windows/consoletty.c | 24 +-- sys/windows/windmain.c | 85 ++++++--- 8 files changed, 480 insertions(+), 402 deletions(-) diff --git a/include/extern.h b/include/extern.h index cf20ca090..8ed1b0f0f 100644 --- a/include/extern.h +++ b/include/extern.h @@ -922,6 +922,10 @@ extern const char *endgamelevelname(char *, int); /* ### earlyarg.c ### */ extern int argcheck(int, char **, enum earlyarg); +extern void early_options(int *argc_p, char ***argv_p, char **hackdir_p); +#ifdef WIN32 +int windows_early_options(const char *); +#endif /* ### eat.c ### */ @@ -2344,9 +2348,9 @@ extern int dohistory(void); /* ### xxmain.c ### */ -#if defined(MICRO) || defined(WIN32) +#if defined(UNIX) || defined(MICRO) || defined(WIN32) #ifdef CHDIR -extern void chdirx(char *, boolean); +extern void chdirx(const char *, boolean); #endif /* CHDIR */ extern boolean authorize_wizard_mode(void); extern boolean authorize_explore_mode(void); diff --git a/include/hack.h b/include/hack.h index 0a025a902..bb9f9d642 100644 --- a/include/hack.h +++ b/include/hack.h @@ -817,6 +817,7 @@ struct sinfo { interface to suppress menu commands in similar conditions; readchar() always resets it to 'otherInp' prior to returning */ int input_state; /* whether next key pressed will be entering a command */ + int early_options; /* inside early_options processing */ #ifdef TTY_GRAPHICS /* resize_pending only matters when handling a SIGWINCH signal for tty; getting_char is used along with that and also separately for UNIX; diff --git a/src/cfgfiles.c b/src/cfgfiles.c index 2084866d0..1b4715a4f 100644 --- a/src/cfgfiles.c +++ b/src/cfgfiles.c @@ -1907,9 +1907,9 @@ assure_syscf_file(void) #else fd = open(SYSCF_FILE, O_RDONLY); #endif -#else +#else /* VMS */ fd = open(SYSCF_FILE, O_RDONLY, 0); -#endif +#endif /* VMS */ if (fd >= 0) { /* readable */ close(fd); diff --git a/src/earlyarg.c b/src/earlyarg.c index 166510993..14a44f770 100755 --- a/src/earlyarg.c +++ b/src/earlyarg.c @@ -3,11 +3,22 @@ /* NetHack may be freely redistributed. See license for details. */ #include "hack.h" +#include "dlb.h" staticfn void debug_fields(char *); #ifndef NODUMPENUMS staticfn void dump_enums(void); #endif +ATTRNORETURN staticfn void opt_terminate(void) NORETURN; +ATTRNORETURN staticfn void opt_usage(const char *) NORETURN; +ATTRNORETURN staticfn void scores_only(int, char **, const char *) NORETURN; +staticfn char *lopt(char *, int, const char *, const char *, int *, char ***); +staticfn void consume_arg(int, int *, char ***); +staticfn void consume_two_args(int, int *, char ***); + +#ifdef UNIX +extern boolean whoami(void); +#endif /* * Argument processing helpers - for xxmain() to share @@ -40,9 +51,383 @@ static const struct early_opt earlyopts[] = { #endif }; -#ifdef WIN32 -extern int windows_early_options(const char *); +static char ArgVal_novalue[] = "[nothing]"; /* note: not 'const' */ + +enum cmdlinearg { + ArgValRequired = 0, + ArgValOptional = 1, + ArgValDisallowed = 2, + ArgVal_mask = (1 | 2), + ArgNamOneLetter = 4, + ArgNam_mask = 4, + ArgErrSilent = 0, + ArgErrComplain = 8, + ArgErr_mask = 8 +}; + +/* approximate 'getopt_long()' for one option; all the comments refer to + "-windowtype" but the code isn't specific to that */ +staticfn char * +lopt(char *arg, /* command line token; beginning matches 'optname' */ + int lflags, /* cmdlinearg | errorhandling */ + const char *optname, /* option's name; "-windowtype" in examples below */ + const char *origarg, /* 'arg' might have had a dash prefix removed */ + int *argc_p, /* argc that can have changes passed to caller */ + char ***argv_p) /* argv[] ditto */ +{ + int argc = *argc_p; + char **argv = *argv_p; + char *p, *nextarg = (argc > 1 && argv[1][0] != '-') ? argv[1] : 0; + int l, opttype = (lflags & ArgVal_mask); + boolean oneletterok = ((lflags & ArgNam_mask) == ArgNamOneLetter), + complain = ((lflags & ArgErr_mask) == ArgErrComplain); + + /* first letter must match */ + if (arg[1] != optname[1]) { + loptbail: + if (complain) + config_error_add("Unknown option: %.60s", origarg); + return (char *) 0; + loptnotallowed: + if (complain) + config_error_add("Value not allowed: %.60s", origarg); + return (char *) 0; + loptrequired: + if (complain) + config_error_add("Missing required value: %.60s", origarg); + return (char *) 0; + } + + if ((p = strchr(arg, '=')) == 0) + p = strchr(arg, ':'); + if (p && opttype == ArgValDisallowed) + goto loptnotallowed; + + l = (int) (p ? (long) (p - arg) : (long) strlen(arg)); + if ((l > 2 || oneletterok) && !strncmp(arg, optname, l)) { + /* "-windowtype[=foo]" */ + if (p) + ++p; /* past '=' or ':' */ + else if (opttype == ArgValRequired) + p = eos(arg); /* we have "-w[indowtype]" w/o "=foo" + * so we'll take foo from next element */ + else + return ArgVal_novalue; + } else if (oneletterok) { + /* "-w..." but not "-w[indowtype[=foo]]" */ + if (!p) { + p = &arg[2]; /* past 'w' of "-wfoo" */ +#if 0 /* -x:value could work but is not supported (callers don't expect it) \ + */ + } else if (p == arg + 2) { + ++p; /* past ':' of "-w:foo" */ #endif + } else { + /* "-w...=foo" but not "-w[indowtype]=foo" */ + goto loptbail; + } + } else { + goto loptbail; + } + if (!p || !*p) { + /* "-w[indowtype]" w/o '='/':' if there is a next element, use + it for "foo"; if not, supply a non-Null bogus value */ + if (nextarg + && (opttype == ArgValRequired || opttype == ArgValOptional)) + p = nextarg, --(*argc_p), ++(*argv_p); + else if (opttype == ArgValRequired) + goto loptrequired; + else + p = ArgVal_novalue; /* there is no next element */ + } + return p; +} +/* move argv[ndx] to end of argv[] array, then reduce argc to hide it; + prevents process_options() from encountering it after early_options() + has processed it; elements get reordered but all remain intact */ +staticfn void +consume_arg(int ndx, int *ac_p, char ***av_p) +{ + char *gone, **av = *av_p; + int i, ac = *ac_p; + + /* "-one -two -three -four" -> "-two -three -four -one" */ + if (ac > 2) { + gone = av[ndx]; + for (i = ndx + 1; i < ac; ++i) + av[i - 1] = av[i]; + av[ac - 1] = gone; + } + --(*ac_p); +} + +/* consume two tokens for '-argname value' w/o '=' or ':' */ +staticfn void +consume_two_args(int ndx, int *ac_p, char ***av_p) +{ + /* when consuming "-two arg" from "-two arg -three -four", + the *ac_p manipulation results in "-three -four -two arg" + rather than the "-three -four arg -two" that would happen + with just two ordinary consume_arg() calls */ + consume_arg(ndx, ac_p, av_p); + ++(*ac_p); /* bring the final slot back into view */ + consume_arg(ndx, ac_p, av_p); + --(*ac_p); /* take away restored slot */ +} + +/* process some command line arguments before loading options */ +void +early_options(int *argc_p, char ***argv_p, char **hackdir_p) +{ + char **argv, *arg, *origarg; + int argc, oldargc, ndx = 0, consumed = 0; + + config_error_init(FALSE, "command line", FALSE); + + /* treat "nethack ?" as a request for usage info; due to shell + processing, player likely has to use "nethack \?" or "nethack '?'" + [won't work if used as "nethack -dpath ?" or "nethack -d path ?"] */ + if (*argc_p > 1 && !strcmp((*argv_p)[1], "?")) + opt_usage(*hackdir_p); /* doesn't return */ + + /* + * Both *argc_p and *argv_p account for the program name as (*argv_p)[0]; + * local argc and argv implicitly discard that (by starting 'ndx' at 1). + * argcheck() doesn't mind, prscore() (via scores_only()) does (for the + * number of args it gets passed, not for the value of argv[0]). + */ + for (ndx = 1; ndx < *argc_p; ndx += (consumed ? 0 : 1)) { + consumed = 0; + argc = *argc_p - ndx; + argv = *argv_p + ndx; + + arg = origarg = argv[0]; + /* skip any args intended for deferred options */ + if (*arg != '-') + continue; + /* allow second dash if arg name is longer than one character */ + if (arg[0] == '-' && arg[1] == '-' && arg[2] != '\0' + && (arg[3] != '\0' && arg[3] != '=' && arg[3] != ':')) + ++arg; + + switch (arg[1]) { /* char after leading dash */ + case 'b': +#ifdef CRASHREPORT + // --bidshow + if (argcheck(argc, argv, ARG_BIDSHOW) == 2) { + opt_terminate(); + /*NOTREACHED*/ + } +#endif + break; + case 'd': + if (argcheck(argc, argv, ARG_DEBUG) == 1) { + consume_arg(ndx, argc_p, argv_p), consumed = 1; +#ifndef NODUMPENUMS + } else if (argcheck(argc, argv, ARG_DUMPENUMS) == 2) { + opt_terminate(); + /*NOTREACHED*/ +#endif + } else if (argcheck(argc, argv, ARG_DUMPMONGEN) == 2) { + opt_terminate(); + /*NOTREACHED*/ + } else if (argcheck(argc, argv, ARG_DUMPWEIGHTS) == 2) { + opt_terminate(); + /*NOTREACHED*/ + } else { +#ifdef CHDIR + oldargc = argc; + arg = lopt(arg, + (ArgValRequired | ArgNamOneLetter | ArgErrSilent), + "-directory", origarg, &argc, &argv); + if (!arg) + error("Flag -d must be followed by a directory name."); + if (*arg != 'e') { /* avoid matching -decgraphics or -debug */ + *hackdir_p = arg; + if (oldargc == argc) + consume_arg(ndx, argc_p, argv_p), consumed = 1; + else + consume_two_args(ndx, argc_p, argv_p), consumed = 2; + } +#endif /* CHDIR */ + } + break; + case 'h': + case '?': + if (lopt(arg, ArgValDisallowed, "-help", origarg, &argc, &argv) + || lopt(arg, ArgValDisallowed | ArgNamOneLetter, "-?", + origarg, &argc, &argv)) + opt_usage(*hackdir_p); /* doesn't return */ + break; + case 'n': + oldargc = argc; + if (!strcmp(arg, "-no-nethackrc")) /* no abbreviation allowed */ + arg = nhStr("/dev/null"); + else + arg = lopt(arg, (ArgValRequired | ArgErrComplain), + "-nethackrc", origarg, &argc, &argv); + if (arg) { + gc.cmdline_rcfile = dupstr(arg); + if (oldargc == argc) + consume_arg(ndx, argc_p, argv_p), consumed = 1; + else + consume_two_args(ndx, argc_p, argv_p), consumed = 2; + } + break; + case 's': + if (argcheck(argc, argv, ARG_SHOWPATHS) == 2) { + gd.deferred_showpaths = TRUE; + gd.deferred_showpaths_dir = *hackdir_p; + config_error_done(); + return; + } + /* check for "-s" request to show scores */ + if (lopt(arg, + ((ArgValDisallowed | ArgErrComplain) + /* only accept one-letter if there is just one + dash; reject "--s" because prscore() via + scores_only() doesn't understand it */ + | ((origarg[1] != '-') ? ArgNamOneLetter : 0)), + /* [ought to omit val-disallowed and accept + --scores=foo since -s foo and -sfoo are + allowed, but -s form can take more than one + space-separated argument and --scores=foo + isn't suited for that] */ + "-scores", origarg, &argc, &argv)) { + /* at this point, argv[0] contains "-scores" or a leading + substring of it; prscore() (via scores_only()) expects + that to be in argv[1] so we adjust the pointer to make + that be the case; if there are any non-early args waiting + to be passed along to process_options(), the resulting + argv[0] will be one of those rather than the program + name but prscore() doesn't care */ + scores_only(argc + 1, argv - 1, *hackdir_p); + /*NOTREACHED*/ + } + break; + case 'u': +#if defined(UNIX) + if (lopt(arg, ArgValDisallowed, "-usage", origarg, &argc, &argv)) + opt_usage(*hackdir_p); +#elif defined(WIN32) || defined(MSDOS) || defined(AMIGA) + if (arg[2]) { + (void) strncpy(svp.plname, arg + 2, sizeof(svp.plname) - 1); + } else if (ndx + 1 < *argc_p) { + const char *nextarg = (*argv_p)[ndx + 1]; + + if (nextarg[0] != '-') { + (void) strncpy(svp.plname, nextarg, sizeof(svp.plname) - 1); + } else { + raw_print("Player name expected after -u\n"); + } + } +#endif + break; + case 'v': + if (argcheck(argc, argv, ARG_VERSION) == 2) { + opt_terminate(); + /*NOTREACHED*/ + } + break; + case 'w': /* windowtype: "-wfoo" or "-w[indowtype]=foo" + * or "-w[indowtype]:foo" or "-w[indowtype] foo" */ + arg = + lopt(arg, (ArgValRequired | ArgNamOneLetter | ArgErrComplain), + "-windowtype", origarg, &argc, &argv); + if (gc.cmdline_windowsys) + free((genericptr_t) gc.cmdline_windowsys); + gc.cmdline_windowsys = arg ? dupstr(arg) : NULL; + break; + #if !defined(UNIX) && !defined(VMS) + case 'D': + wizard = TRUE, discover = FALSE; + break; + case 'X': + discover = TRUE, wizard = FALSE; + break; +#endif + default: + break; + } + } + /* empty or "N errors on command line" */ + config_error_done(); + return; +} +/* for command-line options that perform some immediate action and then + terminate the program without starting play, like 'nethack --version' + or 'nethack -s Zelda'; do some cleanup before that termination */ +ATTRNORETURN staticfn void +opt_terminate(void) +{ + program_state.early_options = 0; + config_error_done(); /* free memory allocated by config_error_init() */ + + nh_terminate(EXIT_SUCCESS); + /*NOTREACHED*/ +} + +ATTRNORETURN staticfn void +opt_usage(const char *hackdir) +{ +#ifdef CHDIR + chdirx(hackdir, TRUE); +#else + nhUse(hackdir); +#endif + dlb_init(); + + genl_display_file(USAGEHELP, TRUE); + opt_terminate(); +} +/* show the sysconf file name, playground directory, run-time configuration + file name, dumplog file name if applicable, and some other things */ +ATTRNORETURN void +after_opt_showpaths(const char *dir) +{ +#ifdef CHDIR + chdirx(dir, FALSE); +#else + nhUse(dir); +#endif + opt_terminate(); + /*NOTREACHED*/ +} + +/* handle "-s [character-names]" to show all the entries + in the high scores file ('record') belonging to particular characters; + nethack will end after doing so without starting play */ +ATTRNORETURN staticfn void +scores_only(int argc, char **argv, const char *dir) +{ + /* do this now rather than waiting for final termination, in case there + is an error summary coming */ + config_error_done(); + +#ifdef CHDIR + chdirx(dir, FALSE); +#else + nhUse(dir); +#endif +#ifdef SYSCF + iflags.initoptions_noterminate = TRUE; + initoptions(); /* sysconf options affect whether panictrace is enabled */ + iflags.initoptions_noterminate = FALSE; +#endif +#ifdef PANICTRACE + ARGV0 = gh.hname; /* save for possible stack trace */ +#ifndef NO_SIGNAL + panictrace_setsignals(TRUE); +#endif +#endif +#ifdef UNIX + (void) whoami(); /* set up default plname[] */ +#endif + prscore(argc, argv); + + nh_terminate(EXIT_SUCCESS); /* bypass opt_terminate() */ + /*NOTREACHED*/ +} /* * Returns: diff --git a/sys/share/pcmain.c b/sys/share/pcmain.c index fdfc599c6..39e5aec4e 100644 --- a/sys/share/pcmain.c +++ b/sys/share/pcmain.c @@ -671,7 +671,7 @@ nhusage(void) #ifdef CHDIR void -chdirx(char *dir, boolean wr) +chdirx(const char *dir, boolean wr) { #ifdef AMIGA static char thisdir[] = ""; diff --git a/sys/unix/unixmain.c b/sys/unix/unixmain.c index 6fb14c417..65a5401c4 100644 --- a/sys/unix/unixmain.c +++ b/sys/unix/unixmain.c @@ -26,17 +26,11 @@ extern struct passwd *getpwuid(int); #endif extern struct passwd *getpwnam(const char *); #ifdef CHDIR -static void chdirx(const char *, boolean); +void chdirx(const char *, boolean); #endif /* CHDIR */ -static boolean whoami(void); -static char *lopt(char *, int, const char *, const char *, int *, char ***); +boolean whoami(void); static void process_options(int, char **); -static void consume_arg(int, int *, char ***); -static void consume_two_args(int, int *, char ***); -static void early_options(int *, char ***, char **); -ATTRNORETURN static void opt_terminate(void) NORETURN; -ATTRNORETURN static void opt_usage(const char *) NORETURN; -ATTRNORETURN static void scores_only(int, char **, const char *) NORETURN; + #ifdef SND_LIB_INTEGRATED uint32_t soundlibchoice = soundlib_nosound; #endif @@ -128,12 +122,14 @@ main(int argc, char *argv[]) if (!dir) dir = nh_getenv("HACKDIR"); #endif /* CHDIR */ + program_state.early_options = 1; #ifdef ENHANCED_SYMBOLS if (argcheck(argc, argv, ARG_DUMPGLYPHIDS) == 2) exit(EXIT_SUCCESS); #endif /* handle -dalthackdir, -s , --version, --showpaths */ early_options(&argc, &argv, &dir); + program_state.early_options = 0; #ifdef CHDIR /* * Change directories before we initialize the window system so @@ -325,93 +321,6 @@ main(int argc, char *argv[]) return 0; } -static char ArgVal_novalue[] = "[nothing]"; /* note: not 'const' */ - -enum cmdlinearg { - ArgValRequired = 0, ArgValOptional = 1, - ArgValDisallowed = 2, ArgVal_mask = (1 | 2), - ArgNamOneLetter = 4, ArgNam_mask = 4, - ArgErrSilent = 0, ArgErrComplain = 8, ArgErr_mask = 8 -}; - -/* approximate 'getopt_long()' for one option; all the comments refer to - "-windowtype" but the code isn't specific to that */ -static char * -lopt( - char *arg, /* command line token; beginning matches 'optname' */ - int lflags, /* cmdlinearg | errorhandling */ - const char *optname, /* option's name; "-windowtype" in examples below */ - const char *origarg, /* 'arg' might have had a dash prefix removed */ - int *argc_p, /* argc that can have changes passed to caller */ - char ***argv_p) /* argv[] ditto */ -{ - int argc = *argc_p; - char **argv = *argv_p; - char *p, *nextarg = (argc > 1 && argv[1][0] != '-') ? argv[1] : 0; - int l, opttype = (lflags & ArgVal_mask); - boolean oneletterok = ((lflags & ArgNam_mask) == ArgNamOneLetter), - complain = ((lflags & ArgErr_mask) == ArgErrComplain); - - /* first letter must match */ - if (arg[1] != optname[1]) { - loptbail: - if (complain) - config_error_add("Unknown option: %.60s", origarg); - return (char *) 0; - loptnotallowed: - if (complain) - config_error_add("Value not allowed: %.60s", origarg); - return (char *) 0; - loptrequired: - if (complain) - config_error_add("Missing required value: %.60s", origarg); - return (char *) 0; - } - - if ((p = strchr(arg, '=')) == 0) - p = strchr(arg, ':'); - if (p && opttype == ArgValDisallowed) - goto loptnotallowed; - - l = (int) (p ? (long) (p - arg) : (long) strlen(arg)); - if ((l > 2 || oneletterok) && !strncmp(arg, optname, l)) { - /* "-windowtype[=foo]" */ - if (p) - ++p; /* past '=' or ':' */ - else if (opttype == ArgValRequired) - p = eos(arg); /* we have "-w[indowtype]" w/o "=foo" - * so we'll take foo from next element */ - else - return ArgVal_novalue; - } else if (oneletterok) { - /* "-w..." but not "-w[indowtype[=foo]]" */ - if (!p) { - p = &arg[2]; /* past 'w' of "-wfoo" */ -#if 0 /* -x:value could work but is not supported (callers don't expect it) */ - } else if (p == arg + 2) { - ++p; /* past ':' of "-w:foo" */ -#endif - } else { - /* "-w...=foo" but not "-w[indowtype]=foo" */ - goto loptbail; - } - } else { - goto loptbail; - } - if (!p || !*p) { - /* "-w[indowtype]" w/o '='/':' if there is a next element, use - it for "foo"; if not, supply a non-Null bogus value */ - if (nextarg && (opttype == ArgValRequired - || opttype == ArgValOptional)) - p = nextarg, --(*argc_p), ++(*argv_p); - else if (opttype == ArgValRequired) - goto loptrequired; - else - p = ArgVal_novalue; /* there is no next element */ - } - return p; -} - /* caveat: argv elements might be arbitrarily long */ static void process_options(int argc, char *argv[]) @@ -573,272 +482,8 @@ process_options(int argc, char *argv[]) return; } -/* move argv[ndx] to end of argv[] array, then reduce argc to hide it; - prevents process_options() from encountering it after early_options() - has processed it; elements get reordered but all remain intact */ -static void -consume_arg(int ndx, int *ac_p, char ***av_p) -{ - char *gone, **av = *av_p; - int i, ac = *ac_p; - - /* "-one -two -three -four" -> "-two -three -four -one" */ - if (ac > 2) { - gone = av[ndx]; - for (i = ndx + 1; i < ac; ++i) - av[i - 1] = av[i]; - av[ac - 1] = gone; - } - --(*ac_p); -} - -/* consume two tokens for '-argname value' w/o '=' or ':' */ -static void -consume_two_args(int ndx, int *ac_p, char ***av_p) -{ - /* when consuming "-two arg" from "-two arg -three -four", - the *ac_p manipulation results in "-three -four -two arg" - rather than the "-three -four arg -two" that would happen - with just two ordinary consume_arg() calls */ - consume_arg(ndx, ac_p, av_p); - ++(*ac_p); /* bring the final slot back into view */ - consume_arg(ndx, ac_p, av_p); - --(*ac_p); /* take away restored slot */ -} - -/* process some command line arguments before loading options */ -static void -early_options(int *argc_p, char ***argv_p, char **hackdir_p) -{ - char **argv, *arg, *origarg; - int argc, oldargc, ndx = 0, consumed = 0; - - config_error_init(FALSE, "command line", FALSE); - - /* treat "nethack ?" as a request for usage info; due to shell - processing, player likely has to use "nethack \?" or "nethack '?'" - [won't work if used as "nethack -dpath ?" or "nethack -d path ?"] */ - if (*argc_p > 1 && !strcmp((*argv_p)[1], "?")) - opt_usage(*hackdir_p); /* doesn't return */ - - /* - * Both *argc_p and *argv_p account for the program name as (*argv_p)[0]; - * local argc and argv implicitly discard that (by starting 'ndx' at 1). - * argcheck() doesn't mind, prscore() (via scores_only()) does (for the - * number of args it gets passed, not for the value of argv[0]). - */ - for (ndx = 1; ndx < *argc_p; ndx += (consumed ? 0 : 1)) { - consumed = 0; - argc = *argc_p - ndx; - argv = *argv_p + ndx; - - arg = origarg = argv[0]; - /* skip any args intended for deferred options */ - if (*arg != '-') - continue; - /* allow second dash if arg name is longer than one character */ - if (arg[0] == '-' && arg[1] == '-' && arg[2] != '\0' - && (arg[3] != '\0' && arg[3] != '=' && arg[3] != ':')) - ++arg; - - switch (arg[1]) { /* char after leading dash */ - case 'b': -#ifdef CRASHREPORT - // --bidshow - if (argcheck(argc, argv, ARG_BIDSHOW) == 2){ - opt_terminate(); - /*NOTREACHED*/ - } -#endif - break; - case 'd': - if (argcheck(argc, argv, ARG_DEBUG) == 1) { - consume_arg(ndx, argc_p, argv_p), consumed = 1; -#ifndef NODUMPENUMS - } else if (argcheck(argc, argv, ARG_DUMPENUMS) == 2) { - opt_terminate(); - /*NOTREACHED*/ -#endif - } else if (argcheck(argc, argv, ARG_DUMPMONGEN) == 2) { - opt_terminate(); - /*NOTREACHED*/ - } else if (argcheck(argc, argv, ARG_DUMPWEIGHTS) == 2) { - opt_terminate(); - /*NOTREACHED*/ - } else { #ifdef CHDIR - oldargc = argc; - arg = lopt(arg, - (ArgValRequired | ArgNamOneLetter | ArgErrSilent), - "-directory", origarg, &argc, &argv); - if (!arg) - error("Flag -d must be followed by a directory name."); - if (*arg != 'e') { /* avoid matching -decgraphics or -debug */ - *hackdir_p = arg; - if (oldargc == argc) - consume_arg(ndx, argc_p, argv_p), consumed = 1; - else - consume_two_args(ndx, argc_p, argv_p), consumed = 2; - } -#endif /* CHDIR */ - } - break; - case 'h': - case '?': - if (lopt(arg, ArgValDisallowed, "-help", origarg, &argc, &argv) - || lopt(arg, ArgValDisallowed | ArgNamOneLetter, "-?", - origarg, &argc, &argv)) - opt_usage(*hackdir_p); /* doesn't return */ - break; - case 'n': - oldargc = argc; - if (!strcmp(arg, "-no-nethackrc")) /* no abbreviation allowed */ - arg = nhStr("/dev/null"); - else - arg = lopt(arg, (ArgValRequired | ArgErrComplain), - "-nethackrc", origarg, &argc, &argv); - if (arg) { - gc.cmdline_rcfile = dupstr(arg); - if (oldargc == argc) - consume_arg(ndx, argc_p, argv_p), consumed = 1; - else - consume_two_args(ndx, argc_p, argv_p), consumed = 2; - } - break; - case 's': - if (argcheck(argc, argv, ARG_SHOWPATHS) == 2) { - gd.deferred_showpaths = TRUE; - gd.deferred_showpaths_dir = *hackdir_p; - config_error_done(); - return; - } - /* check for "-s" request to show scores */ - if (lopt(arg, ((ArgValDisallowed | ArgErrComplain) - /* only accept one-letter if there is just one - dash; reject "--s" because prscore() via - scores_only() doesn't understand it */ - | ((origarg[1] != '-') ? ArgNamOneLetter : 0)), - /* [ought to omit val-disallowed and accept - --scores=foo since -s foo and -sfoo are - allowed, but -s form can take more than one - space-separated argument and --scores=foo - isn't suited for that] */ - "-scores", origarg, &argc, &argv)) { - /* at this point, argv[0] contains "-scores" or a leading - substring of it; prscore() (via scores_only()) expects - that to be in argv[1] so we adjust the pointer to make - that be the case; if there are any non-early args waiting - to be passed along to process_options(), the resulting - argv[0] will be one of those rather than the program - name but prscore() doesn't care */ - scores_only(argc + 1, argv - 1, *hackdir_p); - /*NOTREACHED*/ - } - break; - case 'u': - if (lopt(arg, ArgValDisallowed, "-usage", origarg, &argc, &argv)) - opt_usage(*hackdir_p); - break; - case 'v': - if (argcheck(argc, argv, ARG_VERSION) == 2) { - opt_terminate(); - /*NOTREACHED*/ - } - break; - case 'w': /* windowtype: "-wfoo" or "-w[indowtype]=foo" - * or "-w[indowtype]:foo" or "-w[indowtype] foo" */ - arg = lopt(arg, - (ArgValRequired | ArgNamOneLetter | ArgErrComplain), - "-windowtype", origarg, &argc, &argv); - if (gc.cmdline_windowsys) - free((genericptr_t) gc.cmdline_windowsys); - gc.cmdline_windowsys = arg ? dupstr(arg) : NULL; - break; - default: - break; - } - } - /* empty or "N errors on command line" */ - config_error_done(); - return; -} - -/* for command-line options that perform some immediate action and then - terminate the program without starting play, like 'nethack --version' - or 'nethack -s Zelda'; do some cleanup before that termination */ -ATTRNORETURN static void -opt_terminate(void) -{ - config_error_done(); /* free memory allocated by config_error_init() */ - - nh_terminate(EXIT_SUCCESS); - /*NOTREACHED*/ -} - -ATTRNORETURN static void -opt_usage(const char *hackdir) -{ -#ifdef CHDIR - chdirx(hackdir, TRUE); -#else - nhUse(hackdir); -#endif - dlb_init(); - - genl_display_file(USAGEHELP, TRUE); - opt_terminate(); -} - -/* show the sysconf file name, playground directory, run-time configuration - file name, dumplog file name if applicable, and some other things */ -ATTRNORETURN void -after_opt_showpaths(const char *dir) -{ -#ifdef CHDIR - chdirx(dir, FALSE); -#else - nhUse(dir); -#endif - opt_terminate(); - /*NOTREACHED*/ -} - -/* handle "-s [character-names]" to show all the entries - in the high scores file ('record') belonging to particular characters; - nethack will end after doing so without starting play */ -ATTRNORETURN static void -scores_only(int argc, char **argv, const char *dir) -{ - /* do this now rather than waiting for final termination, in case there - is an error summary coming */ - config_error_done(); - -#ifdef CHDIR - chdirx(dir, FALSE); -#else - nhUse(dir); -#endif -#ifdef SYSCF - iflags.initoptions_noterminate = TRUE; - initoptions(); /* sysconf options affect whether panictrace is enabled */ - iflags.initoptions_noterminate = FALSE; -#endif -#ifdef PANICTRACE - ARGV0 = gh.hname; /* save for possible stack trace */ -#ifndef NO_SIGNAL - panictrace_setsignals(TRUE); -#endif -#endif - (void) whoami(); /* set up default plname[] */ - - prscore(argc, argv); - - nh_terminate(EXIT_SUCCESS); /* bypass opt_terminate() */ - /*NOTREACHED*/ -} - -#ifdef CHDIR -static void +void chdirx(const char *dir, boolean wr) { if (dir /* User specified directory? */ @@ -905,7 +550,7 @@ chdirx(const char *dir, boolean wr) #endif /* CHDIR */ /* returns True iff we set plname[] to username which contains a hyphen */ -static boolean +boolean whoami(void) { /* diff --git a/sys/windows/consoletty.c b/sys/windows/consoletty.c index ed44f1271..34489430f 100644 --- a/sys/windows/consoletty.c +++ b/sys/windows/consoletty.c @@ -2590,19 +2590,19 @@ void nethack_enter_consoletty(void) /* clear the entire console buffer */ - int size = console.orig_csbi.dwSize.X * console.orig_csbi.dwSize.Y; - DWORD unused; - set_console_cursor(0, 0); - FillConsoleOutputAttribute( - console.hConOut, CONSOLE_CLEAR_ATTRIBUTE, - size, console.cursor, &unused); + //int size = console.orig_csbi.dwSize.X * console.orig_csbi.dwSize.Y; + //DWORD unused; + //set_console_cursor(0, 0); + // FillConsoleOutputAttribute( + // console.hConOut, CONSOLE_CLEAR_ATTRIBUTE, + // size, console.cursor, &unused); - FillConsoleOutputCharacter( - console.hConOut, CONSOLE_CLEAR_CHARACTER, - size, console.cursor, &unused); + // FillConsoleOutputCharacter( + // console.hConOut, CONSOLE_CLEAR_CHARACTER, + // size, console.cursor, &unused); - set_console_cursor(1, 0); - SetConsoleCursorPosition(console.hConOut, console.cursor); + //set_console_cursor(1, 0); + //SetConsoleCursorPosition(console.hConOut, console.cursor); /* At this point early_raw_print will work */ @@ -2757,7 +2757,7 @@ VA_DECL(const char *, fmt) VA_START(fmt); VA_INIT(fmt, const char *); (void) vsnprintf(buf, sizeof buf, fmt, VA_ARGS); - if (redirect_stdout) + if (redirect_stdout || program_state.early_options) fprintf(stdout, "%s", buf); else { #ifdef TTY_GRAPHICS diff --git a/sys/windows/windmain.c b/sys/windows/windmain.c index a2fe2da5e..9e34152ac 100644 --- a/sys/windows/windmain.c +++ b/sys/windows/windmain.c @@ -19,7 +19,6 @@ #endif static void nhusage(void); -static void early_options(int argc, char **argv); char *exename(void); boolean fakeconsole(void); void freefakeconsole(void); @@ -157,6 +156,7 @@ MAIN(int argc, char *argv[]) HWND hwnd; HDC hdc; int bpp; + char *dir = NULL; #ifdef _MSC_VER _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); @@ -167,12 +167,10 @@ MAIN(int argc, char *argv[]) * Get a set of valid safe windowport function * pointers during early startup initialization. */ - safe_routines(); +// safe_routines(); #endif /* WIN32CON */ -#ifndef MSWIN_GRAPHICS - early_init(argc, argv); /* already in WinMain for MSWIN_GRAPHICS */ -#endif + /* setting iflags.colorcount has to be after early_init() * because it zeros out all of iflags */ hwnd = GetDesktopWindow(); @@ -207,6 +205,35 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ #endif gh.hname = "NetHack"; /* used for syntax messages */ +#ifndef MSWIN_GRAPHICS + early_init(argc, argv); /* already in WinMain for MSWIN_GRAPHICS */ +#endif + set_default_prefix_locations(argv[0]); /* must be re-done after initoptions_init() + * which clears out gp.fqn_prefix[] */ + copy_sysconf_content(); + copy_symbols_content(); + /* Now that sysconf has had a chance to set the TROUBLEPREFIX, don't + allow it to be changed from here on out. */ + fqn_prefix_locked[TROUBLEPREFIX] = TRUE; + copy_config_content(); + + // if (iflags.windowtype_deferred && gc.chosen_windowtype[0]) + // windowtype = gc.chosen_windowtype; + // windowtype = gc.chosen_windowtype; + +#if !defined(MSWIN_GRAPHICS) + consoletty_open(1); + nethack_enter_consoletty(); +#endif + + if (!windowtype) { +#ifdef MSWIN_GRAPHICS + windowtype = "mswin"; +#else + windowtype = "tty"; +#endif + } + choose_windows(windowtype); /* sets all the window port function pointers */ #if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS) /* Save current directory and make sure it gets restored when @@ -216,7 +243,14 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ error("NetHack: current directory path too long"); #endif initoptions_init(); // This allows OPTIONS in syscf on Windows. - set_default_prefix_locations(argv[0]); + set_default_prefix_locations(argv[0]); /* must be re-done after initoptions_init() + * which clears out gp.fqn_prefix[] */ + iflags.windowtype_deferred = TRUE; + + program_state.early_options = 1; + early_options(&argc, &argv, &dir); + program_state.early_options = 0; + initoptions(); #if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS) chdir(gf.fqn_prefix[HACKPREFIX]); @@ -226,18 +260,6 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ getreturn_enabled = TRUE; check_recordfile((char *) 0); - iflags.windowtype_deferred = TRUE; - copy_sysconf_content(); - copy_symbols_content(); - early_options(argc, argv); - initoptions(); - - /* Now that sysconf has had a chance to set the TROUBLEPREFIX, don't - allow it to be changed from here on out. */ - fqn_prefix_locked[TROUBLEPREFIX] = TRUE; - - copy_config_content(); - /* did something earlier flag a need to exit without starting a game? */ if (windows_startup_state > 0) { raw_printf("Exiting."); @@ -287,10 +309,10 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ Strcpy(default_window_sys, "curses"); #endif /* CURSES */ #endif /* TTY */ - if (iflags.windowtype_deferred && gc.chosen_windowtype[0]) - windowtype = gc.chosen_windowtype; + // if (iflags.windowtype_deferred && gc.chosen_windowtype[0]) + // windowtype = gc.chosen_windowtype; } - choose_windows(windowtype); + // choose_windows(windowtype); #if defined(SND_LIB_FMOD) assign_soundlib(soundlib_fmod); #elif defined(SND_LIB_WINDSOUND) @@ -451,6 +473,7 @@ attempt_restore: RESTORE_WARNING_UNREACHABLE_CODE +#if 0 static void early_options(int argc, char *argv[]) { @@ -671,6 +694,7 @@ nhusage(void) (void) printf("%s\n", buf1); #undef ADD_USAGE } +#endif /* 0 */ /* copy file if destination does not exist */ void @@ -1297,4 +1321,23 @@ other_self_recover_prompt(void) } return retval; } + +#ifdef CHDIR +void +chdirx(const char *dir, boolean wr) +{ + static char thisdir[] = "."; + + if (dir && chdir(dir) < 0) { + error("Cannot chdir to %s.", dir); + } + + /* warn the player if we can't write the record file */ + /* perhaps we should also test whether . is writable */ + /* unfortunately the access system-call is worthless */ + if (wr) + check_recordfile(dir ? dir : thisdir); +} +#endif /* CHDIR */ + /*windmain.c*/