+ If you feel "stpcpy()" to be non-ANSI, use this: ;-))
  char *stpcpy(char *s1, const char *s2)
  { while((*s1++ = *s2++) != '\0');
    return s1 - 1;
  }
  Then you add a feature:
  HAVE_STPCPY to exclude this stpcpy(), if the particular compiler
  does have stpcpy(), because the built-in stpcpy() can be better
  optimized

+ messages.c
++ [[left-over from optimizing the access?!]]
  string_index is created dynamically, which is not necessary.
  This will require definitely more space in the code segment
  than you save in the data segment.

++ Shouldn't you test the final "fread()" if it succeeded?

++ The messages to emit on missing "MZ" or "FreeDOS"
  are not really meaningful for non-programmers, are they?
  From point of view of a native user these message are more
  confusing than helpful, because there is nothing the user is
  actually informed what he can do.
  How about a generic message, ala: "%s is not the FreeDOS COMMAND.COM"
  or something like that? This gives not a hint _why_ this file
  is not valid, but might inform the user _what_ he can do about it.

++ suggestion
  If you split the current display_string() function into
  display(): Fetch the string into heap, display via vprintf(),
    free() the string
  fetch(): implements the "fetch string" part

  Then you can store _any_ strings that you show to the user or
  compare with strings entered by the user; just fetch() them
  early (e.g. in init()) and store them during the full life-time
  of the program, or when you need them. {e.g. D_ON, D_OFF}.

++ The declaration of fdid[] is currently independed on ID_STRING,
  I'll check for ANSI compliance, but you should be able to use
  char fdid[sizeof(ID_STRING)], because ID_STRING itself is
  constant.

+ init.c
++ comPath = _fullpath() -and- ComDir
  1) dfnexpand() is a replacement for _fullpath() and already used
    within COMMAND
  2) How about this:
    2.1) Don't duplicate comPath into ComDir; spare the modification
    for ComDir until behind chgEnv(); then you can simply use
    the comPath string.
    2.2) Don't define ComDir as "path to command.com" but to contain
    path and name, but delimited by a single '\0'. If you need
    the complete name, you temporarily replace the '\0' by an '\\'.
    This way the user can rename COMMAND.COM to something different
    and all access to it will work (string handling only?).

    If you need the "path to COMMAND" completed with a filename
    more often, how about a function for this, e.g.:
    char *compath(char *fnam)
    { static char *last = NULL;
      char *p;

      free(last);
      if(!fnam) {   /* get absolute path to COMMAND */
        *(p = strchr(ComDir, '\0')) = '\\';
        last = strdup(ComDir);
        *p = '\0';
      }
      else {
        last = malloc(strlen(ComDir) + strlen(fnam) + 2);
        if(last)
          strcpy(stpcpy(stpcpy(last, ComDir), "\\"), fnam);
      }

      if(!last)
        nomem_error();

      return last;
    }
    This will automatically free the last returned string and
    issue an error, if needed.
    Depending on how and under which circumstances this function
    is called, one could return a legal string, e.g. "" if
    last == NULL, in order to be able to directly use the return
    value of this function.

    [[Would be useful for constructs like this:
      if(exist(startupscript = compath("COMSTART.BAT")))
        parsecommandline(startupscript);
    but would not do any good if most of the useages would also
    issue error messages if the action itself fails, e.g.:
      if((stringsfile = fopen(compath(NULL), "rb")) == NULL)
        puts("Cannot open STRINGS file");
    Maybe in this particular case the double error might be
    useful ;)
    ]]
  3) I don't like the aliasload() feature. This can clearly be
    put into AUTOEXEC.BAT or something I'll come up with later.


Suggestion:

+ startup / initialization
++ 4dos do contain a rather nice feature (for me), that will esp. come
  handy if COMMAND can modify internally settings by a command:
  Each time a _secondary_ shell (the one without a /p switch)
  is started, a startup script is called; each time it terminates,
  a shutdown script is executed.

  The primary shell does not call the startup script, because it
  calls AUTOEXEC.BAT, which can execute the startup script if needed.

  To do so:
  The startup script must be called in the "else" branch of
  "if(!canexit)" (== secondary shell).
  The shutdown script witin main() before "return 0;" and after
  the exit flag has been cleared.

