From: PatR Date: Fri, 18 Feb 2022 22:38:24 +0000 (-0800) Subject: Unix command line parsing X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=45bc2dafa930f12a0eea6a49a93e2d8cee3d74b1;p=nethack Unix command line parsing Move a bunch of stuff out of main() into new early_options(): '-dpath' playground directory handling, '-s ...' show scores instead of playing, and the 'argcheck()' options: --version, --showpaths, --dumpenums, and --debug (not to be confused with -D). Also introduce | --nethackrc=filename | --no-nethackrc to control RC file without using NETHACKOPTIONS so that that is still available for setting other options. They can start with either one or two dashes. --no-nethackrc is just --nethackrc=/dev/null under the hood. '-dpath' can now be '--directory=path' or '--directory path' but the old syntax should still work. '-s ...' can be '--scores ...'. Basic call sequence in unixmain relating to options is now |main() { | early_options(argc, argv[]); | initoptions(); /* process sysconf, .nethackrc, NETHACKOPTIONS */ | process_options(possibly_modified_argc, possibly_modified_argv[]); |} Options processed by early_options() that don't terminate the program are moved to the end of argv[], with argc reduced accordingly. Then process_options() only sees the ones that early_options() declines to handle. Most early options were using plain exit() instead of nh_terminate() so not performing any nethack-specific cleanup. However, since they run before the game starts, there wasn't much cleanup being overlooked. chdirx() takes a boolean as second argument but all its callers were passing int (with value of 1 or 0, so it still worked after being implicitly fixed by prototype). Change them to pass TRUE or FALSE. argcheck() was refusing (argc,argv[]) with count of 1 but then it was checking 0..N-1 rather than 1..N-1, so it tested whether argv[0] was an argument instead of skipping that as the program name. Change to allow count of 1 with modified argv that has an option name in argv[0]. That happens to fit well with how early_options() wanted to use it. --- diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 1d692a7f8..fdff91e23 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1,4 +1,4 @@ -NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.788 $ $NHDT-Date: 1644610217 2022/02/11 20:10:17 $ +NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.799 $ $NHDT-Date: 1645223893 2022/02/18 22:38:13 $ General Fixes and Modified Features ----------------------------------- @@ -1232,6 +1232,7 @@ Unix: add "ec2-user" to the list of user names 'sysconf' classifies as generic Unix: work-around a build issue in ubuntu 21.10 by using ifdef to skip the define of warn_unused_result to empty string in tradstdc.h whenever __linux__ is defined during build unless GCC_URWARN is also defined +Unix: re-do command line parsing user_sounds: move the message hook from inside individual window display ports to the core where it allows MSGTYP_NOSHOW msgtyp's to still trigger sounds to correct a reported github issue; also fixes a past reported @@ -1470,6 +1471,9 @@ tty: if a message is marked urgent, override message suppression initiated by user having typed ESC at previous --More-- prompt Unix: can define NOSUSPEND in config.h or src/Makefile's CFLAGS to prevent unixconf.h from enabling SUSPEND without need to modify unixconf.h +Unix: support --nethackrc=filename on the command line; same effect as + NETHACKOPTIONS='@filename' but leaves NETHACKOPTIONS available for + specifying options; --no-nethackrc is same as --nethackrc=/dev/null X11: implement 'selectsaved', restore via menu of saved games X11: echo getline prompt and response (wishes, applying names) to message window and dumplog message history diff --git a/src/allmain.c b/src/allmain.c index 7fcf18b19..7e8839799 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 allmain.c $NHDT-Date: 1644517022 2022/02/10 18:17:02 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.174 $ */ +/* NetHack 3.7 allmain.c $NHDT-Date: 1645223894 2022/02/18 22:38:14 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.177 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -845,7 +845,6 @@ extern int windows_early_options(const char *); * 1 = found and skip past this argument * 2 = found and trigger immediate exit */ - int argcheck(int argc, char *argv[], enum earlyarg e_arg) { @@ -858,7 +857,7 @@ argcheck(int argc, char *argv[], enum earlyarg e_arg) if (earlyopts[idx].e == e_arg) break; } - if ((idx >= SIZE(earlyopts)) || (argc <= 1)) + if (idx >= SIZE(earlyopts) || argc < 1) return FALSE; for (i = 0; i < argc; ++i) { diff --git a/sys/unix/unixmain.c b/sys/unix/unixmain.c index 8ba732129..c62254060 100644 --- a/sys/unix/unixmain.c +++ b/sys/unix/unixmain.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 unixmain.c $NHDT-Date: 1644866265 2022/02/14 19:17:45 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.96 $ */ +/* NetHack 3.7 unixmain.c $NHDT-Date: 1645223897 2022/02/18 22:38:17 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.97 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -30,7 +30,14 @@ extern struct passwd *getpwnam(const char *); static void chdirx(const char *, boolean); #endif /* CHDIR */ static boolean whoami(void); +static char *lopt(char *, int, const char *, const char *, int *, char ***); 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 **); +static void opt_terminate(void) NORETURN; +static void opt_showpaths(const char *); +static void scores_only(int, char **, const char *) NORETURN; #ifdef _M_UNIX extern void check_sco_console(void); @@ -48,9 +55,7 @@ static struct passwd *get_unix_pw(void); int main(int argc, char *argv[]) { -#ifdef CHDIR - char *dir; -#endif + char *dir = NULL; NHFILE *nhfp; boolean exact_username; boolean resuming = FALSE; /* assume new game */ @@ -108,89 +113,23 @@ main(int argc, char *argv[]) if (!dir) dir = nh_getenv("HACKDIR"); #endif /* CHDIR */ + /* handle -dalthackdir, -s , --version, --showpaths */ + early_options(&argc, &argv, &dir); - if (argc > 1) { - if (argcheck(argc, argv, ARG_VERSION) == 2) - exit(EXIT_SUCCESS); - -#ifndef NODUMPENUMS - if (argcheck(argc, argv, ARG_DUMPENUMS) == 2) - exit(EXIT_SUCCESS); -#endif - if (argcheck(argc, argv, ARG_SHOWPATHS) == 2) { -#ifdef CHDIR - chdirx((char *) 0, 0); -#endif - iflags.initoptions_noterminate = TRUE; - initoptions(); - iflags.initoptions_noterminate = FALSE; - reveal_paths(); - exit(EXIT_SUCCESS); - } - if (argcheck(argc, argv, ARG_DEBUG) == 1) { - argc--; - argv++; - } -#ifdef CHDIR - if (argc > 1 && !strncmp(argv[1], "-d", 2) && argv[1][2] != 'e') { - /* avoid matching "-dec" for DECgraphics; since the man page - * says -d directory, hope nobody's using -desomething_else - */ - argc--; - argv++; - dir = argv[0] + 2; - if (*dir == '=' || *dir == ':') - dir++; - if (!*dir && argc > 1) { - argc--; - argv++; - dir = argv[0]; - } - if (!*dir) - error("Flag -d must be followed by a directory name."); - } - } -#endif /* CHDIR */ - - if (argc > 1) { - /* - * Now we know the directory containing 'record' and - * may do a prscore(). Exclude `-style' - it's a Qt option. - */ - if (!strncmp(argv[1], "-s", 2) && strncmp(argv[1], "-style", 6)) { #ifdef CHDIR - chdirx(dir, 0); -#endif -#ifdef SYSCF - initoptions(); -#endif -#ifdef PANICTRACE - ARGV0 = g.hname; /* save for possible stack trace */ -#ifndef NO_SIGNAL - panictrace_setsignals(TRUE); -#endif -#endif - prscore(argc, argv); - /* FIXME: shouldn't this be using nh_terminate() to free - up any memory allocated by initoptions() */ - exit(EXIT_SUCCESS); - } - } /* argc > 1 */ - -/* - * Change directories before we initialize the window system so - * we can find the tile file. - */ -#ifdef CHDIR - chdirx(dir, 1); + /* + * Change directories before we initialize the window system so + * we can find the tile file. + */ + chdirx(dir, TRUE); #endif - #ifdef _M_UNIX check_sco_console(); #endif #ifdef __linux__ check_linux_console(); #endif + initoptions(); #ifdef PANICTRACE ARGV0 = g.hname; /* save for possible stack trace */ @@ -346,12 +285,94 @@ 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 = index(arg, '=')) == 0) + p = index(arg, ':'); + if (p && opttype == ArgValDisallowed) + goto loptnotallowed; + + l = (int) (p ? (p - arg) : strlen(arg)); + if (!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" */ + } 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 arbitrary long */ static void process_options(int argc, char *argv[]) { - static char novalue[] = "[nothing]"; /* note: not 'const' */ - char *p, *arg, *origarg; + char *arg, *origarg; int i, l; config_error_init(FALSE, "command line", FALSE); @@ -373,9 +394,10 @@ process_options(int argc, char *argv[]) && (arg[3] != '\0' && arg[3] != '=' && arg[3] != ':')) ++arg; l = (int) strlen(arg); - /* must supply at least 4 chars to match "-XXXgraphics" */ - if (l < 4) - l = 4; + if (l < 6 && !strncmp(arg, "-no-", 4)) + l = 6; + else if (l < 4) + l = 4; /* must supply at least 4 chars to match "-XXXgraphics" */ switch (arg[1]) { case 'D': @@ -392,11 +414,18 @@ process_options(int argc, char *argv[]) case 'X': discover = TRUE, wizard = FALSE; break; -#ifdef NEWS case 'n': - iflags.news = FALSE; - break; +#ifdef NEWS + if (!arg[2] || !strcmp(arg, "-no-news")) { + iflags.news = FALSE; + break; + } else if (!strcmp(arg, "-news")) { + /* in case RC has !news, allow 'nethack -news' to override */ + iflags.news = TRUE; + break; + } #endif + break; case 'u': if (arg[2]) { (void) strncpy(g.plname, arg + 2, sizeof g.plname - 1); @@ -407,7 +436,7 @@ process_options(int argc, char *argv[]) (void) strncpy(g.plname, argv[0], sizeof g.plname - 1); g.plnamelen = 0; } else { - config_error_add("Player name expected after -u"); + config_error_add("Character name expected after -u"); } break; case 'I': @@ -444,34 +473,11 @@ process_options(int argc, char *argv[]) break; case 'w': /* windowtype: "-wfoo" or "-w[indowtype]=foo" * or "-w[indowtype]:foo" or "-w[indowtype] foo" */ - if ((p = index(arg, '=')) == 0) - p = index(arg, ':'); - l = (int) (p ? (p - arg) : strlen(arg)); - if (!strncmp(arg, "-windowtype", l)) { - /* "-windowtype[=foo]" */ - if (p) - ++p; /* past '=' or ':' */ - else - p = eos(arg); /* we have "-w[indowtype]" w/o "=foo" - * so we'll take foo from next element */ - } else { - /* "-w..." but not "-w[indowtype[=foo]]" */ - if (!p) { - p = &arg[2]; /* past 'w' of "-wfoo" */ - } else { - /* "-w...=foo" but not "-w[indowtype]=foo" */ - config_error_add("Unknown option: %.60s", origarg); - continue; - } - } - if (!*p) { - /* "-w[indowtype]" w/o '='/':' use next element for "foo" */ - if (argc > 1) - --argc, ++argv, p = argv[0]; - else - p = novalue; /* there is no next element */ - } - choose_windows(p); + arg = lopt(arg, + (ArgValRequired | ArgNamOneLetter | ArgErrComplain), + "-windowtype", origarg, &argc, &argv); + if (arg) + choose_windows(arg); break; case '@': flags.randomall = 1; @@ -492,14 +498,22 @@ process_options(int argc, char *argv[]) } } + if (argc > 1) { + int mxplyrs = atoi(argv[1]); + boolean mx_ok = mxplyrs != 0; #ifdef SYSCF - if (argc > 1) - config_error_add("MAXPLAYERS are set in sysconf file.\n"); + config_error_add("%s%s%s", + mx_ok ? "MAXPLAYERS are set in sysconf file" + : "Expected MAXPLAYERS, found \"", + mx_ok ? "" : argv[1], mx_ok ? "" : "\""); #else - /* XXX This is deprecated in favor of SYSCF with MAXPLAYERS */ - if (argc > 1) - g.locknum = atoi(argv[1]); + /* XXX This is deprecated in favor of SYSCF with MAXPLAYERS */ + if (mx_ok) + g.locknum = mxplyrs; + else + config_error_add("Invalid MAXPLATERS \"%s\"", argv[1]); #endif + } #ifdef MAX_NR_OF_PLAYERS /* limit to compile-time limit */ if (!g.locknum || g.locknum > MAX_NR_OF_PLAYERS) @@ -512,6 +526,207 @@ process_options(int argc, char *argv[]) #endif /* empty or "N errors on command line" */ config_error_done(); + 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); + + /* + * Both *argc_p and *argv_p account for the program name as (*argv_p)[0]; + * local argc and argv impicitly discard that (by starting 'ndx' at 1). + * argcheck() doesn't mind, prscore() (via scores_only()) does. + */ + 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 '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 { +#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 '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) { + g.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) { + opt_showpaths(*hackdir_p); + opt_terminate(); + /*NOTREACHED*/ + } + /* check for "-s" request to show scores */ + if (lopt(arg, + (ArgValDisallowed | ArgNamOneLetter | ArgErrComplain), + "-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 'v': + if (argcheck(argc, argv, ARG_VERSION) == 2) { + opt_terminate(); + /*NOTREACHED*/ + } + 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 */ +static void +opt_terminate(void) +{ + config_error_done(); /* free memory allocated by config_error_init() */ + + nh_terminate(EXIT_SUCCESS); + /*NOTREACHED*/ +} + +/* show the sysconf file name, playground directory, run-time configuration + file name, dumplog file name if applicable, and some other things */ +static void +opt_showpaths(const char *dir) +{ +#ifdef CHDIR + chdirx(dir, FALSE); +#else + nhUse(dir); +#endif + iflags.initoptions_noterminate = TRUE; + initoptions(); + iflags.initoptions_noterminate = FALSE; + reveal_paths(); +} + +/* 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 */ +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 = g.hname; /* save for possible stack trace */ +#ifndef NO_SIGNAL + panictrace_setsignals(TRUE); +#endif +#endif + + prscore(argc, argv); + + nh_terminate(EXIT_SUCCESS); /* bypass opt_terminate() */ + /*NOTREACHED*/ } #ifdef CHDIR @@ -535,24 +750,26 @@ chdirx(const char *dir, boolean wr) #ifdef VAR_PLAYGROUND int len = strlen(VAR_PLAYGROUND); + /* FIXME: this allocation never gets freed. + */ g.fqn_prefix[SCOREPREFIX] = (char *) alloc(len + 2); Strcpy(g.fqn_prefix[SCOREPREFIX], VAR_PLAYGROUND); if (g.fqn_prefix[SCOREPREFIX][len - 1] != '/') { g.fqn_prefix[SCOREPREFIX][len] = '/'; g.fqn_prefix[SCOREPREFIX][len + 1] = '\0'; } - #endif } #ifdef HACKDIR - if (dir == (const char *) 0) + if (!dir) dir = HACKDIR; #endif if (dir && chdir(dir) < 0) { perror(dir); error("Cannot chdir to %s.", dir); + /*NOTREACHED*/ } /* warn the player if we can't write the record file @@ -561,6 +778,10 @@ chdirx(const char *dir, boolean wr) */ if (wr) { #ifdef VAR_PLAYGROUND + /* FIXME: if termination cleanup ever frees fqn_prefix[0..N-1], + * these will need to use dupstr() so that they have distinct + * values that can be freed separately. + */ g.fqn_prefix[LEVELPREFIX] = g.fqn_prefix[SCOREPREFIX]; g.fqn_prefix[SAVEPREFIX] = g.fqn_prefix[SCOREPREFIX]; g.fqn_prefix[BONESPREFIX] = g.fqn_prefix[SCOREPREFIX]; @@ -569,6 +790,7 @@ chdirx(const char *dir, boolean wr) #endif check_recordfile(dir); } + return; } #endif /* CHDIR */