Files com076a/28.com and com076b/28.com are identical
Files com076a/50.com and com076b/50.com are identical
diff --new-file --recursive --ignore-space-change -U 5 --report-identical-files com076a/alias.c com076b/alias.c
--- com076a/alias.c	Mon Feb  1 21:31:12 1999
+++ com076b/alias.c	Wed Mar 24 17:16:08 1999
@@ -38,10 +38,13 @@
  * 28-Jan-1999  John P. Price
  * - Changed noalias character from a dot to an asterisk (*).
  *
  * 1/Feb/1999 Rob Linwood
  * - added code to allow alias commands to contain extra whitespace
+ *
+ * 24-Mar-1999  John P. Price
+ * - fixed bug in aliasexpand
  */
 
 #include "config.h"
 
 #ifdef FEATURE_ALIASES
@@ -304,10 +307,11 @@
         m = strlen(ptr->subst);
         if (strlen(cmd) - len + m - n > maxlen)
         {
           error_command_too_long();
           cmd[0] = '\0';        /* the parser won't cause any problems with an empty line */
+          return;  /*JPP 19990324*/
         }
         else
         {
           memmove(&cmd[m], &cmd[n + len], strlen(&cmd[n + len]) + 1);
           for (i = 0; i < m; i++)
Files com076a/alias.h and com076b/alias.h are identical
diff --new-file --recursive --ignore-space-change -U 5 --report-identical-files com076a/batch.c com076b/batch.c
--- com076a/batch.c	Fri Jan 22 17:32:40 1999
+++ com076b/batch.c	Wed Mar 24 16:10:20 1999
@@ -289,12 +289,12 @@
   {
     error_out_of_memory();
     return 1;
   }
 
-  dprintf(("batch ('%s', '%s', '%s')  bf = %x\n", fullname, firstword,
-           param, bf));
+  dprintf(("batch ('%s', '%s', '%s')\n", fullname, firstword,
+           param));
 
 /* HBP_002 { FOR command support */
 
   while (bc && bc->forvar)      /* Kill any and all FOR contexts */
     exit_batch();
Files com076a/batch.h and com076b/batch.h are identical
Files com076a/beep.c and com076b/beep.c are identical
Files com076a/break.c and com076b/break.c are identical
Files com076a/build.bat and com076b/build.bat are identical
Files com076a/call.c and com076b/call.c are identical
Files com076a/cb_catch.asm and com076b/cb_catch.asm are identical
Files com076a/clean.bat and com076b/clean.bat are identical
Files com076a/cls.c and com076b/cls.c are identical
Files com076a/cmdinput.c and com076b/cmdinput.c are identical
Files com076a/cmdtable.c and com076b/cmdtable.c are identical
diff --new-file --recursive --ignore-space-change -U 5 --report-identical-files com076a/command.c com076b/command.c
--- com076a/command.c	Thu Feb 11 00:56:48 1999
+++ com076b/command.c	Wed Mar 24 17:35:48 1999
@@ -129,10 +129,14 @@
  * - changed the spawnl call back to using the exec function.  Now that we
  *   are handling the environment correctly, this seems to work better.  It
  *   passes new environment variables (even ones that were created by this
  *   copy of command.com) to the child process.  This was suggested by ska.
  *
+ * 24-Mar-1999 (John P Price <linux-guru@gcfl.net>)
+ * - added support for swapable exec.
+ * - changed the way we run autoexec.bat or a batch file on the command line.
+ *
  */
 
 #include "config.h"
 
 #include <stdio.h>
@@ -153,10 +157,13 @@
 #include "batch.h"
 #include "strings.h"
 
 #include "openf.h"
 
+#ifdef FEATURE_SWAP_EXEC
+#include "swapexec.h"
+#endif
 extern struct CMD cmds[];       /* The internal command table */
 
 int exitflag = 0;               /* indicates EXIT was typed */
 int canexit = 1;                /* indicates if this shell is exitable */
 int ctrlBreak = 0;              /* Ctrl-Break or Ctrl-C hit */
@@ -238,12 +245,15 @@
   {
     int result;
 
     dprintf(("[EXEC: %s %s]\n", fullname, rest));
 //    result = spawnl(P_WAIT, fullname, fullname, rest, NULL);
+#ifdef FEATURE_SWAP_EXEC
+    result = do_exec(fullname, rest, USE_ALL, 0xFFFF, environ);
+#else
     result = exec(fullname, rest, 0);
-
+#endif
     if (result == -1)
       perror("executing spawnl function");
     else
       errorlevel = result;
   }
@@ -267,11 +277,11 @@
 
   int cl;
 
   struct CMD *cmdptr;
 
-  if ((com = malloc(MAX_INTERNAL_COMMAND_SIZE + 1)) == NULL)
+  if ((com = malloc(strlen(line) + 1)) == NULL)
   {
     error_out_of_memory();
     return;
   }
 
@@ -361,16 +371,17 @@
   int of_attrib = O_CREAT | O_TRUNC | O_TEXT | O_WRONLY;
   int num;
   int oldinfd = -1;
   int oldoutfd = -1;
 
-  dprintf(("[parsecommandline ('%s')]\n", s));
+  dprintf(("[parsecommandline (%s)]\n", s));
 
   /* first thing we do is alias expansion */
 
 #ifdef FEATURE_ALIASES
   aliasexpand(s, MAX_INTERNAL_COMMAND_SIZE);
+  dprintf(("[alias expanded to (%s)]\n", s));
 #endif
 
   if (tracemode)
   {                             /* Question after the variables expansion
                                    and make sure _all_ executed commands will honor the
@@ -468,60 +479,68 @@
     close(oldoutfd);
   }
 
   killtmpfn(fname1);
   killtmpfn(fname0);
+
   if (out)
     free(out);
+
   if (in)
     free(in);
 }
 
 /*
  * do the prompt/input/process loop
  *
  */
 
-static int process_input(int xflag)
+static int process_input(int xflag, char *commandline)
 {
-  char *commandline;
+/* If xflag == 1, then finish processing any batch file, then exit.
+   This is used for the /c flag.
+ */
+
   char *readline;
   char *evar;
   char *tp;
   char *ip;
   char *cp;
 
   /* JPP 19980807 - changed name so not to conflict with echo global */
-  int echothisline;
+  int echothisline = 0;
 
-  if ((commandline = malloc(MAX_INTERNAL_COMMAND_SIZE + 1)) == NULL)
+  do
   {
-    error_out_of_memory();
-    return 1;
-  }
-
   if ((readline = malloc(MAX_INTERNAL_COMMAND_SIZE + 1)) == NULL)
   {
-    free(commandline);
     error_out_of_memory();
     return 1;
   }
 
-  do
+    if (*commandline == '\0')
   {
     if (NULL == (ip = readbatchline(&echothisline, readline,
                                     MAX_INTERNAL_COMMAND_SIZE)))  /* if no batch input then... */
     {
       if (xflag)
+        {
+          free(readline);
         break;                  // out of the do loop
+        }
 
       readcommand(readline, MAX_INTERNAL_COMMAND_SIZE);
       tracemode = 0;            //reset trace mode
 
       ip = readline;
       echothisline = 0;
     }
+    }
+    else
+    {
+      strcpy(readline, commandline);
+    }
 
     cp = commandline;
     while (*ip)
     {
       if (*ip == '%')
@@ -567,20 +586,21 @@
 
               ip = tp + 1;
             }
             break;
         }
-
         continue;
       }
 
       if (iscntrl(*ip))
         *ip = ' ';
 
       *cp++ = *ip++;
     }
 
+    free(readline);
+
     *cp = '\0';
 
     while ((--cp >= commandline) && isspace(*cp)) /* strip trailing spaces */
       ;
 
@@ -591,57 +611,43 @@
     {
       printprompt();
       puts(commandline);
     }
 
-#if 0
-    if (tracemode)
-    {
-      printf("%s [Enter=Y,Esc=N]? ", commandline);
-      do
-      {
-        ch = toupper(getch());
-        if ((ch == 0x1B) || (ch == 'N'))  //pressed ESC or N?
-
-        {
-          puts("N");
-          *commandline = 0;     // erase command line to skip line
-
-        }
-        else if ((ch == '\n') || (ch == 'Y')) //pressed ENTER or Y?
-
-        {
-          puts("Y");
-        }
-        else
-          ch = 0;
-      }
-      while (ch == 0);
-    }
-#endif
-
     if (*commandline)
     {
       parsecommandline(commandline);
       if (echothisline || echo)
         putchar('\n');          /* JPP 19980807 */
+      *commandline = '\0';
     }
   }
   while (!canexit || !exitflag);
 
-  free(readline);
-  free(commandline);
   return 0;
 }
 
 int main(int argc, char *argv[])
 {
+  char *commandline;
+  int xflag;
+
   /*
    * * main function
    */
 
+  if ((commandline = malloc(MAX_INTERNAL_COMMAND_SIZE + 1)) == NULL)
+  {
+    error_out_of_memory();
+    return 1;
+  }
+
+  *commandline = '\0';
   /* check switches on command-line */
+
   /*JPP 19981201 if /c switch, then execute command and get out.
      initialize returns 1 if /c, else returns 0 */
-  process_input(initialize(argc, argv));
+  xflag = initialize(argc, argv, commandline);
+  process_input(xflag, commandline);
+  free(commandline);
   return 0;
 }
Binary files com076a/command.com and com076b/command.com differ
Binary files com076a/command.dsk and com076b/command.dsk differ
diff --new-file --recursive --ignore-space-change -U 5 --report-identical-files com076a/command.h com076b/command.h
--- com076a/command.h	Wed Feb 10 20:31:04 1999
+++ com076b/command.h	Thu Mar 18 22:49:28 1999
@@ -56,11 +56,11 @@
 char is_delim(char);
 void execute(char *, char *);
 void command(char *);
 void parsecommandline(char *);
 int c_brk(void);
-int initialize(int, char *[]);
+int initialize(int, char *[], char *);
 void short_version(void);
 
 /* prototypes for INTERNAL.C */
 /* int set_env(char *s);  no longer necessary - 1998/10/12 ska */
 int cmd_break(char *);
@@ -221,7 +221,12 @@
 
 /* functions to handle external strings */
 void display_string(unsigned id,...);
 int loadMsgs(void);
 void uninitModuleStrings(void);
+char *fetchString(unsigned id);
+char *getMessage(unsigned id);
 
 int showcmds(char *);
+
+/* parse numbers */
+char *parsenum(char *s, int maxCnt, int *cnt, int nums[]);
diff --new-file --recursive --ignore-space-change -U 5 --report-identical-files com076a/command.lsm com076b/command.lsm
--- com076a/command.lsm	Thu Feb 11 00:50:00 1999
+++ com076b/command.lsm	Wed Mar 24 15:46:24 1999
@@ -1,14 +1,14 @@
 Begin3
-Title:          command
-Version:        0.76a
-Entered-date:   01 Feb 1999
+Title:          FreeCom
+Version:        0.76b
+Entered-date:   24 Mar 1999
 Description:    The FreeDOS Command Shell
-Keywords:       freedos command shell
-Author:         linux-guru@gcfl.net
+Keywords:       freecom freedos command shell
+Author:         command@gcfl.net (developers)
 Maintained-by:  linux-guru@gcfl.net
 Primary-site:   http://www.gcfl.net/FreeDOS/command.com/
 Alternate-site: www.freedos.org
 Original-site:  http://www.gcfl.net/FreeDOS/command.com/
-Platforms:      dos
+Platforms:      dos dosemu
 Copying-policy: GPL
 End
diff --new-file --recursive --ignore-space-change -U 5 --report-identical-files com076a/command.mak com076b/command.mak
--- com076a/command.mak	Thu Jan 28 23:22:32 1999
+++ com076b/command.mak	Tue Mar 23 14:54:54 1999
@@ -68,11 +68,14 @@
  copy.obj \
  messages.obj \
  cb_catch.obj \
  tmpnam.obj \
  openf.obj \
- {$(LIBPATH)}suppl_s.lib
+ parsenum.obj \
+ {$(LIBPATH)}suppl_s.lib \
+ swapexec.obj \
+ spawn.obj
 
 #		*Explicit Rules*
 command.exe: command.cfg $(EXE_dependencies)
   $(TLINK) /s/n/c/L$(LIBPATH) @&&|
 c0s.obj+
@@ -123,11 +126,14 @@
 ctty.obj+
 copy.obj+
 messages.obj+
 cb_catch.obj+
 tmpnam.obj+
-openf.obj
+openf.obj+
+parsenum.obj+
+swapexec.obj+
+spawn.obj
 command,command
 suppl_s.lib+
 cs.lib
 |
 
@@ -234,14 +240,20 @@
 
 tmpnam.obj: command.cfg tmpnam.c 
 
 openf.obj: command.cfg openf.c 
 
+parsenum.obj: command.cfg parsenum.c 
+
+swapexec.obj: command.cfg swapexec.c 
+
+spawn.obj: command.cfg spawn.asm 
+	$(TASM) /MX /ZI /O SPAWN.ASM,SPAWN.OBJ
+
 #		*Compiler Configuration File*
 command.cfg: command.mak
   copy &&|
--2
 -a
 -f-
 -ff-
 -K
 -C
Binary files com076a/command.prj and com076b/command.prj differ
diff --new-file --recursive --ignore-space-change -U 5 --report-identical-files com076a/compat.h com076b/compat.h
--- com076a/compat.h	Wed Dec 31 18:00:00 1969
+++ com076b/compat.h	Wed Mar 17 20:55:10 1999
@@ -0,0 +1,69 @@
+/*
+   COMPAT.H: EXEC function with memory swap - 
+             Borland/Microsoft compatibility header file.
+
+   Public domain software by
+
+        Thomas Wagner
+        Ferrari electronic GmbH
+        Beusselstrasse 27
+        D-1000 Berlin 21
+        Germany
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <dos.h>
+
+#if defined(__TURBOC__)
+#define TURBO  1
+#define MSC    0
+#else
+#define TURBO  0
+#define MSC    1
+#endif
+
+#if (TURBO)
+#include <dir.h>
+#include <alloc.h>
+#include <sys\stat.h>
+
+#define OS_MAJOR  (_version & 0xff)
+
+#endif
+
+#if (MSC)
+#include <malloc.h>
+#include <direct.h>
+#include <sys\types.h>
+#include <sys\stat.h>
+
+#define fnsplit   _splitpath
+#define fnmerge   _makepath
+
+#define MAXPATH   _MAX_PATH
+#define MAXDRIVE  _MAX_DRIVE
+#define MAXDIR    _MAX_DIR
+#define MAXFILE   _MAX_FNAME
+#define MAXEXT    _MAX_EXT
+
+#define FA_RDONLY _A_RDONLY
+#define FA_HIDDEN _A_HIDDEN
+#define FA_SYSTEM _A_SYSTEM
+#define FA_ARCH   _A_ARCH
+
+#define findfirst(a,b,c) _dos_findfirst(a,c,b)
+#define ffblk find_t
+
+#define OS_MAJOR  _osmajor
+
+#define farmalloc(x)    ((void far *)halloc(x,1))
+#define farfree(x)      hfree((void huge *)x)
+
+#define stpcpy(d,s)     (strcpy (d, s), d + strlen (s))
+
+#define MK_FP(seg,ofs)  ((void far *)(((unsigned long)(seg) << 16) | (unsigned short)(ofs)))
+#endif
+
diff --new-file --recursive --ignore-space-change -U 5 --report-identical-files com076a/config.h com076b/config.h
--- com076a/config.h	Mon Feb  1 21:29:30 1999
+++ com076b/config.h	Tue Mar 23 00:43:14 1999
@@ -9,11 +9,11 @@
  * 12-Aug-1998 ska
  * - added CTTY macro
  */
 
 /* JPP 20 Jul 1998 - define DEBUG to add debugging code */
-/* #define DEBUG */
+//#define DEBUG
 
 /* Define to enable the alias command, and aliases. */
 #define FEATURE_ALIASES
 
 /* Define to enable history (and the doskey command) */
@@ -22,10 +22,14 @@
 /* Define to enable filename completion */
 #define FEATURE_FILENAME_COMPLETION
 
 /* Define to enable to load messages into memory */
 #define FEATURE_LOAD_MESSAGES
+
+/* Define to enable swapping to XMS/EMS/disk */
+//#define FEATURE_SWAP_EXEC
+
 
 #define INCLUDE_CMD_BEEP
 #define INCLUDE_CMD_BREAK
 #define INCLUDE_CMD_CHDIR
 #define INCLUDE_CMD_CLS
Files com076a/copy.c and com076b/copy.c are identical
Files com076a/copy.h and com076b/copy.h are identical
Files com076a/ctty.c and com076b/ctty.c are identical
diff --new-file --recursive --ignore-space-change -U 5 --report-identical-files com076a/date.c com076b/date.c
--- com076a/date.c	Fri Jan 22 17:32:40 1999
+++ com076b/date.c	Wed Mar 24 16:27:46 1999
@@ -14,17 +14,28 @@
  * - added config.h include
  *
  * 29-Jul-1998 (Rob Lake)
  * - fixed stand-alone mode.  Added Pacific C compatible dos_getdate functions
  *
+ * 1999/03/17 ska
+ * bugfix: detection for invalid date strings.
+ *  Now the whole string must match: ^\s*\d+(.(\d+(.\d*)?)?)?\s*$
+ *  The numbers can be delimited by any character from the 7-bit ASCII set,
+ *  which is printable, but not alphanumerical.
+ *  One need not specify all three parts (in which case the delimiter
+ *  may be omitted, too). If one specify;
+ *    1 number --> only the day changes
+ *    2 numbers --> month/day; year remains unchanged
+ *    3 numbers --> month/day/year
  */
 
 #include "config.h"
 
 #ifdef INCLUDE_CMD_DATE
 
 #include <stdio.h>
+#include <stdlib.h>
 #include <dos.h>
 #include <ctype.h>
 #include <string.h>
 
 #include "command.h"
@@ -49,60 +60,41 @@
 
 int parsedate(char *s)
 {
   struct dosdate_t d;
   unsigned char leap;
+  int nums[3],
+    items;
 
-  if (!*s)
-    return 1;
-
-  d.year = 0;
-  d.day = 0;
-  d.month = 0;
+  /* Parse at maximum three numbers */
+  s = parsenum(s, 3, &items, nums);
 
-  // first get mon
-  if (isdigit(*s))
-  {
-    while (isdigit(*s))
-      d.month = d.month * 10 + (*s++) - '0';
-  }
-  else
+  if (!s || *s)                 /* general error or too many characters */
     return 0;
 
-  // get divider
-  if (*s == '/' || *s == '-')
-    s++;
-  else
-    return 0;
-
-  // now get day
-  if (isdigit(*s))
+  _dos_getdate(&d);             /* fetch current info */
+  switch (items)
   {
-    while (isdigit(*s))
-      d.day = d.day * 10 + (*s++) - '0';
-  }
-  else
-    return 0;
+    case 0:                    /* empty line --> always OK */
+      return 1;
 
-  // get divider
-  if (*s == '/' || *s == '-')
-    s++;
-  else
-    return 0;
+    case 1:                    /* single number --> day only */
+      d.day = nums[0];
+      break;
 
-  // now get year
-  if (isdigit(*s))
-  {
-    while (isdigit(*s))
-      d.year = d.year * 10 + (*s++) - '0';
+    case 3:                    /* three numbers --> year, month & day */
+      d.year = nums[2];
+      /* fall through */
+
+    case 2:                    /* two numbers --> month & day */
+      d.day = nums[1], d.month = nums[0];
+      break;
   }
-  else
-    return 0;
 
-  //if only entered two digits, assume 1900's
+  /* if only entered two digits for year, assume 1900's */
   if (d.year <= 99)
-    d.year = 1900 + d.year;
+    d.year += 1900;
 
   leap = (!(d.year % 4) && (d.year % 100)) || !(d.year % 400);
 
   if ((d.month >= 1 && d.month <= 12) &&
       (d.day >= 1 && d.day <= months[leap][d.month]) &&
@@ -118,40 +110,46 @@
 #pragma argsused
 int cmd_date(char *rest)
 {
   char s[40];
 
-  if (!*rest)
+  if (!rest || !*rest)
   {
     struct dosdate_t d;
 
     _dos_getdate(&d);
 
     display_string(TEXT_MSG_CURRENT_DATE,
                    day_strings[d.dayofweek], d.month, d.day, d.year);
+
+    rest = NULL;
   }
 
   while (1)                     //forever loop
 
   {
-    if (!(rest && *rest))
+    if (!rest)
     {
-      printf("Enter new date (mm-dd-yy): ");
+      if ((rest = getMessage(TEXT_MSG_ENTER_DATE)) == NULL)
+        return 1;               /* failed, error message on screan already */
+
+      fputs(rest, stdout);      /* put onto screen */
+      free(rest);
       fgets(s, sizeof(s), stdin);
-      while (*s && s[strlen(s) - 1] < ' ')
-        s[strlen(s) - 1] = '\0';
+      if (cbreak)
+        return 1;
       if (parsedate(s))
         return 0;
     }
     else
     {
       if (parsedate(rest))
         return 0;
-      // force input the next time around.
-      *rest = '\0';
-      display_string(TEXT_ERROR_INVALID_DATE);
     }
+    display_string(TEXT_ERROR_INVALID_DATE);
+    // force input the next time around.
+    rest = NULL;
   }
 }
 #endif
 
 #ifdef DEBUG_STANDALONE
Files com076a/datefunc.c and com076b/datefunc.c are identical
Files com076a/datefunc.h and com076b/datefunc.h are identical
Files com076a/del.c and com076b/del.c are identical
Files com076a/dir.c and com076b/dir.c are identical
diff --new-file --recursive --ignore-space-change -U 5 --report-identical-files com076a/docs/bugs.txt com076b/docs/bugs.txt
--- com076a/docs/bugs.txt	Fri Jan 22 17:32:40 1999
+++ com076b/docs/bugs.txt	Wed Mar 24 17:14:38 1999
@@ -7,19 +7,13 @@
 
 Reported Bugs
 ~~~~~~~~~~~~~
 
 From: Steffan
- o let set work with or without the '=' sign.  People like it that
-   way.
-
  o The DEL access()/stat() stuff is still broken (included in my last
    buglist report)
 
-From: Yury Semenov
- o "alias v = dir" doesn't work because of the spaces.
-
 From: John P. Price
  o History.  If you typed a long command line (one that wraps to the
    next line), then if you go back and forth with the up and down
    keys, it scrolls the screen in a weird way.
 
@@ -30,37 +24,32 @@
    this a compile-time option.
 
  o typing "?" or "??" runs the first program that matches...
    Likewise with "*".  We should not do wildcards in the command!
 
- o The exec function that was being used by version 0.73 and earlier
-   does not copy the environment to the child process.  In 0.74 this
-   was changed to use spawnl, but the exec function should probably
-   be fixed instead.
-
 
 From: Charles Dye
  o DIR displays incorrect year values for dates 1-1-2000 or later.
    Perhaps it would be better to always display years as four
    digits?
 
  o CD followed by a drive letter (but no directory name) should
    display the current directory for the specified drive.
 
- o VER displays the version of COMMAND.COM -- good.  But it should
-   also show the DOS version as well.  It might also be nice if VER
-   checked for FreeDOS specifically (OEM number of FDh) and
-   displayed a special message, the DOS-C build number, etc.
-   Furthermore, it might be a good idea to reserve /R for detailed
-   DOS info like DOS-is-high, DOS-is-in-ROM, subversion, etc.  (like
-   MS- and PC DOS do.)
-
          ****  Please report bugs to command@gcfl.net!  ****
 
 
 Bugs fixed recently:
 
+ o let set work with or without the '=' sign.  People like it that
+   way.
+
+ o The exec function that was being used by version 0.73 and earlier
+   does not copy the environment to the child process.  In 0.74 this
+   was changed to use spawnl, but the exec function should probably
+   be fixed instead.
+
  o DEL / ERASE now support the badly-missed /P switch.  Great!  Put
    it in the DEL /? syntax screen!  Also, DEL misses files with the
    Hidden and System attributes -- I'm not sure whether that's a bug
    or a deliberate feature.
 
@@ -69,7 +58,16 @@
 
  o (JP- I think this is fixed now) COMMAND.COM has problems when
    loaded from CONFIG.SYS.  Appearantly MSDOS does not create an
    initial environment, so command.com has to do it.  It does not.
 
+ o VER displays the version of COMMAND.COM -- good.  But it should
+   also show the DOS version as well.  It might also be nice if VER
+   checked for FreeDOS specifically (OEM number of FDh) and
+   displayed a special message, the DOS-C build number, etc.
+   Furthermore, it might be a good idea to reserve /R for detailed
+   DOS info like DOS-is-high, DOS-is-in-ROM, subversion, etc.  (like
+   MS- and PC DOS do.)
+
+ o "alias v = dir" doesn't work because of the spaces.
 
          ****  Please report bugs to command@gcfl.net!  ****
Files com076a/docs/changes.txt and com076b/docs/changes.txt are identical
Files com076a/docs/compile.txt and com076b/docs/compile.txt are identical
Files com076a/docs/files.txt and com076b/docs/files.txt are identical
diff --new-file --recursive --ignore-space-change -U 5 --report-identical-files com076a/docs/history.txt com076b/docs/history.txt
--- com076a/docs/history.txt	Thu Feb 11 01:13:00 1999
+++ com076b/docs/history.txt	Wed Mar 24 17:35:48 1999
@@ -515,5 +515,41 @@
 o Changed openStrFile() function to try to read the strings from the
   end of command first, and then to try to read strings.dat in the
   same directory as command.com.  This helps when running from the
   debugger.
 
+24-Mar-1999 0.76b Steffen Kaiser <skaise2a@smail.inf.fh-rhein-sieg.de>
+~~~~~~~~~~~~~~~~~~~~~~
+o date.c: bugfix: detection for invalid date strings.
+   Now the whole string must match: ^\s*\d+(.(\d+(.\d*)?)?)?\s*$
+   The numbers can be delimited by any character from the 7-bit ASCII set,
+   which is printable, but not alphanumerical.
+   One need not specify all three parts (in which case the delimiter
+   may be omitted, too). If one specify;
+     1 number --> only the day changes
+     2 numbers --> month/day; year remains unchanged
+     3 numbers --> month/day/year
+o time.c: bugfix: Detection of invalid time strings
+   One can specify:
+     ^\s*\d+.\d+(.(\d+(.\d*)?)?)?\s*([aApP]([mM]|\.[mM]\.)?\s*$
+   If one specifies:
+     1 number --> error
+     2 numbers --> hour:minute, seconds & hundreds default to zero
+     3 numbers --> hour:minute:seconds, hundreds defaults to zero
+     4 numbers --> hour:minute:seconds.hundreds
+   The numbers may be delimited by any character from the 7-bit ASCII set,
+   which is printable, but not alphanumerical.
+o err_hand.c: bugfix: Print garbage on screen, if no disk in floppy
+    drive An "interrupt" function saves & restores all registers and
+    also sets up DS correctly.
+o created parsenum.c
+
+24-Mar-1999 (John P Price <linux-guru@gcfl.net>)
+~~~~~~~~~~~~~~~~~~~~~~
+o Added support for swapable exec.  There is a compile-time directive
+  so you can leave it out, or use it.  IT DOES NOT WORK UNDER
+  FREEDOS.  Probably a kernel problem, but that has not been
+  determined yet.
+o changed the way we run autoexec.bat or a batch file on the command
+  line.  This seemed to clear up some bugs with running autoexec.bat.
+o Changed set command so you can use a space between the variable and
+  the value.  Prints Syntax error if you don't have a space or a '='.
Files com076a/docs/license.txt and com076b/docs/license.txt are identical
Files com076a/docs/notes.txt and com076b/docs/notes.txt are identical
diff --new-file --recursive --ignore-space-change -U 5 --report-identical-files com076a/docs/readme.txt com076b/docs/readme.txt
--- com076a/docs/readme.txt	Fri Jan 22 17:32:40 1999
+++ com076b/docs/readme.txt	Wed Mar 24 16:41:24 1999
@@ -1,17 +1,17 @@
-FreeDOS Command Line Interface
+FreeCom Command Line Interface
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 About
 ~~~~~
 This software is part of the FreeDOS project. Please email
 freedos@sunsite.unc.edu for more information, or visit the freedos
 archive at "ftp://sunsite.unc.edu/pub/micro/pc-stuff/freedos".  Also,
 visit our web page at http://www.freedos.org/.
 
 The command.com web site is at
 
-   http://www.gcfl.net/FreeDOS/command.com/
+   http://www.gcfl.net/FreeCom
 
 Please send any bug reports, comments or other info to:
 
    command@gcfl.net  (command.com development mailing list)
 
@@ -34,24 +34,18 @@
 may or may not work properly under it.  Of course I am making every
 effort to make it compatible.
 
 Installation
 ~~~~~~~~~~~~
-To use this shell, just run the COMMAND.COM file.  Eventually, you
-will be able to add this to your CONFIG.SYS file, but this doesn't
-currently work.
-
-When it does, you'll be able to add this line to CONFIG.SYS:
+To use this shell, just run the COMMAND.COM file.
+You can also add this to your CONFIG.SYS file:
 
 SHELL=<shell path><shell filename>
 
 e.g.
 
-SHELL=C:\FREEDOS\COMMAND.COM
-
-(This may or not work... I've received complaints that it doesn't, so
-I'd imagine that's the case!)
+SHELL=C:\FREEDOS\COMMAND.COM /P
 
 Current Features
 ~~~~~~~~~~~~~~~~
 environment handling with prompt and path support.
 directory utilities.
Files com076a/docs/sk-notes.txt and com076b/docs/sk-notes.txt are identical
Files com076a/docs/todo.txt and com076b/docs/todo.txt are identical
Files com076a/echo.c and com076b/echo.c are identical
Files com076a/environ.c and com076b/environ.c are identical
diff --new-file --recursive --ignore-space-change -U 5 --report-identical-files com076a/err_hand.c com076b/err_hand.c
--- com076a/err_hand.c	Fri Jan 22 17:32:40 1999
+++ com076b/err_hand.c	Wed Mar 17 19:27:34 1999
@@ -13,10 +13,15 @@
  * 27-Jul-1998 (John P Price <linux-guru@gcfl.net>)
  * - added config.h include
  *
  * 12-Aug-1998 ska
  * - added autofail (/F command line option)
+ *
+ * 1999/03/17 ska
+ * bugfix: Print garbage on screen, if no disk in floppy drive
+ * 	An "interrupt" function saves & restores all registers and
+ *	also sets up DS correctly.
  */
 
 #include "config.h"
 
 #include "err_hand.h"
@@ -46,20 +51,16 @@
  */
 static void printstring(char *string)
 {
   asm {
     push ds
-    push dx
-    push ax
   }
   _DS = FP_SEG(string);
   _DX = FP_OFF(string);
   asm {
     mov ah, 09h
     int 21h
-    pop ax
-    pop dx
     pop ds
   }
 }
 
 /*
@@ -118,30 +119,28 @@
   char *err_msg;
   char device[8];
   char buf[BUFSIZE];
   char action;
 
+  err_flags = reg_Ax >> 8;      /* make it AH */
+  drive = reg_Ax & 0xFF;        /* make it AL */
+/* Clear AL for return */
+  reg_Ax &= 0xFF00;
+
   if(autofail) {
-    reg_Ax = (0xFF00 | FAIL);
+    reg_Ax |= FAIL;
     return;
   }
 
   if (++calls == 5)
   {
     calls = 0;
     printstring("Fail on INT24\n\r$");
-    reg_Ax = (0xFF00 | FAIL);
+    reg_Ax |= FAIL;
     return;
   }
 
-  asm {
-    push cs
-    pop ds
-  }
-
-  err_flags = reg_Ax >> 8;      /* make it AH */
-  drive = reg_Ax & 0xFF;        /* make it AL */
   err_code = reg_Di & 0xFF;     /* error code in lower byte of DI */
   dev = (dev_hdr far *) MK_FP(reg_Bp, reg_Si);
   err_msg = get_err(err_code);
 retry:
   if (err_flags & CHAR_DEV)
@@ -183,13 +182,10 @@
     printstring("Fail$");
   }
   if (err_flags & IGNORE_FLG)
     printstring(", or Ignore$");
   printstring("?$");
-
-/* Clear AL for return */
-  reg_Ax &= 0xFF00;
 
   action = getche();
   printstring("\n\r$");
   switch (toupper(action))
   {
Files com076a/err_hand.h and com076b/err_hand.h are identical
Files com076a/errno.h and com076b/errno.h are identical
Files com076a/error.c and com076b/error.c are identical
Files com076a/exec.c and com076b/exec.c are identical
Files com076a/fdcom.ico and com076b/fdcom.ico are identical
Files com076a/ffind.c and com076b/ffind.c are identical
Files com076a/file_id.diz and com076b/file_id.diz are identical
Files com076a/filecomp.c and com076b/filecomp.c are identical
diff --new-file --recursive --ignore-space-change -U 5 --report-identical-files com076a/fixstrs.bat com076b/fixstrs.bat
--- com076a/fixstrs.bat	Tue Dec  1 13:28:30 1998
+++ com076b/fixstrs.bat	Wed Mar 17 19:23:38 1999
@@ -1,5 +1,6 @@
+@echo off
 cd strings
 fixstrs
 copy strings.dat ..
 del strings.dat
 copy strings.h ..
Files com076a/for.c and com076b/for.c are identical
Files com076a/goto.c and com076b/goto.c are identical
diff --new-file --recursive --ignore-space-change -U 5 --report-identical-files com076a/history.c com076b/history.c
--- com076a/history.c	Fri Jan 22 17:32:40 1999
+++ com076b/history.c	Thu Mar 18 22:06:22 1999
@@ -1,6 +1,5 @@
-
 /*
  *  HISTORY.C - command line history.
  *
  *
  *
@@ -36,10 +35,11 @@
 char *historyptr = NULL;
 unsigned int curline = 0;
 unsigned int numlines = 0;
 unsigned int maxpos = 0;
 
+
 void history(int dir, char *commandline)
 {
   static char *lines[MAXLINES];
   int count;
   int length;
@@ -52,29 +52,21 @@
   }
 
   if (dir > 0)                  /* next command */
   {
     if (curline < numlines)
-    {
       curline++;
-    }
 
     if (curline == numlines)
-    {
       commandline[0] = 0;
-    }
     else
-    {
       strcpy(commandline, lines[curline]);
     }
-  }
   else if (dir < 0)             /* prev command */
   {
     if (curline > 0)
-    {
       curline--;
-    }
 
     strcpy(commandline, lines[curline]);
   }
   else
     /* add to history */
@@ -84,40 +76,32 @@
     while (maxpos + strlen(commandline) + 1 > history_size || numlines >= MAXLINES)
     {
       length = strlen(lines[0]) + 1;
 
       for (count = 0; count < maxpos && count + (lines[1] - lines[0]) < history_size; count++)
-      {
         historyptr[count] = historyptr[count + length];
-      }
 
       maxpos -= length;
 
       for (count = 0; count <= numlines && count < MAXLINES; count++)
-      {
         lines[count] = lines[count + 1] - length;
-      }
 
       numlines--;
 #ifdef DEBUG
       printf("Reduced size:  %ld lines\n", numlines);
 
       for (count = 0; count < numlines; count++)
-      {
         printf("%d: %s\n", count, lines[count]);
-      }
 #endif
     }
 
     strcpy(lines[numlines], commandline);
     numlines++;
     lines[numlines] = lines[numlines - 1] + strlen(commandline) + 1;
     maxpos += strlen(commandline) + 1;
     curline = numlines;         /* last line, empty */
   }
-
-  return;
 }
 
 /*
  * history command
  *
@@ -143,11 +127,10 @@
     curline = 0;
     numlines = 0;
     maxpos = 0;
   }
   else
-  {
     display_string(TEXT_MSG_HISTORY_SIZE, history_size);
-  }
+
   return 0;
 }
 #endif
Files com076a/if.c and com076b/if.c are identical
Files com076a/indent.pro and com076b/indent.pro are identical
diff --new-file --recursive --ignore-space-change -U 5 --report-identical-files com076a/init.c com076b/init.c
--- com076a/init.c	Thu Feb 11 01:01:48 1999
+++ com076b/init.c	Wed Mar 24 17:35:48 1999
@@ -45,10 +45,13 @@
  *   because for some reason kbhit() hangs under the FreeDOS kernel.
  * - prints seconds as it waits for a keypress from user (during initial
  *   boot)
  * - Fixed path to autoexec.bat.
  *
+ * 24-Mar-1999 (John P Price <linux-guru@gcfl.net>)
+ * - changed the way we run autoexec.bat or a batch file on the command line.
+ *
  */
 
 #include "config.h"
 
 #include <ctype.h>
@@ -133,16 +136,18 @@
   printf("[filename completion] ");
 #endif
 #ifdef FEATURE_LOAD_MESSAGES
   printf("[load messages] ");
 #endif
+#ifdef FEATURE_SWAP_EXEC
+  printf("[swapping] ");
+#endif
   putchar('\n');
 
   return 0;
 }
 
-
 int keypressed(void)
 {
   union REGS r;
 
   r.h.ah = 0x01;
@@ -154,11 +159,10 @@
     return 0;
   else
     return 1;
 }
 
-
 /* Waits about 3 secs for a keypress.
    returns 0 if none else returns key pressed.
  */
 
 int WaitForFkeys(void)
@@ -167,19 +171,19 @@
   struct dostime_t now;
   int secs = 3;
   int ch;
 
   _dos_gettime(&start);
-  printf("%d",secs);
+  printf("%d", secs);
   while (secs)
   {
     _dos_gettime(&now);
     if (now.second != start.second)
     {
       _dos_gettime(&start);
       secs--;
-      printf("\b%d",secs);
+      printf("\b%d", secs);
     }
     if (keypressed())
     {
       if ((ch = getch()) == 0)
         ch = getch() + 256;
@@ -242,11 +246,11 @@
  *              size of environment etc.)
  *      5) If shell is interactive, invoke a ver() command.
  */
 
 #pragma argsused
-int initialize(int argc, char *argv[])
+int initialize(int argc, char *argv[], char *commandline)
 {
   char *comPath;                /* path to COMMAND.COM (for COMSPEC/reload) */
   char *newTTY;                 /* what to change TTY to */
   unsigned envSize;             /* minimum environment size (/E:) */
   int loadMSGs;                 /* load messages permanent into memory (/MSG) */
@@ -435,14 +439,14 @@
 
   /* First of all, set up the environment */
   env_resizeCtrl |= ENV_USEUMB | ENV_ALLOWMOVE;
   if (!env_newsize(0, envSize))
     puts("Could not resize/create environment");
-  else
-  {
-    printf("Environment size set to %d bytes.\n", envSize);
-  }
+//  else
+  //  {
+  //    printf("Environment size set to %d bytes.\n", envSize);
+  //  }
 
   /* Then add the COMSPEC variable */
   if (comPath)
   {
     /* Remove quotes, maybe this comes handy in the future */
@@ -496,10 +500,12 @@
     error_env_var("COMSPEC");
 
   free(comPath);
 #else
   /* In my debugger, argv[0] is not fully qualified. */
+//  comPath = _truename(q);
+
   comPath = _fullpath(NULL, q, 0);
   if (chgEnv("COMSPEC", comPath))
     error_env_var("COMSPEC");
   if ((ComDir = strdup(comPath)) == NULL)
   {
@@ -546,16 +552,16 @@
       tracemode = 1;
     }
 
     if (key == KEY_F5)
     {
-      printf("Bypassing %s file.\n", autoexec);
+      printf("Bypassing %s\n", autoexec);
     }
     else
     {
       if (exist(autoexec))
-        parsecommandline(autoexec);
+        strcpy(commandline, autoexec);
       else
       {
         printf("%s not found.\n", autoexec);
 #ifdef INCLUDE_CMD_DATE
         cmd_date(NULL);
@@ -587,11 +593,11 @@
     {
       *q = ' ';
       q = stpcpy(q + 1, p);
     }
 
-    parsecommandline(buf);      // otherwise 'h' see above -- ska
+    strcpy(commandline, buf);   // otherwise 'h' see above -- ska
 
     if (spawnCmd != 'K')
       return 1;
   }
 
@@ -600,9 +606,9 @@
     short_version();
     putchar('\n');
     showcmds(NULL);
     putchar('\n');
   }
-  printf("%ld bytes available.\n\n", farcoreleft());
+//  printf("%ld bytes available.\n\n", farcoreleft());
 
   return 0;
 }
diff --new-file --recursive --ignore-space-change -U 5 --report-identical-files com076a/internal.c com076b/internal.c
--- com076a/internal.c	Fri Jan 22 17:32:40 1999
+++ com076b/internal.c	Wed Mar 24 16:35:30 1999
@@ -138,13 +138,10 @@
 
   /* check if there is no space between the command and the path */
   if (rest[0] == 0)
   {
     /* search for the \ or . so that both short & long names will work */
-//                                                                                                                                                              for (place = first; *place; place++)
-    //                                                                                                                                                                                                                                              if (*place == '.' || *place == '\\')
-    //                                                                                                                                                                                                                                                                                                                              break;
 
     place = rest;
 
     if (*place)
       dir = place;
Files com076a/lh.asm and com076b/lh.asm are identical
Files com076a/loadhigh.c and com076b/loadhigh.c are identical
Files com076a/loadhigh.h and com076b/loadhigh.h are identical
Files com076a/lowexec.asm and com076b/lowexec.asm are identical
Files com076a/makecom.bat and com076b/makecom.bat are identical
diff --new-file --recursive --ignore-space-change -U 5 --report-identical-files com076a/makedist.bat com076b/makedist.bat
--- com076a/makedist.bat	Thu Dec  3 14:22:04 1998
+++ com076b/makedist.bat	Wed Mar 24 16:51:40 1999
@@ -9,22 +9,24 @@
 
 copy %1\command.com temp\bin
 copy %1\docs\*.* temp\doc\%1
 
 cd temp
-pkzip -mprex ..\%1x.zip
+pkzip -mpre0 ..\binaries.zip
 cd ..
+pkzip -mex %1x.zip binaries.zip
 
 rem now make source distribution
 
 mkdir temp\source
 mkdir temp\source\%1
 xcopy /s /e %1 temp\source\%1
 
 cd temp
-pkzip -mprex ..\%1s.zip
+pkzip -mpre0 ..\source.zip
 cd ..
+pkzip -mex %1s.zip source.zip
 
 rmdir temp
 
 goto done
 
Files com076a/makefile.bcc and com076b/makefile.bcc are identical
diff --new-file --recursive --ignore-space-change -U 5 --report-identical-files com076a/makefile.tcc com076b/makefile.tcc
--- com076a/makefile.tcc	Thu Jan 28 23:22:32 1999
+++ com076b/makefile.tcc	Tue Mar 23 14:54:54 1999
@@ -68,11 +68,14 @@
  copy.obj \
  messages.obj \
  cb_catch.obj \
  tmpnam.obj \
  openf.obj \
- {$(LIBPATH)}suppl_s.lib
+ parsenum.obj \
+ {$(LIBPATH)}suppl_s.lib \
+ swapexec.obj \
+ spawn.obj
 
 #		*Explicit Rules*
 command.exe: command.cfg $(EXE_dependencies)
   $(TLINK) /s/n/c/L$(LIBPATH) @&&|
 c0s.obj+
@@ -123,11 +126,14 @@
 ctty.obj+
 copy.obj+
 messages.obj+
 cb_catch.obj+
 tmpnam.obj+
-openf.obj
+openf.obj+
+parsenum.obj+
+swapexec.obj+
+spawn.obj
 command,command
 suppl_s.lib+
 cs.lib
 |
 
@@ -234,14 +240,20 @@
 
 tmpnam.obj: command.cfg tmpnam.c 
 
 openf.obj: command.cfg openf.c 
 
+parsenum.obj: command.cfg parsenum.c 
+
+swapexec.obj: command.cfg swapexec.c 
+
+spawn.obj: command.cfg spawn.asm 
+	$(TASM) /MX /ZI /O SPAWN.ASM,SPAWN.OBJ
+
 #		*Compiler Configuration File*
 command.cfg: command.mak
   copy &&|
--2
 -a
 -f-
 -ff-
 -K
 -C
diff --new-file --recursive --ignore-space-change -U 5 --report-identical-files com076a/messages.c com076b/messages.c
--- com076a/messages.c	Thu Feb 11 01:04:42 1999
+++ com076b/messages.c	Wed Mar 24 16:36:14 1999
@@ -50,10 +50,11 @@
 #ifdef FEATURE_LOAD_MESSAGES
 static unsigned msgSegm = 0;    /* strings segment if loaded */
 
 void uninitModuleStrings(void)  /* called at exit */
 {
+  if (msgSegm)
   freeBlk(msgSegm);
   msgSegm = 0;
 }
 #endif
 
@@ -190,11 +191,11 @@
   fclose(f);
   return 1;
 }
 #endif
 
-static char *fetchString(unsigned id)
+char *fetchString(unsigned id)
 {
   struct indextype string_index;
 
   FILE *f;
   char *thisstr;
@@ -251,10 +252,25 @@
   }
 
   fclose(f);
 
   return thisstr;
+}
+
+/*
+ * As fetchString(), but removes the last character.
+ *
+ *  This is necessary, because there is always a newline character
+ *  at the end of the last line.
+ */
+char *getMessage(unsigned id)
+{
+  char *h;
+
+  if ((h = fetchString(id)) != NULL && *h)
+    strchr(h, '\0')[-1] = '\0';
+  return h;
 }
 
 void display_string(unsigned id,...)
 {
   char *thisstr;
Files com076a/misc.c and com076b/misc.c are identical
Files com076a/model.def and com076b/model.def are identical
Files com076a/openf.c and com076b/openf.c are identical
Files com076a/openf.h and com076b/openf.h are identical
diff --new-file --recursive --ignore-space-change -U 5 --report-identical-files com076a/parsenum.c com076b/parsenum.c
--- com076a/parsenum.c	Wed Dec 31 18:00:00 1969
+++ com076b/parsenum.c	Wed Mar 24 16:24:06 1999
@@ -0,0 +1,82 @@
+/*
+ *  PARSENUM.C - Parse delimited numbers
+ *
+ *  Currently used by DATE & TIME
+ *
+ *  1999/03/17 ska
+ *    started.
+ */
+
+#include "config.h"
+
+#if defined(INCLUDE_CMD_DATE) || defined(INCLUDE_CMD_TIME)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "command.h"
+
+/*
+ *  Parse any number of unsigned integer numbers in the range:
+ *    0..9999
+ *
+ *  Input:  s: String, != NULL
+ *    maxCnt: maximum number of numbers to parse
+ *    *cnt: receives the number of parsed numbers, != NULL
+ *    nums[]: receives the parsed numbers
+ *
+ *  The string may consists of:
+ *  string ::= [whitespaces] [num_list [whitespaces]]
+ *  whitespaces ::= whitespace | whitespaces whitespace
+ *  whitespace ::= any_character_isspace()_returns_true_for
+ *  num_list ::= delimited_numbers [delimiter]
+ *  delimited_numbers ::= number | delimited_numbers delimiter number
+ *  number ::= digit | number digit
+ *  digit ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
+ *  delimiter ::= any_character_ispunct()_and_isascii()_are_true_for
+ *
+ *  There can be at maximum one spurious delimiter behind all the
+ *  numbers, except behind the maxCnt'th number. This allows to
+ *  type in "half" dates, e.g.: "3.3." for 3rd March (equal to: "3.3");
+ *  but this is forbidden for: "3.3.1999.", because maxCnt is equal to
+ *  three, when dates are parsed.
+ *
+ *  Return:
+ *    NULL: Fatal error, e.g. number exceeds boundary
+ *    else: pointer to first character not parsed
+ */
+char *parsenum(char *s, int maxCnt, int *cnt, int nums[])
+{ int n;
+
+  if(!s || !cnt || !nums || maxCnt <= 0)
+    return NULL;
+
+  *cnt = 0;
+
+  /* skip leading whitespaces */
+  while(isspace(*s))
+    ++s;
+
+  while(isdigit(*s)) {
+    /* scan next number */
+    n = 0;
+    do {
+      if(n >= 10000)
+        return NULL;
+      n = n * 10 + *s - '0';
+    } while(isdigit(*++s));
+    nums[(*cnt)++] = n;
+    if(!isascii(*s) || !ispunct(*s) || !--maxCnt)
+      break;
+    ++s;      /* skip delimiter */
+  }
+
+  /* skip trailing whitespaces */
+  while(isspace(*s))
+    ++s;
+
+  return s;
+}
+
+#endif
Files com076a/path.c and com076b/path.c are identical
Files com076a/pause.c and com076b/pause.c are identical
Files com076a/prompt.c and com076b/prompt.c are identical
Files com076a/redir.c and com076b/redir.c are identical
Files com076a/ren.c and com076b/ren.c are identical
diff --new-file --recursive --ignore-space-change -U 5 --report-identical-files com076a/set.c com076b/set.c
--- com076a/set.c	Fri Jan 22 17:32:40 1999
+++ com076b/set.c	Wed Mar 24 17:35:48 1999
@@ -15,10 +15,14 @@
  * - added config.h include
  *
  * 28-Jul-1998 (John P Price <linux-guru@gcfl.net>)
  * - added set_env function to set env. variable without needing set command
  *
+ * 24-Mar-1999 (John P Price <linux-guru@gcfl.net>)
+ * - Changed set command so you can use a space between the variable and
+ *   the value.  Prints Syntax error if you don't have a space or a '='.
+ *
  */
 
 #include "config.h"
 
 #ifdef INCLUDE_CMD_SET
@@ -77,17 +81,16 @@
   }
 
   /* make sure there is an = in the command */
   if ((p = strchr(rest, '=')) == NULL)
   {
-    if ((p = getEnv(rest)) == NULL)
+    if ((p = strchr(rest, ' ')) == NULL)
     {
-      error_env_var_not_found(rest);
+      error_syntax(NULL);
       return 1;
     }
-    puts(p);
-    return 0;
+    *p = '=';
   }
 
   if (saveCase)
   {
     *p = '\0';
Files com076a/shift.c and com076b/shift.c are identical
diff --new-file --recursive --ignore-space-change -U 5 --report-identical-files com076a/spawn.asm com076b/spawn.asm
--- com076a/spawn.asm	Wed Dec 31 18:00:00 1969
+++ com076b/spawn.asm	Wed Mar 17 21:30:48 1999
@@ -0,0 +1,2710 @@
+;
+;	--- Version 3.3 93-11-29 09:06 ---
+;
+;	SPAWN.ASM - Main function for memory swapping spawn call.
+;
+;	Public Domain Software written by
+;		Thomas Wagner
+;		Ferrari electronic GmbH
+;		Beusselstrasse 27
+;		D-1000 Berlin 21
+;		Germany
+;
+;
+; Assemble with
+;
+; tasm  /DPASCAL spawn,spawnp  		- Turbo Pascal (Tasm only), near
+; tasm  /DPASCAL /DFARCALL spawn,spawnp	- Turbo Pascal (Tasm only), far
+; ?asm  spawn;		  		- C, default model (small)
+; ?asm  /DMODL=large spawn  		- C, large model
+;
+;	NOTE:	For C, change the 'model' directive below according to your
+;		memory model, or define MODL=xxx on the command line.
+;
+;		For Turbo C Huge model, you must give /DTC_HUGE on the
+;		command line, or define it here.
+;
+;
+; Main function:
+;
+;   PASCAL:
+;   	function do_spawn (swapping: integer;
+;   			   execfname: string;
+;			   cmdtail: string;
+;   			   envlen: word;
+;			   var envp;
+;			   stdin: string;
+;		           stdout: string;
+;			   stderr: string): integer;
+;
+;   C:
+;   	int do_spawn (int swapping,
+;		      char *execfname,
+;		      char *cmdtail,
+;		      unsigned envlen,
+;		      char *envp,
+;		      char *stdin,
+;		      char *stdout,
+;		      char *stderr)
+;
+;   Parameters:
+;
+;	swapping - swap/spawn/exec function:
+;			< 0: Exec, don't swap
+;		  	  0: Spawn, don't swap
+;			> 0: Spawn, swap
+;			     in this case, prep_swap must have
+;			     been called beforehand (see below).
+;
+;	cmdtail - command tail for EXEC.
+;
+;	execfname - name and path of file to execute.
+;
+;	envlen - length of environment copy (may be 0).
+;
+;	envp -  pointer to environment block (must be aligned on
+;		paragraph boundary). Unused if envlen is 0.
+;
+;	'cmdtail' and 'execfname' must be zero terminated, even when
+;	calling from Pascal. For Pascal, the length byte of the string
+;	is ignored.
+;
+;   Returns:
+;	0000..00ff:	Returncode of EXECed program
+;	03xx:		DOS-Error xx calling EXEC
+;	0500:		Swapping requested, but prep_swap has not
+;			been called or returned an error
+;	0501:		MCBs don't match expected setup
+;	0502:		Error while swapping out
+;	06xx:		DOS-Error xx on redirection
+;
+;
+; For swapping, the swap method must be prepared before calling do_spawn.
+;
+;   PASCAL:
+;	function prep_swap (method: word; swapfname: string): integer;
+;   C:
+;	int prep_swap (unsigned method, char *swapfname)
+;
+;   Parameters:
+;
+;	method	- bit-map of allowed swap devices:
+;			01 - Allow EMS
+;			02 - Allow XMS
+;			04 - Allow File swap
+;			10 - Try XMS first, then EMS
+;			40 - Create file as "hidden"
+;			80 - Use "create temp" call for file swap
+;		       100 - Don't preallocate file
+;		       200 - Check for Network, don't preallocate if net
+;		      4000 - Environment block will not be swapped
+;
+;	swapfname - swap file name (may be undefined if the
+;		    "method" parameters disallows file swap).
+;		    The string must be zero terminated, even
+;		    when calling from Pascal. For Pascal, the 
+;		    length byte of the string is ignored.
+;
+;   Returns:
+;
+;   	A positive integer on success:
+;		1 - EMS swap initialized
+;		2 - XMS swap initialized
+;		4 - File swap initialized
+;	A negative integer on failure:
+;		-1 - Couldn't allocate swap space
+;		-2 - The spawn module is located too low in memory
+;
+;--------------------------------------------------------------------------
+;
+;
+;	Set NO_INHERIT to FALSE if you don't want do_exec to mess with
+;	the handle table in the PSP, and/or you do want the child process
+;	to inherit all open files.
+;	If NO_INHERIT is TRUE, only the first five handles (the standard
+;	ones) will be inherited, all others will be hidden. This allows
+;	the child to open more files, and also protects you from the child
+;	messing with any open handles.
+;	NO_INHERIT should always be TRUE if you use functions to extend
+;	the handle table (for more than 20 open files).
+;
+;	Set REDIRECT to FALSE if you do not want do_spawn to support redirection.
+;
+;	The following defines are for Turbo Pascal only:
+;		Set PAS_FREE to TRUE to not swap the unused heap space.
+;		Set TPAS_6 to TRUE for Turbo Pascal version 6.
+;
+;	When PAS_FREE is TRUE, the unused heap, i.e. the space between
+;	HeapPtr and FreePtr for TP 5.x, or the space between HeapPtr and
+;	HeapEnd for TP6.x, is temporarily chained into the DOS free space 
+;	list, and is not swapped out. Depending on your applications heap 
+;	usage, this can save a large amount of swap space, and considerably
+;	speed up the swap.
+;
+;
+FALSE		=	0
+TRUE		=	NOT FALSE
+;
+NO_INHERIT	=	FALSE
+REDIRECT	=	FALSE
+;
+PAS_FREE	=	FALSE
+TPAS_6		=	FALSE
+;
+;
+	IFDEF	PASCAL
+	.model	tpascal
+	IFDEF	FARCALL
+	%out	Pascal, far calls
+	ELSE
+	%out	Pascal, near calls
+	ENDIF
+;
+	extrn	prefixseg: word
+	IF	PAS_FREE
+	extrn	HeapPtr: dword
+	IF	TPAS_6
+	extrn	HeapEnd: dword
+	ELSE
+	extrn	FreePtr: dword
+	ENDIF
+	ENDIF
+;
+ptrsize	=	1
+	ELSE
+	IFNDEF	MODL
+	.model	small,c
+	%out	small model
+	ELSE
+%	.model	MODL,c
+%	%out	MODL model
+	ENDIF
+;
+ptrsize	=	@DataSize
+;
+	extrn	_psp: word
+	ENDIF
+;
+	public	do_spawn
+	public	prep_swap
+	IFNDEF	PASCAL
+	public	swap_prep
+	ENDIF
+;
+stacklen	=	256		; local stack
+;
+;	"ems_size" is the EMS block size: 16k.
+;
+ems_size	=	16 * 1024	; EMS block size
+ems_parasize	=	ems_size / 16	; same in paragraphs
+ems_shift	=	10		; shift factor for paragraphs
+ems_paramask	=	ems_parasize-1	; block mask
+;
+;	"xms_size" is the unit of measurement for XMS: 1k
+;
+xms_size	=	1024		; XMS block size
+xms_parasize	=	xms_size / 16	; same in paragraphs
+xms_shift	=	6		; shift factor for paragraphs
+xms_paramask	=	xms_parasize-1	; block mask
+;
+;	Method flags
+;
+USE_EMS		=	01h
+USE_XMS		=	02h
+USE_FILE	=	04h
+XMS_FIRST	=	10h
+HIDE_FILE	=	40h
+CREAT_TEMP	=	80h
+NO_PREALLOC	=	100h
+CHECK_NET	=	200h
+DONT_SWAP_ENV	=	4000h
+;
+;	Return codes
+;
+RC_TOOLOW	=	0102h
+RC_BADPREP	=	0500h
+RC_MCBERROR	=	0501h
+RC_SWAPERROR	=	0502h
+RC_REDIRFAIL	=	0600h
+;
+EMM_INT		=	67h
+;
+;	The EXEC function parameter block
+;
+exec_block	struc
+envseg	dw	?		; environment segment
+ppar	dw	?		; program parameter string offset
+pparseg	dw	?		; program parameter string segment
+fcb1	dw	?		; FCB offset
+fcb1seg	dw	?		; FCB segment
+fcb2	dw	?		; FCB offset
+fcb2seg	dw	?		; FCB segment
+exec_block	ends
+;
+;	Structure of an XMS move control block
+;
+xms_control	struc
+lenlo		dw	?	; length to move (doubleword)
+lenhi		dw	?
+srchnd		dw	?	; source handle (0 for standard memory)
+srclo		dw	?	; source address (doubleword or seg:off)
+srchi		dw	?
+desthnd		dw	?	; destination handle (0 for standard memory)
+destlo		dw	?	; destination address (doubleword or seg:off)
+desthi		dw	?
+xms_control	ends
+;
+;	The structure of the start of an MCB (memory control block)
+;
+mcb		struc
+id		db	?
+owner		dw	?
+paras		dw	?
+mcb		ends
+;
+;	The structure of an internal MCB descriptor.
+;	CAUTION: This structure is assumed to be no larger than 16 bytes
+;	in several places in the code, and to be exactly 16 bytes when
+;	swapping in from file. Be careful when changing this structure.
+;
+mcbdesc		struc
+addr		dw	?	; paragraph address of the MCB
+msize		dw	?	; size in paragraphs (excluding header)
+swoffset	dw	?	; swap offset (0 in all blocks except first)
+swsize		dw	?	; swap size (= msize + 1 except in first)
+num_follow	dw	?	; number of following MCBs
+		dw	3 dup(?) ; pad to paragraph (16 bytes)
+mcbdesc		ends
+;
+;	The variable block set up by prep_swap
+;
+prep_block	struc
+xmm		dd	?		; XMM entry address
+first_mcb	dw	?		; Segment of first MCB
+psp_mcb		dw	?		; Segment of MCB of our PSP
+env_mcb		dw	?		; MCB of Environment segment
+noswap_mcb	dw	?		; Env MCB that may not be swapped
+		IF	NOT NO_INHERIT
+noswap_mcb2	dw	?		; Handle MCB that may not be swapped
+		ENDIF
+		IFDEF	PASCAL
+		IF	PAS_FREE
+pmemid		db	?
+pmempar		dw	?
+		ENDIF
+		ENDIF
+ems_pageframe	dw	?		; EMS page frame address
+handle		dw	?		; EMS/XMS/File handle
+total_mcbs	dw	?		; Total number of MCBs
+swapmethod	db	?		; Method for swapping
+swapfilename	db	81 dup(?)	; Swap file name if swapping to file
+prep_block	ends
+;
+;----------------------------------------------------------------------
+;
+;	Since we'll be moving code and data around in memory,
+;	we can't address locations in the resident block with
+;	normal address expressions. MASM does not support
+;	defining variables with a fixed offset, so we have to resort
+;	to a kludge, and define the shrunk-down code as a structure.
+;	It would also be possible to use an absolute segment for the
+;	definition, but this is not supported by the Turbo Pascal linker.
+;
+;	All references to low-core variables from low-core itself 
+;	are made through DS, so we define a text macro "lmem" that 
+;	expands to "ds:". When setting up low core from the normal
+;	code, ES is used to address low memory, so this can't be used.
+;
+lmem	equ	<ds:>
+;
+;	The memory structure for the shrunk-down code, excluding the
+;	code itself. The code follows this block.
+;	The start of this block is the PSP.
+;
+parseg		struc
+		db	18h dup(?)
+psp_handletab	db	20 dup(?)	; Handle Table
+psp_envptr	dw	?		; Environment Pointer
+		dd	?
+psp_handlenum	dw	?		; Number of Handles (DOS >= 3.3)
+psp_handleptro	dw	?		; Handle Table Pointer (DOS >= 3.3)
+psp_handleptrs	dw	?		; Handle Table Pointer Segment
+		db	5ch-38h dup(?)	; start after PSP
+;
+save_ss		dw	?		; 5C - saved global ss
+save_sp		dw	?		; 5E - saved global sp
+xfcb1		db	16 dup(?)	; 60..6F - default FCB
+xfcb2		db	16 dup(?)	; 70..7F - default FCB
+zero		dw	?		; 80 Zero command tail length (dummy)
+;
+expar		db	TYPE exec_block dup (?) ; exec-parameter-block
+spx		dw	?		; saved local sp
+div0_off	dw	?		; divide by zero vector save
+div0_seg	dw	?
+		IF	NO_INHERIT
+lhandlesave	db	26 dup(?)	; saved handle table and pointer
+		IF	REDIRECT
+lredirsav	db	6 dup(?)	; saved redirection handles
+		ENDIF
+		ENDIF
+		IF	REDIRECT
+lstdinsav	dw	3 dup(?)	; duped redirection handles
+		ENDIF
+filename	db	82 dup(?)	; exec filename
+progpars	db	128 dup(?)	; command tail
+		db	stacklen dup(?)	; local stack space
+mystack		db	?
+lprep		db	TYPE prep_block dup(?)	; the swapping variables
+lcurrdesc	db	TYPE mcbdesc dup(?)	; the current MCB descriptor
+lxmsctl		db	TYPE xms_control dup(?)
+eretcode	dw	?		; EXEC return code
+retflags	dw	?		; EXEC return flags
+cgetmcb		dw	?		; address of get_mcb
+;
+parseg	ends
+;
+param_len	=	((TYPE parseg + 1) / 2) * 2	; make even
+codebeg		=	param_len
+;
+	.code
+;
+;------------------------------------------------------------------------
+;
+lowcode_begin:
+;
+;       The following parts of the program code will be moved to
+;	low core and executed there, so there must be no absolute 
+;	memory references.
+;	The call to get_mcb must be made indirect, since the offset
+;	from the swap-in routine to get_mcb will not be the same
+;	after moving.
+;
+;
+;	get_mcb allocates a block of memory by modifying the MCB chain
+;	directly.
+;
+;	On entry, lcurrdesc has the mcb descriptor for the block to
+;		  allocate.
+;
+;	On exit,  Carry is set if the block couldn't be allocated.
+;
+;	Uses 	AX, BX, CX, ES
+;	Modifies lprep.first_mcb
+;
+get_mcb	proc	near
+;
+	mov	ax,lmem lprep.first_mcb
+	mov	bx,lmem lcurrdesc.addr
+;
+getmcb_loop:
+	mov	es,ax
+	cmp	ax,bx
+	ja	gmcb_abort		; halt if MCB > wanted
+	je	mcb_found		; jump if same addr as wanted
+	add	ax,es:paras		; last addr
+	inc	ax			; next mcb
+	cmp	ax,bx
+	jbe	getmcb_loop		; Loop if next <= wanted
+;
+;
+;	The wanted MCB starts within the current MCB. We now have to
+;	create a new MCB at the wanted position, which is initially
+;	free, and shorten the current MCB to reflect the reduced size.
+;
+	cmp	es:owner,0
+	jne	gmcb_abort		; halt if not free
+	mov	bx,es			; current
+	inc	bx			; + 1 (header doesn't count)
+	mov	ax,lmem lcurrdesc.addr
+	sub	ax,bx			; paragraphs between MCB and wanted
+	mov	bx,es:paras		; paras in current MCB
+	sub	bx,ax			; remaining paras
+	dec	bx			; -1 for header
+	mov	es:paras,ax		; set new size for current
+	mov	cl,es:id		; old id
+	mov	es:id,4dh		; set id: there is a next
+	mov	ax,lmem lcurrdesc.addr
+	mov	es,ax
+	mov	es:id,cl		; and init to free
+	mov	es:owner,0
+	mov	es:paras,bx
+;
+;	We have found an MCB at the right address. If it's not free,
+;	abort. Else check the size. If the size is ok, we're done 
+;	(more or less).
+;
+mcb_found:
+	mov	es,ax
+	cmp	es:owner,0
+	je	mcb_check		; continue if free
+;
+gmcb_abort:
+	stc
+	ret
+;
+mcb_check:
+	mov	ax,es:paras		; size
+	cmp	ax,lmem lcurrdesc.msize	; needed size
+	jae	mcb_ok			; ok if enough space
+;
+;	If there's not enough room in this MCB, check if the next
+;	MCB is free, too. If so, coalesce both MCB's and check again.
+;
+	cmp	es:id,4dh
+	jnz	gmcb_abort		; halt if no next
+	push	es			; save current
+	mov	bx,es
+	add	ax,bx
+	inc	ax			; next MCB
+	mov	es,ax
+	cmp	es:owner,0		; next free ?
+	jne	gmcb_abort		; halt if not
+	mov	ax,es:paras		; else load size
+	inc	ax			; + 1 for header
+	mov	cl,es:id		; and load ID
+	pop	es			; back to last MCB
+	add	es:paras,ax		; increase size
+	mov	es:id,cl		; and store ID
+	jmp	mcb_check		; now try again
+;
+;	The MCB is free and large enough. If it's larger than the
+;	wanted size, create another MCB after the wanted.
+;
+mcb_ok:
+	mov	bx,es:paras
+	sub	bx,lmem lcurrdesc.msize
+	jz	mcb_no_next		; ok, no next to create
+	push	es
+	dec	bx			; size of next block
+	mov	ax,es
+	add	ax,lmem lcurrdesc.msize
+	inc	ax			; next MCB addr
+	mov	cl,es:id		; id of this block
+	mov	es,ax			; address next
+	mov	es:id,cl		; store id
+	mov	es:paras,bx		; store size
+	mov	es:owner,0		; and mark as free
+	pop	es			; back to old MCB
+	mov	es:id,4dh		; mark next block present
+	mov	ax,lmem lcurrdesc.msize	; and set size to wanted
+	mov	es:paras,ax
+;
+mcb_no_next:
+	mov	es:owner,cx		; set owner to current PSP
+;
+;	Set the 'first_mcb' pointer to the current one, so we don't
+;	walk through all the previous blocks the next time.
+;	Also, check if the block we just allocated is the environment
+;	segment of the program. If so, restore the environment pointer
+;	in the PSP.
+;
+	mov	ax,es
+	mov	lmem lprep.first_mcb,ax
+	cmp	lmem lprep.env_mcb,ax
+	jne	getmcb_finis
+	inc	ax
+	mov	lmem psp_envptr,ax
+;
+getmcb_finis:
+	clc
+	ret				; all finished (whew!)
+;
+get_mcb	endp
+;
+;
+ireti:
+	iret
+;
+;
+;	The actual EXEC call.
+;	Registers on entry:
+;		BX	= paragraphs to keep (0 if no swap)
+;		CX 	= length of environment to copy (words) or zero
+;		DS:SI	= environment source
+;		ES:DI	= environment destination
+;		(ES = our low core code segment)
+;
+;
+;	copy environment buffer down if present
+;
+doexec:
+	jcxz	noenvcpy
+	rep movsw
+;
+noenvcpy:
+	push	es			; DS = ES = low core = PSP
+	pop	ds
+	or	bx,bx
+	jz	no_shrink
+;
+;	first, shrink the base memory block down.
+;
+        mov	ah,04ah
+	int     21h                     ; resize memory block
+;
+;	Again walk all MCBs. This time, all blocks owned by the 
+;	current process are released.
+;
+	mov	si,lmem lprep.first_mcb
+	or	si,si
+	jz	no_shrink
+	mov	dx,lmem lprep.psp_mcb
+	mov	bx,dx
+	inc	bx			; base PSP (MCB owner)
+	mov	di,lmem lprep.noswap_mcb
+;
+free_loop:
+	cmp	si,dx
+	je	free_next		; don't free base block
+	cmp	si,di
+	je	free_next
+
+	IF	NOT NO_INHERIT
+	cmp	si,lmem lprep.noswap_mcb2
+	je	free_next
+	ENDIF
+
+	mov	es,si
+	cmp	bx,es:owner		; our process?
+	jne	free_next		; next if not
+	cmp	si,lmem lprep.env_mcb	; is this the environment block?
+	jne	free_noenv
+	mov	ds:psp_envptr,0		; else clear PSP pointer
+;
+free_noenv:
+	inc	si
+	mov	es,si
+	dec	si
+	mov	ah,049h			; free memory block
+	int	21h
+;
+free_next:
+	mov	es,si
+	cmp	es:id,4dh		; normal block?
+	jne	free_ready		; ready if end of chain
+	add	si,es:paras		; start + length
+	inc	si			; next MCB
+	jmp	free_loop
+;
+free_ready:
+	mov	ax,ds
+	mov	es,ax
+;
+no_shrink:
+	mov	dx,filename		; params for exec
+	mov	bx,expar
+	mov	ax,04b00h
+	int	21h			; exec
+;
+;	Return from EXEC system call. Don't count on any register except
+;	CS to be restored (DOS 2.11 and previous versions killed all regs).
+;
+	mov	bx,cs
+	mov	ds,bx
+	mov	es,bx
+	cli
+	mov	ss,bx
+	mov	sp,lmem spx
+	sti
+	cld
+	mov	lmem eretcode,ax	; save return code
+	pushf
+	pop	bx
+	mov	lmem retflags,bx	; and returned flags
+;
+;	Cancel Redirection
+;
+	IF	REDIRECT
+	IF	NO_INHERIT
+	mov	si,lredirsav
+	mov	di,psp_handletab+5
+	mov	cx,3
+	rep movsw
+	ENDIF
+	mov	si,lstdinsav
+	xor	cx,cx
+;
+lredirclose:
+	lodsw
+	cmp	ax,-1
+	je	lredclosenext
+	mov	bx,ax
+	mov	ah,46h
+	int	21h
+;
+lredclosenext:
+	inc	cx
+	cmp	cx,3
+	jb	lredirclose
+	ENDIF
+;
+	cmp	lmem lprep.swapmethod,0
+	je	exec_memok
+	jg	exec_expand
+;
+;	Terminate.
+;
+	test	lmem retflags,1		; carry?
+	jnz	exec_term		; use EXEc retcode if set
+	mov	ah,4dh			; else get program return code
+	int	21h
+;
+exec_term:
+	mov	ah,4ch
+	int	21h
+;
+;
+exec_expand:
+	mov	ah,4ah			; expand memory
+	mov	bx,lmem lcurrdesc.msize
+	int	21h
+	jnc	exec_memok
+	mov	ax,4cffh
+	int	21h			; terminate on error
+;
+;	Swap memory back
+;
+	nop
+;
+exec_memok:
+;
+;	FALL THROUGH to the appropriate swap-in routine
+;
+;
+getmcboff	=	offset get_mcb - offset lowcode_begin
+iretoff		=	offset ireti - offset lowcode_begin
+doexec_entry	=	offset doexec - offset lowcode_begin
+base_length	=	offset $ - offset lowcode_begin
+;
+;-----------------------------------------------------------------------
+;
+;	The various swap in routines follow. Only one of the routines
+;	is copied to low memory.
+;	Note that the routines are never actually called, the EXEC return
+;	code falls through. The final RET thus will return to the restored
+;	memory image.
+;
+;	On entry, DS must point to low core.
+;	On exit to the restored code, DS is unchanged.
+;
+;
+;	swapin_ems:	swap in from EMS.
+;
+swapin_ems	proc	far
+;
+	xor	bx,bx
+	mov	si,ems_parasize
+	mov	dx,lmem lprep.handle	; EMS handle
+;
+swinems_main:
+	push	ds
+	mov	cx,lmem lcurrdesc.swsize	; block length in paras
+	mov	di,lmem lcurrdesc.swoffset	; swap offset
+	mov	es,lmem lcurrdesc.addr		; segment to swap
+	mov	ds,lmem lprep.ems_pageframe	; page frame address
+;
+	mov	ax,ems_parasize		; max length
+	sub	ax,si			; minus current offset
+	jnz	swinems_ok		; go copy if nonzero
+;
+swinems_loop:
+	mov	ax,4400h		; map in next page
+	int	EMM_INT
+	or	ah,ah
+	jnz	swinems_error
+	mov	si,0			; reset offset
+	inc	bx			; bump up page number
+	mov	ax,ems_parasize		; max length to copy
+;
+swinems_ok:
+	cmp	ax,cx			; length to copy
+	jbe	swinems_doit		; go do it if <= total length
+	mov	ax,cx			; else use total length
+;
+swinems_doit:
+	sub	cx,ax			; subtract copy length from total
+	push	cx			; and save
+	push	ax			; save the copy length in paras
+	push	si
+	push	di
+	mov	cl,3
+	shl	ax,cl			; convert to number of words (!)
+	inc	cl
+	shl	si,cl			; convert to byte address
+	mov	cx,ax
+	rep movsw
+	pop	di
+	pop	si
+	pop	cx			; copy length in paras
+	mov	ax,es
+	add	ax,cx			; add copy length to dest segment
+	add	si,cx			; and EMS page offset
+	mov	es,ax
+	pop	cx			; remaining length
+	or	cx,cx			; did we copy everything?
+	jnz	swinems_loop		; go loop if not
+;
+	pop	ds
+	cmp	lmem lcurrdesc.num_follow,0	; another MCB?
+	je	swinems_complete	; exit if not
+;
+;	Another MCB follows, read next mcb descriptor into currdesc
+;
+	cmp	si,ems_parasize
+	jb	swinems_nonewpage	; no new block needed
+	mov	ax,4400h		; map page, phys = 0
+	int	EMM_INT
+	or	ah,ah
+	jnz	swinems_error1
+	mov	si,0
+	inc	bx
+;
+swinems_nonewpage:
+	push	si
+	push	ds
+	mov	ax,ds
+	mov	es,ax
+	mov	ds,lmem lprep.ems_pageframe	; page frame address
+	mov	cl,4
+	shl	si,cl			; convert to byte address
+	mov	cx,TYPE mcbdesc
+	mov	di,lcurrdesc
+	rep movsb
+	pop	ds
+	pop	si
+	inc	si			; one paragraph
+;
+	push	bx
+	call	lmem cgetmcb
+	pop	bx
+	jc	swinems_error1
+	jmp	swinems_main
+;
+swinems_complete:
+	mov	ah,45h			; release EMS pages
+	int	EMM_INT
+	ret
+;
+swinems_error:
+	pop	ds
+swinems_error1:
+	mov	ah,45h			; release EMS pages on error
+	int	EMM_INT
+	mov	ax,4cffh
+	int	21h			; terminate
+;
+swapin_ems	endp
+;
+swinems_length	= offset $ - offset swapin_ems
+;
+;
+;	swapin_xms:	swap in from XMS.
+;
+swapin_xms	proc	far
+;
+	mov	ax,lmem lprep.handle	; XMS handle
+	mov	lmem lxmsctl.srchnd,ax 	; source is XMS
+	mov	lmem lxmsctl.desthnd,0 	; dest is normal memory
+	mov	lmem lxmsctl.srclo,0
+	mov	lmem lxmsctl.srchi,0
+;
+swinxms_main:
+	mov	ax,lmem lcurrdesc.swsize ; size in paragraphs
+	mov	cl,4
+	rol	ax,cl			; size in bytes + high nibble
+	mov	dx,ax
+	and	ax,0fff0h		; low word
+	and	dx,0000fh		; high word
+	mov	lmem lxmsctl.lenlo,ax	; into control block
+	mov	lmem lxmsctl.lenhi,dx
+	mov	ax,lmem lcurrdesc.swoffset	; swap offset
+	mov	lmem lxmsctl.destlo,ax 		; into control block
+	mov	ax,lmem lcurrdesc.addr		; segment to swap
+	mov	lmem lxmsctl.desthi,ax
+	mov	si,lxmsctl
+	mov	ah,0bh
+	call	lmem lprep.xmm		; move it
+	or	ax,ax
+	jz	swinxms_error
+	mov	ax,lmem lxmsctl.lenlo	; adjust source addr
+	add	lmem lxmsctl.srclo,ax
+	mov	ax,lmem lxmsctl.lenhi
+	adc	lmem lxmsctl.srchi,ax
+;
+	cmp	lmem lcurrdesc.num_follow,0	; another MCB?
+	je	swinxms_complete
+;
+	mov	lmem lxmsctl.lenlo,TYPE mcbdesc
+	mov	lmem lxmsctl.lenhi,0
+	mov	lmem lxmsctl.desthi,ds
+	mov	lmem lxmsctl.destlo,lcurrdesc
+	mov	si,lxmsctl
+	mov	ah,0bh
+	call	lmem lprep.xmm		; move it
+	or	ax,ax
+	jz	swinxms_error
+	add	lmem lxmsctl.srclo,16	; one paragraph
+	adc	lmem lxmsctl.srchi,0
+;
+	call	lmem cgetmcb
+	jc	swinxms_error
+	jmp	swinxms_main
+;
+swinxms_complete:
+	mov	ah,0ah			; release XMS frame
+	mov	dx,lmem lprep.handle   	; XMS handle
+	call	lmem lprep.xmm
+	ret
+;
+swinxms_error:
+	mov	ah,0ah			; release XMS frame on error
+	call	lmem lprep.xmm
+	mov	ax,4c00h
+	int	21h
+;
+swapin_xms	endp
+;
+swinxms_length	= offset $ - offset swapin_xms
+;
+;
+;	swapin_file:	swap in from file.
+;
+swapin_file	proc	far
+;
+	mov	dx,lprep.swapfilename
+	mov	ax,3d00h			; open file
+	int	21h
+	jc	swinfile_error2
+	mov	bx,ax				; file handle
+;
+swinfile_main:
+	push	ds
+	mov	cx,lmem lcurrdesc.swsize	; size in paragraphs
+	mov	dx,lmem lcurrdesc.swoffset	; swap offset
+	mov	ds,lmem lcurrdesc.addr		; segment to swap
+;
+swinfile_loop:
+	mov	ax,cx
+	cmp	ah,8h			; above 32k?
+	jbe	swinfile_ok		; go read if not
+	mov	ax,800h			; else read 32k
+;
+swinfile_ok:
+	sub	cx,ax			; remaining length
+	push	cx			; save it
+	push	ax			; and save paras to read
+	mov	cl,4
+	shl	ax,cl			; convert to bytes
+	mov	cx,ax
+	mov	ah,3fh			; read
+	int	21h
+	jc	swinfile_error
+	cmp	ax,cx
+	jne	swinfile_error
+	pop	cx			; paras read
+	mov	ax,ds
+	add	ax,cx			; bump up dest segment
+	mov	ds,ax
+	pop	cx			; remaining length
+	or	cx,cx			; anything left?
+	jnz	swinfile_loop		; go loop if yes
+;
+	pop	ds
+	cmp	lmem lcurrdesc.num_follow,0	; another MCB?
+	je	swinfile_complete	; ready if not
+	mov	cx,16			; read one paragraph
+	mov	dx,lcurrdesc
+	mov	ah,3fh
+	int	21h
+	jc	swinfile_error1
+	cmp	ax,cx
+	jne	swinfile_error1
+;
+	push	bx
+	call	lmem cgetmcb
+	pop	bx
+	jc	swinfile_error1
+	jmp	swinfile_main
+;
+;
+swinfile_complete:
+	mov	ah,3eh			; close file
+	int	21h
+	mov	dx,lprep.swapfilename
+	mov	ah,41h			; delete file
+	int	21h
+	ret
+;
+swinfile_error:
+	pop	cx
+	pop	cx
+	pop	ds
+swinfile_error1:
+	mov	ah,3eh			; close file
+	int	21h
+swinfile_error2:
+	mov	dx,lprep.swapfilename
+	mov	ah,41h			; delete file
+	int	21h
+	mov	ax,4cffh
+	int	21h
+;
+swapin_file	endp
+;
+swinfile_length	= offset $ - offset swapin_file
+;
+;
+;	swapin_none:	no swap, return immediately.
+;
+swapin_none	proc	far
+;
+	ret
+;
+swapin_none	endp
+;
+;
+	IF	swinems_length GT swinxms_length
+swcodelen	=	swinems_length
+	ELSE
+swcodelen	=	swinxms_length
+	ENDIF
+	IF	swinfile_length GT swcodelen
+swcodelen	=	swinfile_length
+	ENDIF
+;
+swap_codelen	=	((swcodelen + 1) / 2) * 2
+;
+codelen		=	base_length + swap_codelen
+reslen		=	codebeg + codelen
+keep_paras	=	(reslen + 15) shr 4	; paragraphs to keep
+swapbeg		=	keep_paras shl 4	; start of swap space
+savespace	=	swapbeg - 5ch	; length of overwritten area
+;
+;--------------------------------------------------------------------
+;
+	IFDEF	PASCAL
+	.data
+	extrn	swap_prep: prep_block
+	ELSE
+	IFDEF	TC_HUGE
+	.fardata?	my_data
+	assume	ds:my_data
+	ELSE
+	.data?
+	ENDIF
+	ENDIF
+;
+;
+;	Space for saving the part of the memory image below the
+;	swap area that is overwritten by our code.
+;
+save_dat	db	savespace dup(?)
+;
+;	Variables used while swapping out.
+;	The "swap_prep" structure is initialized by prep_swap.
+;
+	IFNDEF	PASCAL
+swap_prep	prep_block	<>
+	ENDIF
+nextmcb		mcbdesc		<>
+currdesc	mcbdesc		<>
+xmsctl		xms_control	<>
+ems_curpage	dw		?	; current EMS page number
+ems_curoff	dw		?	; current EMS offset (paragraph)
+;
+;--------------------------------------------------------------------
+;       
+	.code
+;
+;	swapout_ems:	swap out an MCB block to EMS.
+;
+;	Entry:	"currdesc" 	contains description of block to swap
+;		"nextmcb"	contains MCB-descriptor of next block
+;				if currdesc.num_follow is nonzero
+;
+;	Exit:	0 if OK, != 0 if error, Zero-flag set accordingly.
+;
+;	Uses:	All regs excpt DS
+;
+swapout_ems	proc	near
+;
+	push	ds
+	mov	cx,currdesc.swsize	; block length in paras
+	mov	si,currdesc.swoffset	; swap offset
+	mov	dx,swap_prep.handle	; EMS handle
+	mov	bx,ems_curpage		; current EMS page
+	mov	di,ems_curoff		; current EMS page offset (paras)
+	mov	es,swap_prep.ems_pageframe	; page frame address
+	mov	ds,currdesc.addr	; segment to swap
+;
+	mov	ax,ems_parasize		; max length
+	sub	ax,di			; minus current offset
+	jnz	swems_ok		; go copy if there's room
+;
+swems_loop:
+	mov	ax,4400h		; map in next page
+	int	EMM_INT
+	or	ah,ah
+	jnz	swems_error
+	mov	di,0			; reset offset
+	inc	bx			; bump up page number
+	mov	ax,ems_parasize		; max length to copy
+;
+swems_ok:
+	cmp	ax,cx			; length to copy
+	jbe	swems_doit		; go do it if <= total length
+	mov	ax,cx			; else use total length
+;
+swems_doit:
+	sub	cx,ax			; subtract copy length from total
+	push	cx			; and save
+	push	ax			; save the copy length in paras
+	push	si
+	push	di
+	mov	cl,3
+	shl	ax,cl			; convert to number of words (!)
+	inc	cl
+	shl	di,cl			; convert to byte address
+	mov	cx,ax
+	rep movsw
+	pop	di
+	pop	si
+	pop	cx			; copy length in paras
+	mov	ax,ds
+	add	ax,cx			; add copy length to source segment
+	add	di,cx			; and EMS page offset
+	mov	ds,ax
+	pop	cx			; remaining length
+	or	cx,cx			; did we copy everything?
+	jnz	swems_loop		; go loop if not
+;
+	pop	ds
+	cmp	currdesc.num_follow,0	; another MCB?
+	je	swems_complete		; exit if not
+;
+;	Another MCB follows, append nextmcb to save block.
+;
+	cmp	di,ems_parasize
+	jb	swems_nonewpage		; no new block needed
+	mov	ax,4400h		; map page, phys = 0
+	int	EMM_INT
+	or	ah,ah
+	jnz	swems_error1
+	mov	di,0
+	inc	bx
+;
+swems_nonewpage:
+	push	di
+	mov	cl,4
+	shl	di,cl			; convert to byte address
+	mov	cx,TYPE mcbdesc
+	mov	si,offset nextmcb
+	rep movsb
+	pop	di
+	inc	di			; one paragraph
+;
+swems_complete:
+	mov	ems_curpage,bx
+	mov	ems_curoff,di
+	xor	ax,ax
+	ret
+;
+swems_error:
+	pop	ds
+swems_error1:
+	mov	ah,45h			; release EMS pages on error
+	int	EMM_INT
+	mov	ax,RC_SWAPERROR
+	or	ax,ax
+	ret
+;
+swapout_ems	endp
+;
+;
+;	swapout_xms:	swap out an MCB block to XMS.
+;
+;	Entry:	"currdesc" 	contains description of block to swap
+;		"nextmcb"	contains MCB-descriptor of next block
+;				if currdesc.num_follow is nonzero
+;
+;	Exit:	0 if OK, -1 if error, Zero-flag set accordingly.
+;
+;	Uses:	All regs excpt DS
+;
+swapout_xms	proc	near
+;
+	push	ds
+	pop	es
+	mov	ax,currdesc.swsize	; size in paragraphs
+	mov	cl,4
+	rol	ax,cl			; size in bytes + high nibble
+	mov	dx,ax
+	and	ax,0fff0h		; low word
+	and	dx,0000fh		; high word
+	mov	xmsctl.lenlo,ax		; into control block
+	mov	xmsctl.lenhi,dx
+	mov	xmsctl.srchnd,0		; source is normal memory
+	mov	ax,currdesc.swoffset	; swap offset
+	mov	xmsctl.srclo,ax		; into control block
+	mov	ax,currdesc.addr	; segment to swap
+	mov	xmsctl.srchi,ax
+	mov	ax,swap_prep.handle	; XMS handle
+	mov	xmsctl.desthnd,ax
+	mov	si,offset xmsctl
+	mov	ah,0bh
+	call	swap_prep.xmm		; move it
+	or	ax,ax
+	jz	swxms_error
+	mov	ax,xmsctl.lenlo		; adjust destination addr
+	add	xmsctl.destlo,ax
+	mov	ax,xmsctl.lenhi
+	adc	xmsctl.desthi,ax
+;
+	cmp	currdesc.num_follow,0	; another MCB?
+	je	swxms_complete
+;
+	mov	xmsctl.lenlo,TYPE mcbdesc
+	mov	xmsctl.lenhi,0
+	mov	xmsctl.srchi,ds
+	mov	xmsctl.srclo,offset nextmcb
+	mov	si,offset xmsctl
+	mov	ah,0bh
+	call	swap_prep.xmm		; move it
+	or	ax,ax
+	jz	swxms_error
+	add	xmsctl.destlo,16	; one paragraph
+	adc	xmsctl.desthi,0
+;
+swxms_complete:
+	xor	ax,ax
+	ret
+;
+swxms_error:
+	mov	ah,0ah			; release XMS frame on error
+	mov	dx,swap_prep.handle	; XMS handle
+	call	swap_prep.xmm
+	mov	ax,RC_SWAPERROR
+	or	ax,ax
+	ret
+;
+swapout_xms	endp
+;
+;
+;	swapout_file:	swap out an MCB block to file.
+;
+;	Entry:	"currdesc" 	contains description of block to swap
+;		"nextmcb"	contains MCB-descriptor of next block
+;				if currdesc.num_follow is nonzero
+;
+;	Exit:	0 if OK, -1 if error, Zero-flag set accordingly.
+;
+;	Uses:	All regs excpt DS
+;
+swapout_file	proc	near
+;
+	push	ds
+	mov	cx,currdesc.swsize	; size in paragraphs
+	mov	bx,swap_prep.handle	; file handle
+	mov	dx,currdesc.swoffset	; swap offset
+	mov	ds,currdesc.addr	; segment to swap
+;
+swfile_loop:
+	mov	ax,cx
+	cmp	ah,8h			; above 32k?
+	jbe	swfile_ok		; go write if not
+	mov	ax,800h			; else write 32k
+;
+swfile_ok:
+	sub	cx,ax			; remaining length
+	push	cx			; save it
+	push	ax			; and save paras to write
+	mov	cl,4
+	shl	ax,cl			; convert to bytes
+	mov	cx,ax
+	mov	ah,40h			; write
+	int	21h
+	jc	swfile_error
+	cmp	ax,cx
+	jne	swfile_error
+	pop	cx			; paras written
+	mov	ax,ds
+	add	ax,cx			; bump up source segment
+	mov	ds,ax
+	pop	cx			; remaining length
+	or	cx,cx			; anything left?
+	jnz	swfile_loop		; go loop if yes
+;
+	pop	ds
+	cmp	currdesc.num_follow,0	; another MCB?
+	je	swfile_complete		; ready if not
+	mov	cx,16			; write one paragraph
+	mov	dx,offset nextmcb
+	mov	ah,40h
+	int	21h
+	jc	swfile_error1
+	cmp	ax,cx
+	jne	swfile_error1
+;
+swfile_complete:
+	xor	ax,ax
+	ret
+;
+swfile_error:
+	pop	cx
+	pop	cx
+	pop	ds
+swfile_error1:
+	mov	ah,3eh			; close file
+	int	21h
+	mov	dx,offset swap_prep.swapfilename
+	mov	ah,41h			; delete file
+	int	21h
+	mov	ax,RC_SWAPERROR
+	or	ax,ax
+	ret
+;
+swapout_file	endp
+;
+;--------------------------------------------------------------------------
+;
+	IF	REDIRECT
+;
+;	@redirect: Redirect a file.
+;
+;	Entry:	DS:SI = Filename pointer
+;		AX zero if filename is NULL
+;		CX    = Handle to redirect
+;		ES:DI = Handle save pointer
+;
+;	Exit:	Carry set on error, then AL has DOS error code
+;		ES:DI updated
+;
+;	Uses:	AX,BX,DX,SI
+;
+@redirect	proc	near
+		local	doserr
+;
+	or	ax,ax
+	jz	no_redirect
+	cmp	byte ptr [si],0
+	jne	do_redirect
+;
+no_redirect:
+	mov	ax,-1
+	stosw
+	ret
+;
+do_redirect:
+	IFDEF	PASCAL
+	inc	si			; skip length byte
+	ENDIF
+	or	cx,cx
+	jnz	redir_write
+	mov	dx,si
+	mov	ax,3d00h	; open file, read only
+	int	21h
+	mov	doserr,ax
+	jc	redir_failed
+;
+redir_ok:
+	mov	dx,ax
+	mov	ah,45h		; duplicate handle
+	mov	bx,cx
+	int	21h
+	mov	doserr,ax
+	jc	redir_failed_dup
+	push	ax
+	mov	bx,dx
+	mov	ah,46h		; force duplicate handle
+	int	21h
+	mov	doserr,ax
+	pop	ax
+	jc	redir_failed_force
+	stosw
+	mov	ah,3eh		; close file
+	int	21h
+	clc
+	ret
+;
+redir_failed_force:
+	mov	bx,ax
+	mov	ah,3eh		; close file
+	int	21h
+;
+redir_failed_dup:
+	mov	bx,dx
+	mov	ah,3eh		; close file
+	int	21h
+;
+redir_failed:
+	mov	ax,doserr
+	stc
+	ret
+;
+redir_write:
+	cmp	byte ptr [si],'>'
+	jne	no_append
+	inc	si
+	mov	dx,si
+	mov	ax,3d02h		; open file, read/write
+	int	21h
+	jc	no_append
+	mov	bx,ax
+	push	cx
+	mov	ax,4202h		; move file, offset from EOF
+	xor	cx,cx
+	mov	dx,cx
+	int	21h
+	mov	doserr,ax
+	pop	cx
+	mov	ax,bx
+	jnc	redir_ok
+	mov	dx,ax
+	jmp	redir_failed_dup
+;
+no_append:
+	mov	dx,si
+	mov	ah,3ch
+	push	cx
+	xor	cx,cx
+	int	21h
+	mov	doserr,ax
+	pop	cx
+	jc	redir_failed
+	jmp	redir_ok
+;
+@redirect	endp
+;
+	ENDIF
+;
+;--------------------------------------------------------------------------
+;
+	IFDEF	PASCAL
+	IF	PAS_FREE
+;
+;	@freepas: Chain unused Pascal Heap into DOS free list.
+;
+@freepas	proc	near
+;
+	mov	swap_prep.pmemid,0	; Init to no free block
+;
+;	Load pointer to end of unused heap. For TP 5.x, this is
+;	FreePtr (which has to be adjusted if the offset part is 0).
+;	For TP 6.x, it is HeapEnd.
+;	For both versions, there may be data located after this
+;	pointer, so we may have to create an MCB.
+;
+	IF	TPAS_6
+	mov	ax,word ptr HeapEnd
+	mov	dx,word ptr HeapEnd+2
+	ELSE
+	mov	ax,word ptr FreePtr
+	mov	dx,word ptr FreePtr+2
+	or	ax,ax			; offset 0?
+	jnz	freepok
+	add	dx,1000h		; adjust segment
+freepok:
+	ENDIF
+	mov	cl,4
+	shr	ax,cl
+	add	ax,dx		; FreeList/HeapEnd segment (rounded down)
+	dec	ax		; MCB addr FreeList/HeapEnd segment
+	mov	si,ax
+;
+	mov	bx,word ptr HeapPtr
+	mov	dx,word ptr HeapPtr+2
+	add	bx,0fh
+	shr	bx,cl
+	add	bx,dx		; heap end segment (rounded up)
+	mov	di,bx
+;
+	sub	ax,bx		; free heap size
+	jc	pnofree
+	cmp	ax,1024		; don't bother if less than 16k
+	jbe	pnofree
+;
+	mov	ax,PrefixSeg	; PSP
+	sub	bx,ax		; size of space in use
+	dec	ax		; MCB
+	mov	es,ax
+	inc	ax
+;
+;	Modify base MCB to contain only the part up to HeapPtr.
+;
+	mov	cl,es:id	; save MCB ID
+	mov	swap_prep.pmemid,cl
+	mov	es:id,'M'
+	mov	dx,es:paras
+	mov	swap_prep.pmempar,dx
+	mov	es:paras,bx
+;
+;	Insert new, free, MCB at address HeapPtr.
+;
+	mov	es,di		; HeapPtr
+	sub	dx,bx
+	dec	dx
+	mov	es:id,'M'
+	mov	es:owner,0
+	mov	bx,si		; HeapEnd/FreeList
+	sub	bx,di		; - HeapPtr
+	dec	bx		; - 1 (MCB)
+	mov	es:paras,bx
+;
+	sub	dx,bx
+	dec	dx
+	jnz	frnotempty
+	inc	es:paras
+	mov	es:id,cl
+	jmp	short pnofree
+;
+;	There is a non-empty block after HeapEnd/FreePtr. Insert an MCB
+;	with Owner = PSP.
+;
+frnotempty:
+	mov	es,si		; HeapEnd/FreeList
+	mov	es:id,cl
+	mov	es:owner,ax
+	mov	es:paras,dx
+;
+pnofree:
+	ret
+;
+@freepas	endp
+;
+;	@restpas - restore original memory layout modified by @freepas
+;	
+@restpas	proc	near
+;
+	mov	al,swap_prep.pmemid
+	or	al,al
+	jz	norestpas
+	mov	bx,PrefixSeg
+	dec	bx
+	mov	es,bx
+	mov	es:id,al
+	mov	ax,swap_prep.pmempar
+	mov	es:paras,ax
+;
+norestpas:
+	ret
+;
+@restpas	endp
+	ENDIF
+	ENDIF
+;
+;--------------------------------------------------------------------------
+;--------------------------------------------------------------------------
+;
+;
+	IFDEF	PASCAL
+	IFDEF	FARCALL
+do_spawn	PROC	far swapping: word, execfname: dword, params: dword, envlen: word, envp: dword, stdin: dword, stdout: dword, stderr: dword
+	ELSE
+do_spawn	PROC	near swapping: word, execfname: dword, params: dword, envlen: word, envp: dword, stdin: dword, stdout: dword, stderr: dword
+	ENDIF
+	ELSE
+do_spawn	PROC	uses si di,swapping: word, execfname:ptr byte,params:ptr byte,envlen:word,envp:ptr byte,stdin:ptr byte, stdout:ptr byte, stderr:ptr byte
+	ENDIF
+	local	datseg,pspseg,currmcb,dos33
+;
+	IFDEF	TC_HUGE
+	mov	ax,SEG my_data
+	mov	ds,ax
+	ENDIF
+;
+	mov	datseg,ds		; save default DS
+;
+	mov	dos33,0
+	mov	ax,3000h		; get DOS version
+	int	21h
+	cmp	al,3
+	jb	notdos33
+	ja	isdos33
+	cmp	ah,33
+	jb	notdos33
+isdos33:
+	mov	dos33,1
+notdos33:
+;
+	IFDEF	PASCAL
+	cld
+	mov	bx,prefixseg
+	ELSE
+	IFDEF	TC_HUGE
+	mov	ax,SEG _psp
+	mov	es,ax
+	mov	bx,es:_psp
+	ELSE
+	mov	bx,_psp
+	ENDIF
+	ENDIF
+	mov	pspseg,bx
+;
+;	Check if spawn is too low in memory
+;
+	mov	ax,cs
+	mov	dx,offset lowcode_begin
+	mov	cl,4
+	shr	dx,cl
+	add	ax,dx			; normalized start of this code
+	mov	dx,keep_paras		; the end of the modified area
+	add	dx,bx			; plus PSP = end paragraph
+	cmp	ax,dx
+	ja	doswap_ok	; ok if start of code > end of low mem
+	mov	ax,RC_TOOLOW
+	ret
+;
+doswap_ok:
+	cmp	swapping,0
+	jle	method_ok
+;
+;	check the swap method, to make sure prep_swap has been called
+;
+	mov	al,swap_prep.swapmethod
+	cmp	al,USE_EMS
+	je	method_ok
+	cmp	al,USE_XMS
+	je	method_ok
+	cmp	al,USE_FILE
+	je	method_ok
+	mov	ax,RC_BADPREP
+	ret
+;
+;	Save the memory below the swap space.
+;	We must do this before swapping, so the saved memory is
+;	in the swapped out image.
+;	Anything else we'd want to save on the stack or anywhere
+;	else in "normal" memory also has to be saved here, any
+;	modifications done to memory after the swap will be lost.
+;
+;	Note that the memory save is done even when not swapping,
+;	because we use some of the variables in low core for
+;	simplicity.
+;
+method_ok:
+	mov	es,datseg
+	mov	ds,pspseg		; DS points to PSP
+	mov	si,5ch
+	mov	di,offset save_dat
+	mov	cx,savespace / 2	; NOTE: savespace is always even
+	rep movsw
+;
+	mov	ds,datseg
+	IFDEF	PASCAL
+	IF	PAS_FREE
+	call	@freepas
+	ENDIF
+	ENDIF
+;
+	mov	ax,swapping
+	cmp	ax,0
+	jg	begin_swap
+;
+;	not swapping, prep_swap wasn't called. Init those variables in
+;  	the 'swap_prep' block we need in any case.
+;
+	mov	swap_prep.swapmethod,al
+	je	no_reduce
+;
+	mov	ax,pspseg
+	dec	ax
+	mov	swap_prep.psp_mcb,ax
+	mov	swap_prep.first_mcb,ax
+	inc	ax
+	mov	es,ax
+	mov	bx,es:psp_envptr
+	mov	swap_prep.env_mcb,bx
+	mov	swap_prep.noswap_mcb,0
+	cmp	envlen,0
+	jne	swp_can_swap_env
+	mov	swap_prep.noswap_mcb,bx
+;
+swp_can_swap_env:
+	IF	NOT NO_INHERIT
+	mov	swap_prep.noswap_mcb2,0
+	cmp	dos33,0
+	je	swp_no_exthandle
+	mov	ax,es:psp_handleptrs
+	mov	bx,es
+	cmp	ax,bx
+	je	swp_no_exthandle
+	dec	ax
+	mov	swap_prep.noswap_mcb2,ax
+;
+swp_no_exthandle:
+	ENDIF
+	xor	bx,bx
+	mov	es,bx
+	mov	ah,52h			; get list of lists
+	int	21h
+	mov	ax,es
+	or	ax,bx
+	jz	no_reduce
+	mov	es,es:[bx-2]		; first MCB
+	cmp	es:id,4dh		; normal ID?
+	jne	no_reduce
+	mov	swap_prep.first_mcb,es
+;
+no_reduce:
+	jmp	no_swap1
+;
+;	set up first block descriptor
+;
+begin_swap:
+	mov	ax,swap_prep.first_mcb
+	mov	currmcb,ax
+	mov	es,swap_prep.psp_mcb	; let ES point to base MCB
+	mov	ax,es:paras
+	mov	currdesc.msize,ax
+	sub	ax,keep_paras
+	mov	currdesc.swsize,ax
+	mov	currdesc.addr,es
+	mov	currdesc.swoffset,swapbeg + 16
+;		NOTE: swapbeg is 1 para higher when seen from MCB
+	mov	ax,swap_prep.total_mcbs
+	mov	currdesc.num_follow,ax
+;
+;	init other vars
+;
+	mov	xmsctl.destlo,0
+	mov	xmsctl.desthi,0
+	mov	ems_curpage,0
+	mov	ems_curoff,ems_parasize
+;
+;	Do the swapping. Each MCB block (except the last) has an 
+;	"mcbdesc" structure appended that gives location and size 
+;	of the next MCB.
+;
+swapout_main:
+	cmp	currdesc.num_follow,0	; next block?
+	je	swapout_no_next		; ok if not
+;
+;	There is another MCB block to be saved. So we don't have
+;	to do two calls to the save routine with complicated
+;	parameters, we set up the next MCB descriptor beforehand.
+;	Walk the MCB chain starting at the current MCB to find
+;	the next one belonging to this process.
+;
+	mov	ax,currmcb
+	mov	bx,pspseg
+	mov	cx,swap_prep.psp_mcb
+	mov	dx,swap_prep.noswap_mcb
+;
+swm_mcb_walk:
+	mov	es,ax
+	cmp	ax,cx
+	je	swm_next_mcb
+	cmp	ax,dx
+	je	swm_next_mcb
+	IF	NOT NO_INHERIT
+	cmp	ax,swap_prep.noswap_mcb2
+	je	swm_next_mcb
+	ENDIF
+;
+	cmp	bx,es:owner		; our process?
+	je	swm_mcb_found		; found it if yes
+;
+swm_next_mcb:
+	cmp	es:id,4dh		; normal block?
+	jne	swm_mcb_error		; error if end of chain
+	add	ax,es:paras		; start + length
+	inc	ax			; next MCB
+	jmp	swm_mcb_walk
+;
+;	MCB found, set up an mcbdesc in the "nextmcb" structure
+;
+swm_mcb_found:
+	mov	nextmcb.addr,es
+	mov	ax,es:paras		; get number of paragraphs
+	mov	nextmcb.msize,ax	; and save
+	inc	ax
+	mov	nextmcb.swsize,ax
+	mov	bx,es
+	add	bx,ax
+	mov	currmcb,bx
+	mov	nextmcb.swoffset,0
+	mov	ax,currdesc.num_follow
+	dec	ax
+	mov	nextmcb.num_follow,ax
+;
+swapout_no_next:
+	cmp	swap_prep.swapmethod,USE_EMS
+	je	swm_ems
+	cmp	swap_prep.swapmethod,USE_XMS
+	je	swm_xms
+	call	swapout_file
+	jmp	short swm_next
+;
+swm_ems:
+	call	swapout_ems
+	jmp	short swm_next
+;
+swm_xms:
+	call	swapout_xms
+;
+swm_next:
+	jnz	swapout_error
+	cmp	currdesc.num_follow,0
+	je	swapout_complete
+;
+;	next MCB exists, copy the "nextmcb" descriptor into
+;	currdesc, and loop.
+;
+	mov	es,datseg
+	mov	si,offset nextmcb
+	mov	di,offset currdesc
+	mov	cx,TYPE mcbdesc
+	rep movsb
+	jmp	swapout_main
+;
+;
+swm_mcb_error:
+	mov	ax,RC_MCBERROR
+;
+swapout_kill:
+	cmp	swapping,0
+	jl	swapout_error
+	push	ax
+	cmp	swap_prep.swapmethod,USE_FILE
+	je	swm_mcberr_file
+	cmp	swap_prep.swapmethod,USE_EMS
+	je	swm_mcberr_ems
+;
+	mov	ah,0ah			; release XMS frame on error
+	mov	dx,swap_prep.handle	; XMS handle
+	call	swap_prep.xmm
+	pop	ax
+	jmp	short swapout_error
+;
+swm_mcberr_ems:
+	mov	dx,swap_prep.handle	; EMS handle
+	mov	ah,45h			; release EMS pages on error
+	int	EMM_INT
+	pop	ax
+	jmp	short swapout_error
+;
+swm_mcberr_file:
+	mov	bx,swap_prep.handle
+	cmp	bx,-1
+	je	swm_noclose
+	mov	ah,3eh			; close file
+	int	21h
+swm_noclose:
+	mov	dx,offset swap_prep.swapfilename
+	mov	ah,41h			; delete file
+	int	21h
+	pop	ax
+;
+swapout_error:
+	IFDEF	PASCAL
+	IF	PAS_FREE
+	call	@restpas
+	ENDIF
+	ENDIF
+	ret
+;
+;
+;	Swapout complete. Close the handle (EMS/file only),
+;	then set up low memory.
+;
+swapout_complete:
+	cmp	swap_prep.swapmethod,USE_FILE
+	jne	swoc_nofile
+;
+;	File swap: Close the swap file to make the handle available
+;
+	mov	bx,swap_prep.handle
+	mov	swap_prep.handle,-1
+	mov	ah,3eh
+	int	21h			; close file
+	mov	si,offset swapin_file
+	jnc	swoc_ready
+	mov	ax,RC_SWAPERROR
+	jmp	swapout_kill
+;
+swoc_nofile:
+	cmp	swap_prep.swapmethod,USE_EMS
+	jne	swoc_xms
+;
+;	EMS: Unmap page
+;
+	mov	ax,4400h
+	mov	bx,-1
+	mov	dx,swap_prep.handle
+	int	EMM_INT
+	mov	si,offset swapin_ems
+	jmp	short swoc_ready
+;
+swoc_xms:
+	mov	si,offset swapin_xms
+	jmp	short swoc_ready
+;
+no_swap1:
+	mov	si,offset swapin_none
+;	
+;	Copy the appropriate swap-in routine to low memory.
+;
+swoc_ready:
+	mov	es,pspseg
+	mov	cx,swap_codelen / 2
+	mov	di,codebeg + base_length
+	push	ds
+	mov	ax,cs
+	mov	ds,ax
+	rep movsw
+;
+;	And while we're at it, copy the MCB allocation routine (which
+;	also includes the initial MCB release and exec call) down.
+;
+	mov	cx,base_length / 2
+	mov	di,param_len
+	mov	si,offset lowcode_begin
+	rep movsw
+;
+	pop	ds
+	mov	bx,es
+	dec	bx
+	mov	es,bx		; let ES point to base MCB
+;
+;	Again set up the base MCB descriptor, and copy it as well as
+;	the variables set up by prep_swap to low memory.
+;	This isn't too useful if we're not swapping, but it doesn't
+;	hurt, either. The only variable used when not swapping is
+;	lprep.swapmethod.
+;
+	mov	ax,es:paras
+	mov	currdesc.msize,ax
+	sub	ax,keep_paras
+	mov	currdesc.swsize,ax
+	mov	currdesc.addr,es
+	mov	currdesc.swoffset,swapbeg + 16
+	mov	ax,swap_prep.total_mcbs
+	mov	currdesc.num_follow,ax
+;
+	mov	es,pspseg		; ES points to PSP again
+;
+	mov	cx,TYPE prep_block
+	mov	si,offset swap_prep
+	mov	di,lprep
+	rep movsb
+	mov	cx,TYPE mcbdesc
+	mov	si,offset currdesc
+	mov	di,lcurrdesc
+	rep movsb
+;
+;	now set up other variables in low core
+;
+	mov	ds,pspseg
+	mov	ds:cgetmcb,getmcboff + codebeg
+	mov	ds:eretcode,0
+	mov	ds:retflags,0
+;
+;
+;	If 'NO_INHERIT' is nonzero, save the entries of the 
+;	handle table, and set the last 15 to 0ffh (unused).
+;
+	IF	NO_INHERIT
+	mov	si,psp_handletab
+	mov	di,lhandlesave
+	mov	cx,10
+	rep movsw
+	mov	si,psp_handlenum	; Length of handle table
+	mov	ax,[si]
+	stosw
+	mov	word ptr [si],20	; set to default to be safe
+	add	si,2
+	lodsw				; Handle table pointer
+	mov	bx,ax
+	stosw
+	lodsw
+	stosw
+	cmp	dos33,0
+	je	no_handlecopy
+	cmp	ax,pspseg
+	jne	copy_handles
+	cmp	bx,psp_handletab
+	je	no_handlecopy
+;
+;	if the handle table pointer in the PSP does not point to
+;	the default PSP location, copy the first five entries from
+;	this table into the PSP - but only if we have DOS >= 3.3.
+;
+copy_handles:
+	mov	ds,ax
+	mov	si,bx
+	mov	di,psp_handletab
+	mov	es:psp_handleptro,di
+	mov	es:psp_handleptrs,es
+	movsw
+	movsw
+	movsb
+;
+no_handlecopy:
+	mov	di,psp_handletab+5
+	mov	ax,0ffffh
+	stosb
+	mov	cx,7
+	rep stosw
+;
+	ENDIF
+;
+;	Handle Redirection
+;
+	IF	REDIRECT
+	mov	es,pspseg
+	mov	di,lstdinsav
+	mov	ax,-1
+	stosw
+	stosw
+	stosw
+	mov	di,lstdinsav
+	xor	cx,cx
+	IF	ptrsize
+	lds	si,stdin
+	mov	ax,ds
+	or	ax,si
+	ELSE
+	mov	si,stdin
+	mov	ds,datseg
+	or	si,si
+	ENDIF
+	call	@redirect
+	jc	failed_redir
+	inc	cx
+	IF	ptrsize
+	lds	si,stdout
+	mov	ax,ds
+	or	ax,si
+	ELSE
+	mov	si,stdout
+	or	si,si
+	ENDIF
+	call	@redirect
+	jc	failed_redir
+	inc	cx
+	IF	ptrsize
+	lds	si,stderr
+	mov	ax,ds
+	or	ax,si
+	ELSE
+	mov	si,stderr
+	or	si,si
+	ENDIF
+	call	@redirect
+	jnc	redir_complete
+;
+failed_redir:
+	push	ax
+;
+;	restore handle table and pointer
+;
+	mov	ds,pspseg
+	mov	si,lstdinsav
+	xor	cx,cx
+;
+redirclose:
+	lodsw
+	cmp	ax,-1
+	je	redclosenext
+	mov	bx,ax
+	mov	ah,46h
+	int	21h
+;
+redclosenext:
+	inc	cx
+	cmp	cx,3
+	jb	redirclose
+;
+	IF	NO_INHERIT
+	mov	ds,pspseg
+	mov	es,pspseg
+	mov	si,lhandlesave
+	mov	di,psp_handletab
+	mov	cx,10
+	rep movsw
+	mov	di,psp_handlenum
+	movsw
+	movsw
+	movsw
+	ENDIF
+;
+;	Restore overwritten part of program
+;
+	mov	ds,datseg
+	mov	es,pspseg
+	mov	si,offset save_dat
+	mov	di,5ch
+	mov	cx,savespace
+	rep movsb
+;
+	pop	ax
+	mov	ah,RC_REDIRFAIL SHR 8
+	jmp	swapout_kill
+;
+redir_complete:
+	IF	NO_INHERIT
+	mov	ds,pspseg
+	mov	es,pspseg
+	mov	si,psp_handletab+5
+	mov	di,lredirsav
+	mov	cx,3
+	rep movsw
+	mov	di,psp_handletab+5
+	mov	cx,3
+	mov	ax,0ffffh
+	rep stosw
+	ENDIF
+	ENDIF
+;
+;	Prepare exec parameter block
+;
+	mov	ax,es
+	mov	es:expar.fcb1seg,ax
+	mov	es:expar.fcb2seg,ax
+	mov	es:expar.pparseg,ax
+	mov	es:expar.envseg,0
+;
+;	The 'zero' word is located at 80h in the PSP, the start of
+;	the command line. So as not to confuse MCB walking programs,
+;	a command line length of zero is inserted here.
+;
+	mov	es:zero,0d00h		; 00h,0dh = empty command line
+;
+;	Init default fcb's by parsing parameter string
+;
+	IF	ptrsize
+	lds	si,params
+	ELSE
+	mov	si,params
+	mov	ds,datseg
+	ENDIF
+	IFDEF	PASCAL
+	inc	si			; skip length byte
+	ENDIF
+	push	si
+	mov	di,xfcb1
+	mov	es:expar.fcb1,di
+	push	di
+	mov	cx,16
+	xor	ax,ax
+	rep stosw			; init both fcb's to 0
+	pop	di
+	mov	ax,2901h
+	int	21h
+	mov	di,xfcb2
+	mov	es:expar.fcb2,di
+	mov	ax,2901h
+	int	21h
+	pop	si
+;
+;	move command tail string into low core
+;
+	mov	di,progpars
+	mov	es:expar.ppar,di
+	xor	cx,cx
+	inc	di
+cmdcpy:
+	lodsb
+	or	al,al
+	jz	cmdcpy_end
+	stosb
+	inc	cx
+	jmp	cmdcpy
+;
+cmdcpy_end:
+	mov	al,0dh
+	stosb
+	mov	es:progpars,cl
+;
+;	move filename string into low core
+;
+	IF	ptrsize
+	lds	si,execfname
+	ELSE
+	mov	si,execfname
+	ENDIF
+	IFDEF	PASCAL
+	inc	si
+	ENDIF
+	mov	di,filename
+fncpy:
+	lodsb
+	stosb
+	or	al,al
+	jnz	fncpy
+;
+;	Setup environment copy
+;
+	mov	bx,keep_paras		; paras to keep
+	mov	cx,envlen		; environment size
+	jcxz	no_environ		; go jump if no environment
+	cmp	swapping,0
+	jne	do_envcopy
+;
+;	Not swapping, use the environment pointer directly.
+;	Note that the environment copy must be paragraph aligned.
+;
+	IF	ptrsize
+	mov	ax,word ptr (envp)+2
+	mov	bx,word ptr (envp)
+	ELSE
+	mov	ax,ds
+	mov	bx,envp
+	ENDIF
+	add	bx,15			; make sure it's paragraph aligned
+	mov	cl,4
+	shr	bx,cl			; and convert to segment addr
+	add	ax,bx
+	mov	es:expar.envseg,ax	; new environment segment
+	xor	cx,cx			; mark no copy
+	xor	bx,bx			; and no shrink
+	jmp	short no_environ
+;
+;	Swapping or EXECing without return. Set up the pointers for
+;	an environment copy (we can't do the copy yet, it might overwrite
+;	this code).
+;
+do_envcopy:
+	inc	cx
+	shr	cx,1			; words to copy
+	mov	ax,cx			; convert envsize to paras
+	add	ax,7
+	shr	ax,1
+	shr	ax,1
+	shr	ax,1
+	add	bx,ax			; add envsize to paras to keep
+	IF	ptrsize
+	lds	si,envp
+	ELSE
+	mov	si,envp
+	ENDIF
+;
+	mov	ax,es			; low core segment
+	add	ax,keep_paras		; plus fixed paras
+	mov	es:expar.envseg,ax	; = new environment segment
+;
+;	Save stack regs, switch to local stack
+;
+no_environ:
+	mov	es:save_ss,ss
+	mov	es:save_sp,sp
+	mov	ax,es
+	cli
+	mov	ss,ax
+	mov	sp,mystack
+	sti
+;
+	push	cx			; save env length
+	push	si			; save env pointer
+	push	ds			; save env segment
+;
+;	save and patch INT0 (division by zero) vector
+;
+	xor	ax,ax
+	mov	ds,ax
+	mov	ax,word ptr ds:0
+	mov	es:div0_off,ax
+	mov	ax,word ptr ds:2
+	mov	es:div0_seg,ax
+	mov	word ptr ds:0,codebeg + iretoff
+	mov	word ptr ds:2,es
+;
+	pop	ds			; pop environment segment
+	pop	si			; pop environment offset
+	pop	cx			; pop environment length
+	mov	di,swapbeg		; environment destination
+;
+;	Push return address on local stack
+;
+	push	cs			; push return segment
+	mov	ax,offset exec_cont
+	push	ax			; push return offset
+	mov	es:spx,sp		; save stack pointer
+;
+;	Goto low core code
+;
+	push	es			; push entry segment
+        mov	ax,codebeg + doexec_entry
+        push	ax			; push entry offset
+;	ret	far			; can't use RET here because
+	db	0cbh			; of .model
+;
+;----------------------------------------------------------------
+;
+;	Low core code will return to this location, with DS set to
+;	the PSP segment.
+;
+exec_cont:
+	push	ds
+	pop	es
+	cli
+	mov	ss,ds:save_ss		; reload stack
+	mov	sp,ds:save_sp
+	sti
+;
+;	restore handle table and pointer
+;
+	IF	NO_INHERIT
+	mov	si,lhandlesave
+	mov	di,psp_handletab
+	mov	cx,10
+	rep movsw
+	mov	di,psp_handlenum
+	movsw
+	movsw
+	movsw
+	ENDIF
+;
+;	restore INT0 (division by zero) vector
+;
+	xor	cx,cx
+	mov	ds,cx
+	mov	cx,es:div0_off
+	mov	word ptr ds:0,cx
+	mov	cx,es:div0_seg
+	mov	word ptr ds:2,cx
+;
+	mov	ds,datseg
+;
+	IFDEF	PASCAL
+	IF	PAS_FREE
+	push	es
+	call	@restpas
+	pop	es
+	ENDIF
+	ENDIF
+;
+	mov	ax,es:eretcode
+	mov	bx,es:retflags
+;
+;	Restore overwritten part of program
+;
+	mov	si,offset save_dat
+	mov	di,5ch
+	mov	cx,savespace
+	rep movsb
+;
+	test	bx,1			; carry set?
+	jnz	exec_fault		; return EXEC error code if fault
+	mov	ah,4dh			; else get program return code
+	int	21h
+	ret
+;
+exec_fault:
+	mov	ah,3			; return error as 03xx
+	ret
+;	
+do_spawn	ENDP
+;
+;----------------------------------------------------------------------------
+;----------------------------------------------------------------------------
+;
+emm_name	db	'EMMXXXX0'
+;
+;	prep_swap - prepare for swapping.
+;
+;	This routine checks all parameters necessary for swapping,
+;	and attempts to set up the swap-out area in EMS/XMS, or on file.
+;	In detail:
+;
+;	     1) Check whether the do_spawn routine is located
+;		too low in memory, so it would get overwritten.
+;		If this is true, return an error code (-2).
+;
+;	     2) Walk the memory control block chain, adding up the
+;		paragraphs in all blocks assigned to this process.
+;
+;	     3) Check EMS (if the method parameter allows EMS):
+;		- is an EMS driver installed?
+;		- are sufficient EMS pages available?
+;		if all goes well, the EMS pages are allocated, and the
+;		routine returns success (1).
+;
+;	     4) Check XMS (if the method parameter allows XMS):
+;		- is an XMS driver installed?
+;		- is a sufficient XMS block available?
+;		if all goes well, the XMS block is allocated, and the
+;		routine returns success (2).
+;
+;	     5) Check file swap (if the method parameter allows it):
+;		- try to create the file
+;		- pre-allocate the file space needed by seeking to the end
+;		  and writing a byte.
+;		If the file can be written, the routine returns success (4).
+;
+;	     6) Return an error code (-1).
+;
+	IFDEF	PASCAL
+	IFDEF	FARCALL
+prep_swap	PROC	far pmethod: word, swapfname: dword
+	ELSE
+prep_swap	PROC	near pmethod: word, swapfname: dword
+	ENDIF
+	ELSE
+prep_swap	PROC	uses si di,pmethod:word,swapfname:ptr byte
+	ENDIF
+	LOCAL	totparas: word
+;
+	IFDEF	TC_HUGE
+	mov	ax,SEG my_data
+	mov	ds,ax
+	ENDIF
+;
+	IFDEF	PASCAL
+	cld
+	mov	ax,prefixseg
+	ELSE
+	IFDEF	TC_HUGE
+	mov	ax,SEG _psp
+	mov	es,ax
+	mov	ax,es:_psp
+	ELSE
+	mov	ax,_psp
+	ENDIF
+	ENDIF
+;
+	dec	ax
+	mov	swap_prep.psp_mcb,ax
+	mov	swap_prep.first_mcb,ax	; init first MCB to PSP
+;
+;	Make a copy of the environment pointer in the PSP
+;
+	inc	ax
+	mov	es,ax
+	mov	bx,es:psp_envptr
+	dec	bx
+	mov	swap_prep.env_mcb,bx
+	mov	swap_prep.noswap_mcb,0
+	test	pmethod,DONT_SWAP_ENV
+	jz	can_swap_env
+	mov	swap_prep.noswap_mcb,bx
+;
+can_swap_env:
+	IF	NOT NO_INHERIT
+	push	ax
+	mov	swap_prep.noswap_mcb2,0
+	mov	ax,3000h
+	int	21h
+	cmp	al,3
+	jb	no_exthandle
+	ja	check_exthandle
+	cmp	ah,33
+	jb	no_exthandle
+;
+check_exthandle:
+	mov	ax,es:psp_handleptrs
+	mov	bx,es
+	cmp	ax,bx
+	je	no_exthandle
+	dec	ax
+	mov	swap_prep.noswap_mcb2,ax
+;
+no_exthandle:
+	pop	ax
+	ENDIF
+;
+;	Check if spawn is too low in memory
+;
+	mov	bx,cs
+	mov	dx,offset lowcode_begin
+	mov	cl,4
+	shr	dx,cl
+	add	bx,dx			; normalized start of this code
+	mov	dx,keep_paras		; the end of the modified area
+	add	dx,ax			; plus PSP = end paragraph
+	cmp	bx,dx
+	ja	prepswap_ok	; ok if start of code > end of low mem
+	mov	ax,-2
+	mov	swap_prep.swapmethod,al
+	ret
+;
+;	Walk the chain of memory blocks, adding up the paragraphs
+;	in all blocks belonging to this process.
+;	We try to find the first MCB by getting DOS's "list of lists",
+;	and fetching the word at offset -2 of the returned address.
+;	If this fails, we use our PSP as the starting point.
+;
+prepswap_ok:
+	IFDEF	PASCAL
+	IF	PAS_FREE
+	call	@freepas
+	ENDIF
+	ENDIF
+	xor	bx,bx
+	mov	es,bx
+	mov	ah,52h			; get list of lists
+	int	21h
+	mov	ax,es
+	or	ax,bx
+	jz	prep_no_first
+	mov	es,es:[bx-2]		; first MCB
+	cmp	es:id,4dh		; normal ID?
+	jne	prep_no_first
+	mov	swap_prep.first_mcb,es
+;
+prep_no_first:
+	mov	es,swap_prep.psp_mcb	; ES points to base MCB
+	mov	cx,es			; save this value
+	mov	bx,es:owner		; the current process
+	mov	dx,es:paras		; memory size in the base block
+	sub	dx,keep_paras		; minus resident paragraphs
+	mov	si,0			; number of MCBs except base
+	mov	di,swap_prep.noswap_mcb
+	mov	ax,swap_prep.first_mcb
+	mov	swap_prep.first_mcb,0
+;
+prep_mcb_walk:
+	mov	es,ax
+	cmp	ax,cx			; base block?
+	je	prep_walk_upd_first	; then don't count again, but update first
+	cmp	ax,di			; Non-swap MCB?
+	je	prep_walk_next		; then don't count
+	IF	NOT NO_INHERIT
+	cmp	ax,swap_prep.noswap_mcb2
+	je	prep_walk_next
+	ENDIF
+;
+	cmp	bx,es:owner		; our process?
+	jne	prep_walk_next		; next if not
+	inc	si
+	mov	ax,es:paras		; else get number of paragraphs
+	add	ax,2			; + 1 for descriptor + 1 for MCB
+	add	dx,ax			; total number of paras
+prep_walk_upd_first:
+	cmp	swap_prep.first_mcb,0
+	jne	prep_walk_next
+	mov	swap_prep.first_mcb,es
+;
+prep_walk_next:
+	cmp	es:id,4dh		; normal block?
+	jne	prep_mcb_ready		; ready if end of chain
+	mov	ax,es
+	add	ax,es:paras		; start + length
+	inc	ax			; next MCB
+	jmp	prep_mcb_walk
+;
+prep_mcb_ready:
+	mov	totparas,dx
+	mov	swap_prep.total_mcbs,si
+;
+	IFDEF	PASCAL
+	IF	PAS_FREE
+	call	@restpas
+	ENDIF
+	ENDIF
+;
+	test	pmethod,XMS_FIRST
+	jnz	check_xms
+;
+;	Check for EMS swap
+;
+check_ems:
+	test	pmethod,USE_EMS
+	jz	prep_no_ems
+;
+	push	ds
+	mov	al,EMM_INT
+	mov	ah,35h
+	int	21h			; get EMM int vector
+	mov	ax,cs
+	mov	ds,ax
+	mov	si,offset emm_name
+	mov	di,10
+	mov	cx,8
+	repz cmpsb			; EMM name present?
+	pop	ds
+	jnz	prep_no_ems
+;
+	mov	ah,40h			; get EMS status
+	int	EMM_INT
+	or	ah,ah			; EMS ok?
+	jnz	prep_no_ems
+;
+	mov	ah,46h			; get EMS version
+	int	EMM_INT
+	or	ah,ah			; AH must be 0
+	jnz	prep_no_ems
+;
+	cmp	al,30h			; >= version 3.0?
+	jb	prep_no_ems
+;
+	mov	ah,41h			; Get page frame address
+	int	EMM_INT
+	or	ah,ah
+	jnz	prep_no_ems
+;
+;	EMS present, try to allocate pages
+;
+	mov	swap_prep.ems_pageframe,bx
+	mov	bx,totparas
+	add	bx,ems_paramask
+	mov	cl,ems_shift
+	shr	bx,cl
+	mov	ah,43h			; allocate handle and pages
+	int	EMM_INT
+	or	ah,ah			; success?
+	jnz	prep_no_ems
+;
+;	EMS pages allocated, swap to EMS
+;
+	mov	swap_prep.handle,dx
+	mov	ax,USE_EMS
+	mov	swap_prep.swapmethod,al
+	ret
+;
+;	No EMS allowed, or EMS not present/full. Try XMS.
+;
+prep_no_ems:
+	test	pmethod,XMS_FIRST
+	jnz	check_file		; don't try again
+;
+check_xms:
+	test	pmethod,USE_XMS
+	jz	prep_no_xms
+;
+	mov	ax,4300h		; check if XMM driver present
+	int	2fh
+	cmp	al,80h			; is XMM installed?
+	jne	prep_no_xms
+	mov	ax,4310h		; get XMM entrypoint
+	int	2fh
+	mov	word ptr swap_prep.xmm,bx	; save entry address
+	mov	word ptr swap_prep.xmm+2,es
+;
+	mov	dx,totparas
+	add	dx,xms_paramask		; round to nearest multiple of 1k
+	mov	cl,xms_shift
+	shr	dx,cl			; convert to k
+	mov	ah,9			; allocate extended memory block
+	call	swap_prep.xmm
+	or	ax,ax
+	jz	prep_no_xms
+;
+;	XMS block allocated, swap to XMS
+;
+	mov	swap_prep.handle,dx
+	mov	ax,USE_XMS
+	mov	swap_prep.swapmethod,al
+	ret
+;
+;	No XMS allowed, or XMS not present/full. Try File swap.
+;
+prep_no_xms:
+	test	pmethod,XMS_FIRST
+	jz	check_file
+	jmp	check_ems
+;
+check_file:
+	test	pmethod,USE_FILE
+	jnz	prep_do_file
+	jmp	prep_no_file
+;
+prep_do_file:
+	push	ds
+	IF	ptrsize
+	lds	dx,swapfname
+	ELSE
+	mov	dx,swapfname
+	ENDIF
+	IFDEF	PASCAL
+	inc	dx			; skip length byte
+	ENDIF
+	mov	cx,2			; hidden attribute
+	test	pmethod,HIDE_FILE
+	jnz	prep_hide
+	xor	cx,cx			; normal attribute
+;
+prep_hide:
+	mov	ah,3ch			; create file
+	test	pmethod,CREAT_TEMP
+	jz	prep_no_temp
+	mov	ah,5ah
+;
+prep_no_temp:
+	int	21h			; create/create temp
+	jnc	prep_got_file
+	jmp	prep_no_file
+;
+prep_got_file:
+	mov	bx,ax			; handle
+;
+;	save the file name
+;
+	pop	es
+	push	es
+	mov	di,offset swap_prep.swapfilename
+	mov	cx,81
+	mov	si,dx
+	rep movsb
+;
+	pop	ds
+	mov	swap_prep.handle,bx
+;
+;	preallocate the file
+;
+	test	pmethod,NO_PREALLOC
+	jnz	prep_noprealloc
+	test	pmethod,CHECK_NET
+	jz	prep_nonetcheck
+;
+;	check whether file is on a network drive, and don't preallocate
+;	if so. preallocation can slow down swapping significantly when
+;	running on certain networks (Novell)
+;
+	mov	ax,440ah	; check if handle is remote
+	int	21h
+	jc	prep_nonetcheck	; assume not remote if function fails
+	test	dh,80h		; DX bit 15 set ?
+	jnz	prep_noprealloc	; remote if yes
+;
+prep_nonetcheck:
+	mov	dx,totparas
+	mov	cl,4
+	rol	dx,cl
+	mov	cx,dx
+	and	dx,0fff0h
+	and	cx,0000fh
+	sub	dx,1
+	sbb	cx,0
+	mov	si,dx			; save
+	mov	ax,4200h		; move file pointer, absolute
+	int	21h
+	jc	prep_file_err
+	cmp	dx,cx
+	jne	prep_file_err
+	cmp	ax,si
+	jne	prep_file_err
+	mov	cx,1			; write 1 byte
+	mov	ah,40h
+	int	21h
+	jc	prep_file_err
+	cmp	ax,cx
+	jne	prep_file_err
+;
+	mov	ax,4200h		; move file pointer, absolute
+	xor	dx,dx
+	xor	cx,cx			; rewind to beginning
+	int	21h
+	jc	prep_file_err
+;
+prep_noprealloc:
+	mov	ax,USE_FILE
+	mov	swap_prep.swapmethod,al
+	ret
+;
+prep_file_err:
+	mov	ah,3eh			; close file
+	int	21h
+	mov	dx,offset swap_prep.swapfilename
+	mov	ah,41h			; delete file
+	int	21h
+;
+prep_no_file:
+	mov	ax,-1
+	mov	swap_prep.swapmethod,al
+	ret
+;
+prep_swap	endp
+;
+	end
+
Files com076a/strings/fixstrs.c and com076b/strings/fixstrs.c are identical
Files com076a/strings/fixstrs.exe and com076b/strings/fixstrs.exe are identical
Files com076a/strings/indent.pro and com076b/strings/indent.pro are identical
diff --new-file --recursive --ignore-space-change -U 5 --report-identical-files com076a/strings/strings.txt com076b/strings/strings.txt
--- com076a/strings/strings.txt	Mon Feb  1 20:48:56 1999
+++ com076b/strings/strings.txt	Wed Mar 24 15:32:06 1999
@@ -241,54 +241,61 @@
 
 :TEXT_MSG_CURRENT_DATE
 Current date is %s %02d-%02d-%d
 .
 
+:TEXT_MSG_ENTER_DATE
+Enter new date (mm-dd-[yy]yy):
+.
+
 :TEXT_MSG_CURRENT_TIME
 Current time is %2d:%02d:%02d.%02d%c
 .
 
+:TEXT_MSG_ENTER_TIME
+Enter new time:
+.
+
 :TEXT_MSG_DELETE_ALL
 All files in directory will be deleted!
 Are you sure (Y/N)?
 ,
 
-:TEXT_MSG_VER_COPYRIGHT
-Copyright (C) 1994-1998 Tim Norman and others.
-.
-
 :TEXT_MSG_VER_BASIC
+Copyright (C) 1994-1998 Tim Norman and others.
 
 The FreeDOS Command Shell comes with ABSOLUTELY NO WARRANTY.
 For details type: "ver /w". This is free software, and you are
 welcome to redistribute it under certain conditions; type "ver /d"
 for details.  Type "ver /c" for a listing of credits.
 
 Send bug reports to command@gcfl.net.
-Updates are available from http://www.gcfl.net/FreeDOS/command.com
+Updates are available from http://www.gcfl.net/FreeCom
 .
 
 :TEXT_MSG_VER_WARRANTY
+Copyright (C) 1994-1998 Tim Norman and others.
 
 This program is distributed in the hope that it will be useful, but
 WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 General Public License for more details.
 
 Send bug reports to command@gcfl.net.
-Updates are available from http://www.gcfl.net/FreeDOS/command.com
+Updates are available from http://www.gcfl.net/FreeCom
 .
 
 :TEXT_MSG_VER_REDISTRIBUTION
+Copyright (C) 1994-1998 Tim Norman and others.
 
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at
 your option) any later version.
 
 Send bug reports to command@gcfl.net.
-Updates are available from http://www.gcfl.net/FreeDOS/command.com
+Updates are available from http://www.gcfl.net/FreeCom
 .
 
 :TEXT_MSG_VER_DEVELOPERS
 
 The FreeDOS Command Shell developed by:
@@ -297,14 +304,14 @@
 		Svante Frey     Oliver Mueller
 		Aaron Kaufman   Marc Desrochers
 		Rob Lake        John P Price
 		Hans B Pufal
 
-Currently maintained by John P Price & Rob Lake.
+Currently maintained by John P Price.
 
 Send bug reports to command@gcfl.net.
-Updates are available from http://www.gcfl.net/FreeDOS/command.com
+Updates are available from http://www.gcfl.net/FreeCom
 .
 
 
 #
 # Command help text
@@ -678,15 +685,18 @@
 .
 
 :TEXT_CMDHELP_SET
 Displays, sets, or removes environment variables.
 
-SET [variable[=][string]]
+SET [variable[= | space][string]]
 
   variable  Specifies the environment-variable name.
   string    Specifies a series of characters to assign to the variable.
 
+* If you include a '=', but no string, then the variable is removed
+  from the environment.
+* Now you can use a space instead of '=' to create/change a variable.
 
 Type SET without parameters to display the current environment variables.
 .
 
 :TEXT_CMDHELP_SHIFT
Binary files com076a/strings.dat and com076b/strings.dat differ
diff --new-file --recursive --ignore-space-change -U 5 --report-identical-files com076a/strings.h com076b/strings.h
--- com076a/strings.h	Mon Feb  1 21:37:24 1999
+++ com076b/strings.h	Wed Mar 24 16:42:48 1999
@@ -1,10 +1,10 @@
 #define  STRINGS_ID         "FreeDOS"
 
-#define  NUMBER_OF_STRINGS  0x63
+#define  NUMBER_OF_STRINGS  0x64
 
-#define  SIZE_OF_STRINGS    0x3BA1
+#define  SIZE_OF_STRINGS    0x3C9C
 
 #define  TEXT_ERROR_INVALID_SWITCH          0x00 /* @ 0x0000 */
 #define  TEXT_ERROR_CLOSE_QUOTE             0x01 /* @ 0x0017 */
 #define  TEXT_ERROR_TEMPFILE                0x02 /* @ 0x0032 */
 #define  TEXT_ERROR_TOO_MANY_PARAMETERS_STR 0x03 /* @ 0x0050 */
@@ -56,50 +56,51 @@
 #define  TEXT_MSG_DOSKEY                    0x31 /* @ 0x05C8 */
 #define  TEXT_MSG_ECHO_STATE                0x32 /* @ 0x05FB */
 #define  TEXT_MSG_VERIFY_STATE              0x33 /* @ 0x0607 */
 #define  TEXT_MSG_BREAK_STATE               0x34 /* @ 0x0615 */
 #define  TEXT_MSG_CURRENT_DATE              0x35 /* @ 0x0622 */
-#define  TEXT_MSG_CURRENT_TIME              0x36 /* @ 0x0643 */
-#define  TEXT_MSG_DELETE_ALL                0x37 /* @ 0x0669 */
-#define  TEXT_MSG_VER_COPYRIGHT             0x38 /* @ 0x06A5 */
-#define  TEXT_MSG_VER_BASIC                 0x39 /* @ 0x06D5 */
-#define  TEXT_MSG_VER_WARRANTY              0x3A /* @ 0x0836 */
-#define  TEXT_MSG_VER_REDISTRIBUTION        0x3B /* @ 0x098C */
-#define  TEXT_MSG_VER_DEVELOPERS            0x3C /* @ 0x0AE8 */
-#define  TEXT_CMDHELP_ALIAS                 0x3D /* @ 0x0C5D */
-#define  TEXT_CMDHELP_BEEP                  0x3E /* @ 0x0D4B */
-#define  TEXT_CMDHELP_BREAK                 0x3F /* @ 0x0D5F */
-#define  TEXT_CMDHELP_CALL                  0x40 /* @ 0x0DE1 */
-#define  TEXT_CMDHELP_CD                    0x41 /* @ 0x0EA9 */
-#define  TEXT_CMDHELP_CHCP                  0x42 /* @ 0x0FED */
-#define  TEXT_CMDHELP_CLS                   0x43 /* @ 0x1096 */
-#define  TEXT_CMDHELP_COMMAND               0x44 /* @ 0x10AF */
-#define  TEXT_CMDHELP_COPY                  0x45 /* @ 0x1533 */
-#define  TEXT_CMDHELP_CTTY                  0x46 /* @ 0x18BF */
-#define  TEXT_CMDHELP_DATE                  0x47 /* @ 0x1945 */
-#define  TEXT_CMDHELP_DEL                   0x48 /* @ 0x19F0 */
-#define  TEXT_CMDHELP_DIR                   0x49 /* @ 0x1B0B */
-#define  TEXT_CMDHELP_DOSKEY                0x4A /* @ 0x2037 */
-#define  TEXT_CMDHELP_ECHO                  0x4B /* @ 0x25C9 */
-#define  TEXT_CMDHELP_EXIT                  0x4C /* @ 0x2668 */
-#define  TEXT_CMDHELP_FOR                   0x4D /* @ 0x2690 */
-#define  TEXT_CMDHELP_GOTO                  0x4E /* @ 0x2871 */
-#define  TEXT_CMDHELP_HISTORY               0x4F /* @ 0x2947 */
-#define  TEXT_CMDHELP_IF                    0x50 /* @ 0x2959 */
-#define  TEXT_CMDHELP_LH                    0x51 /* @ 0x2C5F */
-#define  TEXT_CMDHELP_LOADFIX               0x52 /* @ 0x2F85 */
-#define  TEXT_CMDHELP_MD                    0x53 /* @ 0x3067 */
-#define  TEXT_CMDHELP_PATH                  0x54 /* @ 0x30A1 */
-#define  TEXT_CMDHELP_PAUSE                 0x55 /* @ 0x31A8 */
-#define  TEXT_CMDHELP_PROMPT                0x56 /* @ 0x323D */
-#define  TEXT_CMDHELP_RD                    0x57 /* @ 0x34A1 */
-#define  TEXT_CMDHELP_REM                   0x58 /* @ 0x34E5 */
-#define  TEXT_CMDHELP_REN                   0x59 /* @ 0x352F */
-#define  TEXT_CMDHELP_SET                   0x5A /* @ 0x366A */
-#define  TEXT_CMDHELP_SHIFT                 0x5B /* @ 0x3782 */
-#define  TEXT_CMDHELP_TIME                  0x5C /* @ 0x37CA */
-#define  TEXT_CMDHELP_TRUENAME              0x5D /* @ 0x387C */
-#define  TEXT_CMDHELP_TYPE                  0x5E /* @ 0x38D3 */
-#define  TEXT_CMDHELP_VER                   0x5F /* @ 0x3916 */
-#define  TEXT_CMDHELP_VERIFY                0x60 /* @ 0x3A45 */
-#define  TEXT_CMDHELP_VOL                   0x61 /* @ 0x3B02 */
-#define  TEXT_CMDHELP_QUESTION              0x62 /* @ 0x3B52 */
+#define  TEXT_MSG_ENTER_DATE                0x36 /* @ 0x0643 */
+#define  TEXT_MSG_CURRENT_TIME              0x37 /* @ 0x0663 */
+#define  TEXT_MSG_ENTER_TIME                0x38 /* @ 0x0689 */
+#define  TEXT_MSG_DELETE_ALL                0x39 /* @ 0x069A */
+#define  TEXT_MSG_VER_BASIC                 0x3A /* @ 0x06D6 */
+#define  TEXT_MSG_VER_WARRANTY              0x3B /* @ 0x085A */
+#define  TEXT_MSG_VER_REDISTRIBUTION        0x3C /* @ 0x09D3 */
+#define  TEXT_MSG_VER_DEVELOPERS            0x3D /* @ 0x0B52 */
+#define  TEXT_CMDHELP_ALIAS                 0x3E /* @ 0x0CB0 */
+#define  TEXT_CMDHELP_BEEP                  0x3F /* @ 0x0D9C */
+#define  TEXT_CMDHELP_BREAK                 0x40 /* @ 0x0DB0 */
+#define  TEXT_CMDHELP_CALL                  0x41 /* @ 0x0E32 */
+#define  TEXT_CMDHELP_CD                    0x42 /* @ 0x0EFA */
+#define  TEXT_CMDHELP_CHCP                  0x43 /* @ 0x103E */
+#define  TEXT_CMDHELP_CLS                   0x44 /* @ 0x10E7 */
+#define  TEXT_CMDHELP_COMMAND               0x45 /* @ 0x1100 */
+#define  TEXT_CMDHELP_COPY                  0x46 /* @ 0x1584 */
+#define  TEXT_CMDHELP_CTTY                  0x47 /* @ 0x1910 */
+#define  TEXT_CMDHELP_DATE                  0x48 /* @ 0x1996 */
+#define  TEXT_CMDHELP_DEL                   0x49 /* @ 0x1A41 */
+#define  TEXT_CMDHELP_DIR                   0x4A /* @ 0x1B5C */
+#define  TEXT_CMDHELP_DOSKEY                0x4B /* @ 0x2088 */
+#define  TEXT_CMDHELP_ECHO                  0x4C /* @ 0x261A */
+#define  TEXT_CMDHELP_EXIT                  0x4D /* @ 0x26B9 */
+#define  TEXT_CMDHELP_FOR                   0x4E /* @ 0x26E1 */
+#define  TEXT_CMDHELP_GOTO                  0x4F /* @ 0x28C2 */
+#define  TEXT_CMDHELP_HISTORY               0x50 /* @ 0x2998 */
+#define  TEXT_CMDHELP_IF                    0x51 /* @ 0x29AA */
+#define  TEXT_CMDHELP_LH                    0x52 /* @ 0x2CB0 */
+#define  TEXT_CMDHELP_LOADFIX               0x53 /* @ 0x2FD6 */
+#define  TEXT_CMDHELP_MD                    0x54 /* @ 0x30B8 */
+#define  TEXT_CMDHELP_PATH                  0x55 /* @ 0x30F2 */
+#define  TEXT_CMDHELP_PAUSE                 0x56 /* @ 0x31F9 */
+#define  TEXT_CMDHELP_PROMPT                0x57 /* @ 0x328E */
+#define  TEXT_CMDHELP_RD                    0x58 /* @ 0x34F2 */
+#define  TEXT_CMDHELP_REM                   0x59 /* @ 0x3536 */
+#define  TEXT_CMDHELP_REN                   0x5A /* @ 0x3580 */
+#define  TEXT_CMDHELP_SET                   0x5B /* @ 0x36BB */
+#define  TEXT_CMDHELP_SHIFT                 0x5C /* @ 0x387D */
+#define  TEXT_CMDHELP_TIME                  0x5D /* @ 0x38C5 */
+#define  TEXT_CMDHELP_TRUENAME              0x5E /* @ 0x3977 */
+#define  TEXT_CMDHELP_TYPE                  0x5F /* @ 0x39CE */
+#define  TEXT_CMDHELP_VER                   0x60 /* @ 0x3A11 */
+#define  TEXT_CMDHELP_VERIFY                0x61 /* @ 0x3B40 */
+#define  TEXT_CMDHELP_VOL                   0x62 /* @ 0x3BFD */
+#define  TEXT_CMDHELP_QUESTION              0x63 /* @ 0x3C4D */
Files com076a/suppl/dfn.h and com076b/suppl/dfn.h are identical
Files com076a/suppl/dynstr.h and com076b/suppl/dynstr.h are identical
Files com076a/suppl/environ.h and com076b/suppl/environ.h are identical
Files com076a/suppl/mcb.h and com076b/suppl/mcb.h are identical
Files com076a/suppl/p-bc.h and com076b/suppl/p-bc.h are identical
Files com076a/suppl/portable.h and com076b/suppl/portable.h are identical
diff --new-file --recursive --ignore-space-change -U 5 --report-identical-files com076a/suppl/readme.txt com076b/suppl/readme.txt
--- com076a/suppl/readme.txt	Thu Dec  3 23:09:32 1998
+++ com076b/suppl/readme.txt	Wed Mar 24 15:43:04 1999
@@ -3,6 +3,6 @@
 Copyright (C) 1995-97 Steffen Kaiser, Release Version 2.6
 
 This library is available from the Steffen's site at
 ftp://www.inf.fh-rhein-sieg.de/pub/freedos/local/suppl26.zip
 
-It is also mirrored at http://www.gcfl.net/freedos/suppl/suppl26.zip
+It is also mirrored at ftp://ftp.gcfl.net/freedos/suppl/suppl26.zip
Files com076a/suppl/regproto.h and com076b/suppl/regproto.h are identical
Files com076a/suppl/suppl.h and com076b/suppl/suppl.h are identical
Binary files com076a/suppl_s.lib and com076b/suppl_s.lib differ
diff --new-file --recursive --ignore-space-change -U 5 --report-identical-files com076a/swapexec.c com076b/swapexec.c
--- com076a/swapexec.c	Wed Dec 31 18:00:00 1969
+++ com076b/swapexec.c	Wed Mar 24 16:26:00 1999
@@ -0,0 +1,218 @@
+/*
+   --- Version 3.4 93-06-22 13:40 ---
+
+   EXEC.C: EXEC function with memory swap - Prepare parameters.
+
+   Public domain software by
+
+   Thomas Wagner
+   Ferrari electronic GmbH
+   Beusselstrasse 27
+   D-1000 Berlin 21
+   Germany
+ */
+
+#include "config.h"
+
+#ifdef FEATURE_SWAP_EXEC
+
+#include "command.h"
+
+#include "compat.h"
+#include "swapexec.h"
+#include <bios.h>
+
+/* internal flags for prep_swap */
+
+#define CREAT_TEMP      0x0080
+#define DONT_SWAP_ENV   0x4000
+
+#define ERR_COMSPEC     -7
+#define ERR_NOMEM       -8
+
+#ifdef __cplusplus
+extern "C" int
+#else
+extern int _cdecl
+#endif
+  do_spawn(int swapping,        /* swap if non-0 */
+           char *xeqfn,         /* file to execute */
+           char *cmdtail,       /* command tail string */
+           unsigned envlen,     /* environment length */
+           char *envp           /* environment pointer */
+);
+
+#ifdef __cplusplus
+extern "C" int
+#else
+extern int _cdecl
+#endif
+  prep_swap(int method,         /* swap method */
+            char *swapfn);      /* swap file name and/or path */
+
+void spawn_check(int swapping, char *execfn, char *progpars)
+{
+  if (swapping > 0)
+  {
+    switch (swap_prep.swapmethod)
+    {
+      case USE_EMS:
+        printf("Swapping to EMS handle %u\n", swap_prep.handle);
+        break;
+
+      case USE_XMS:
+        printf("Swapping to XMS handle %u\n", swap_prep.handle);
+        break;
+
+      case USE_FILE:
+        printf("Swapping to File <%s>\n", swap_prep.swapfilename);
+        break;
+    }
+  }
+  else
+  {
+    printf("swapping == 0\n");
+  }
+  printf("Executing %s %s\n\n", execfn, progpars);
+}
+
+/*
+   retcode = do_exec (filename to execute,
+   program parameters,
+   spawn options,
+   memory needed (0xffff to always swap, 0 to never swap),
+   environment pointer/flag)
+ */
+
+int do_exec(char *exfn, char *epars, int spwn, unsigned needed, char **envp)
+{
+  static char swapfn[MAXPATH];
+  static char execfn[MAXPATH];
+  unsigned avail;
+  union REGS regs;
+  unsigned envlen;
+  int rc;
+  char **env;
+  char *ep, *envptr, *envbuf;
+  char *progpars;
+  char *tempname = NULL;
+  int swapping;
+
+  envlen = 0;
+  envptr = NULL;
+  envbuf = NULL;
+
+  if (epars == NULL)
+    epars = "";
+  if (exfn == NULL)
+    execfn[0] = 0;
+  else
+    strcpy(execfn, exfn);
+
+  progpars = (char *)malloc(strlen(epars) + 1);
+  if (progpars == NULL)
+    return RC_NOFILE | -ERR_NOMEM;
+  strcpy(progpars, epars);
+
+  /* Now create a copy of the environment if the user wants it. */
+
+  if (envp != NULL)
+    for (env = envp; *env != NULL; env++)
+      envlen += strlen(*env) + 1;
+
+  if (envlen)
+  {
+    /* round up to paragraph, and alloc another paragraph leeway */
+    envlen = (envlen + 32) & 0xfff0;
+    envbuf = (char *)malloc(envlen);
+    if (envbuf == NULL)
+    {
+      rc = RC_ENVERR;
+      goto exit;
+    }
+
+    /* align to paragraph */
+    envptr = envbuf;
+    if (FP_OFF(envptr) & 0x0f)
+      envptr += 16 - (FP_OFF(envptr) & 0x0f);
+    ep = envptr;
+
+    for (env = envp; *env != NULL; env++)
+    {
+      ep = stpcpy(ep, *env) + 1;
+    }
+    *ep = 0;
+  }
+
+  if (!spwn)
+    swapping = -1;
+  else
+  {
+    /* Determine amount of free memory */
+
+    regs.x.ax = 0x4800;
+    regs.x.bx = 0xffff;
+    intdos(&regs, &regs);
+    avail = regs.x.bx;
+
+    /* No swapping if available memory > needed */
+
+    if (needed < avail)
+      swapping = 0;
+    else
+    {
+      /* Swapping necessary, use 'TMP' or 'TEMP' environment variable
+         to determine swap file path if defined. */
+
+      swapping = spwn;
+      if (spwn & USE_FILE)
+      {
+        if ((tempname = tmpfn()) != NULL)
+        {
+          spwn &= ~USE_FILE;
+          swapping = spwn;
+          strcpy(swapfn, tempname);
+          free(tempname);
+        }
+        else
+        {
+          if (OS_MAJOR >= 3)
+            swapping |= CREAT_TEMP;
+        }
+      }
+    }
+  }
+
+  /* All set up, ready to go. */
+
+  if (swapping > 0)
+  {
+    if (!envlen)
+      swapping |= DONT_SWAP_ENV;
+
+    rc = prep_swap(swapping, swapfn);
+    if (rc < 0)
+      rc = RC_PREPERR | -rc;
+    else
+      rc = 0;
+  }
+  else
+    rc = 0;
+
+  if (!rc)
+  {
+    spawn_check(swapping, execfn, progpars);
+    rc = do_spawn(swapping, execfn, progpars, envlen, envptr);
+  }
+
+  /* Free the environment buffer if it was allocated. */
+
+exit:
+  free(progpars);
+  if (envlen)
+    free(envbuf);
+
+  return rc;
+}
+
+#endif
diff --new-file --recursive --ignore-space-change -U 5 --report-identical-files com076a/swapexec.h com076b/swapexec.h
--- com076a/swapexec.h	Wed Dec 31 18:00:00 1969
+++ com076b/swapexec.h	Wed Mar 17 22:26:52 1999
@@ -0,0 +1,216 @@
+/*
+	--- Version 3.3 91-11-21 16:12 ---
+
+   EXEC.H: EXEC function with memory swap - Main function header file.
+
+   Public domain software by
+
+        Thomas Wagner
+        Ferrari electronic GmbH
+        Beusselstrasse 27
+        D-1000 Berlin 21
+        Germany
+*/
+
+
+extern int do_exec (char *xfn, char *pars, int spawn, unsigned needed,
+						  char **envp);
+
+/*
+   The EXEC function.
+
+      Parameters:
+
+         xfn      is a string containing the name of the file
+                  to be executed. If the string is empty,
+                  the COMSPEC environment variable is used to
+                  load a copy of COMMAND.COM or its equivalent.
+                  If the filename does not include a path, the
+                  current PATH is searched after the default.
+                  If the filename does not include an extension,
+                  the path is scanned for a COM, EXE, or BAT file
+                  in that order.
+
+         pars     The program parameters.
+
+         spawn    If 0, the function will terminate after the
+                  EXECed program returns, the function will not return.
+
+                  NOTE: If the program file is not found, the function
+                        will always return with the appropriate error
+                        code, even if 'spawn' is 0.
+
+                  If non-0, the function will return after executing the
+                  program. If necessary (see the "needed" parameter),
+                  memory will be swapped out before executing the program.
+                  For swapping, spawn must contain a combination of the
+                  following flags:
+
+                     USE_EMS  (0x01)  - allow EMS swap
+                     USE_XMS  (0x02)  - allow XMS swap
+                     USE_FILE (0x04)  - allow File swap
+
+                  The order of trying the different swap methods can be
+                  controlled with one of the flags
+
+                     EMS_FIRST (0x00) - EMS, XMS, File (default)
+                     XMS_FIRST (0x10) - XMS, EMS, File
+
+                  If swapping is to File, the attribute of the swap file
+                  can be set to "hidden", so users are not irritated by
+                  strange files appearing out of nowhere with the flag
+
+                     HIDE_FILE (0x40)    - create swap file as hidden
+
+                  and the behaviour on Network drives can be changed with
+
+                     NO_PREALLOC (0x100) - don't preallocate
+                     CHECK_NET (0x200)   - don't preallocate if file on net.
+
+                  This checking for Network is mainly to compensate for
+                  a strange slowdown on Novell networks when preallocating
+                  a file. You can either set NO_PREALLOC to avoid allocation
+                  in any case, or let the prep_swap routine decide whether
+                  to do preallocation or not depending on the file being
+                  on a network drive (this will only work with DOS 3.1 or
+                  later).
+
+         needed   The memory needed for the program in paragraphs (16 Bytes).
+                  If not enough memory is free, the program will
+                  be swapped out.
+                  Use 0 to never swap, 0xffff to always swap.
+                  If 'spawn' is 0, this parameter is irrelevant.
+
+         envp     The environment to be passed to the spawned
+                  program. If this parameter is NULL, a copy
+                  of the parent's environment is used (i.e.
+                  'putenv' calls have no effect). If non-NULL,
+                  envp must point to an array of pointers to
+                  strings, terminated by a NULL pointer (the
+                  standard variable 'environ' may be used).
+
+      Return value:
+
+         0x0000..00FF: The EXECed Program's return code
+
+         0x0101:       Error preparing for swap: no space for swapping
+         0x0102:       Error preparing for swap: program too low in memory
+
+         0x0200:       Program file not found
+         0x0201:       Program file: Invalid drive
+         0x0202:       Program file: Invalid path
+         0x0203:       Program file: Invalid name
+         0x0204:       Program file: Invalid drive letter
+         0x0205:       Program file: Path too long
+         0x0206:       Program file: Drive not ready
+         0x0207:       Batchfile/COMMAND: COMMAND.COM not found
+         0x0208:       Error allocating temporary buffer
+
+         0x03xx:       DOS-error-code xx calling EXEC
+
+         0x0400:       Error allocating environment buffer
+
+         0x0500:       Swapping requested, but prep_swap has not
+                       been called or returned an error.
+         0x0501:       MCBs don't match expected setup
+         0x0502:       Error while swapping out
+
+         0x0600:       Redirection syntax error
+         0x06xx:       DOS error xx on redirection
+*/
+
+
+/* Return codes (only upper byte significant) */
+
+#define RC_PREPERR   0x0100
+#define RC_NOFILE    0x0200
+#define RC_EXECERR   0x0300
+#define RC_ENVERR    0x0400
+#define RC_SWAPERR   0x0500
+#define RC_REDIRERR  0x0600
+
+/* Swap method and option flags */
+
+#define USE_EMS      0x01
+#define USE_XMS      0x02
+#define USE_FILE     0x04
+#define EMS_FIRST    0x00
+#define XMS_FIRST    0x10
+#define HIDE_FILE    0x40
+#define NO_PREALLOC  0x100
+#define CHECK_NET    0x200
+
+#define USE_ALL      (USE_EMS | USE_XMS | USE_FILE)
+
+
+/*
+   The function pointed to by "spawn_check" will be called immediately
+   before doing the actual swap/exec, provided that
+
+      - the preparation code did not detect an error, and
+      - "spawn_check" is not NULL.
+
+   The function definition is
+      int name (int cmdbat, int swapping, char *execfn, char *progpars)
+
+   The parameters passed to this function are
+
+      cmdbat      1: Normal EXE/COM file
+                  2: Executing BAT file via COMMAND.COM
+                  3: Executing COMMAND.COM (or equivalent)
+
+      swapping    < 0: Exec, don't swap
+                    0: Spawn, don't swap
+                  > 0: Spawn, swap
+
+      execfn      the file name to execute (complete with path)
+
+      progpars    the program parameter string
+
+   If the routine returns anything other than 0, the swap/exec will
+   not be executed, and do_exec will return with this code.
+
+   You can use this function to output messages (for example, the
+   usual "enter EXIT to return" message when loading COMMAND.COM)
+   and to do clean-up and additional checking.
+
+   CAUTION: If swapping is > 0, the routine may not modify the
+   memory layout, i.e. it may not call any memory allocation or
+   deallocation routines.
+
+   "spawn_check" is initialized to NULL.
+*/
+
+//typedef int (spawn_check_proc)(int cmdbat, int swapping, char *execfn, char *progpars);
+//extern spawn_check_proc *spawn_check;
+
+/*
+   The 'swap_prep' variable can be accessed from the spawn_check
+   call-back routine for additional information on the nature and
+   parameters of the swap. This variable will ONLY hold useful
+   information if the 'swapping' parameter to spawn_check is > 0.
+   The contents of this variable may not be changed.
+
+   The 'swapmethod' field will contain one of the flags USE_FILE,
+   USE_XMS, or USE_EMS.
+
+   Caution: The module using this data structure must be compiled
+   with structure packing on byte boundaries on, i.e. with /Zp1 for
+   MSC, or -a- for Turbo/Borland.
+*/
+
+typedef struct {
+               long xmm;            /* XMM entry address */
+               int first_mcb;       /* Segment of first MCB */
+               int psp_mcb;         /* Segment of MCB of our PSP */
+               int env_mcb;         /* MCB of Environment segment */
+               int noswap_mcb;      /* MCB that may not be swapped */
+               int ems_pageframe;   /* EMS page frame address */
+               int handle;          /* EMS/XMS/File handle */
+               int total_mcbs;      /* Total number of MCBs */
+               char swapmethod;     /* Method for swapping */
+               char swapfilename[81]; /* Swap file name if swapping to file */
+               } prep_block;
+
+extern prep_block swap_prep;
+
Files com076a/tempfile.c and com076b/tempfile.c are identical
Files com076a/tempfile.h and com076b/tempfile.h are identical
Files com076a/tests/bat1.bat and com076b/tests/bat1.bat are identical
Files com076a/tests/bat2.bat and com076b/tests/bat2.bat are identical
Files com076a/tests/bat3.bat and com076b/tests/bat3.bat are identical
Files com076a/tests/dir-test.txt and com076b/tests/dir-test.txt are identical
Files com076a/tests/fdos.out and com076b/tests/fdos.out are identical
Files com076a/tests/hbp001a.bat and com076b/tests/hbp001a.bat are identical
Files com076a/tests/hbp001b.bat and com076b/tests/hbp001b.bat are identical
Files com076a/tests/hbp002.txt and com076b/tests/hbp002.txt are identical
Files com076a/tests/hbp_001.txt and com076b/tests/hbp_001.txt are identical
Files com076a/tests/msdos.out and com076b/tests/msdos.out are identical
Files com076a/tests/refl.bat and com076b/tests/refl.bat are identical
Files com076a/tests/t.bat and com076b/tests/t.bat are identical
Files com076a/tests/test.bat and com076b/tests/test.bat are identical
Files com076a/tests/test1.bat and com076b/tests/test1.bat are identical
Files com076a/tests/test2.bat and com076b/tests/test2.bat are identical
Files com076a/tests/test3.bat and com076b/tests/test3.bat are identical
Files com076a/tests/test4.bat and com076b/tests/test4.bat are identical
Files com076a/tests/testenv.c and com076b/tests/testenv.c are identical
diff --new-file --recursive --ignore-space-change -U 5 --report-identical-files com076a/time.c com076b/time.c
--- com076a/time.c	Fri Jan 22 17:32:40 1999
+++ com076b/time.c	Wed Mar 24 16:27:24 1999
@@ -7,17 +7,29 @@
  *    started.
  *
  * 27-Jul-1998 (John P Price <linux-guru@gcfl.net>)
  * - added config.h include
  *
+ * 1999/03/17 ska
+ * bugfix: Detection of invalid time strings
+ *  One can specify:
+ *    ^\s*\d+.\d+(.(\d+(.\d*)?)?)?\s*([aApP]([mM]|\.[mM]\.)?\s*$
+ *  If one specifies:
+ *    1 number --> error
+ *    2 numbers --> hour:minute, seconds & hundreds default to zero
+ *    3 numbers --> hour:minute:seconds, hundreds defaults to zero
+ *    4 numbers --> hour:minute:seconds.hundreds
+ *  The numbers may be delimited by any character from the 7-bit ASCII set,
+ *  which is printable, but not alphanumerical.
  */
 
 #include "config.h"
 
 #ifdef INCLUDE_CMD_TIME
 
 #include <stdio.h>
+#include <stdlib.h>
 #include <dos.h>
 #include <ctype.h>
 #include <string.h>
 
 #include "command.h"
@@ -25,78 +37,74 @@
 #include "strings.h"
 
 int parsetime(char *s)
 {
   struct dostime_t t;
+  int nums[4],
+    items;
+  int pm;
 
-  if (!*s)
-    return 1;
+  memset(nums, 0, sizeof(nums));	/* let default everything to zero */
 
-  t.hour = 0;
-  t.minute = 0;
-  t.second = 0;
-  t.hsecond = 0;
+  /* Parse at maximum three numbers */
+  s = parsenum(s, 4, &items, nums);
 
-  // first get hour
-  if (isdigit(*s))
-  {
-    while (isdigit(*s))
-      t.hour = t.hour * 10 + (*s++) - '0';
-  }
-  else
+  if (!s)                       /* general error */
     return 0;
 
-  // get colon
-  if (*s == ':')
-  {
-    s++;
-
-    // now get minutes
-    if (isdigit(*s))
-    {
-      while (isdigit(*s))
-        t.minute = t.minute * 10 + (*s++) - '0';
-
-      // get colon
-      if (*s == ':')
-      {
-        s++;
+  /* 12 hour time format? */
+  pm = 0;                       /* no such flag */
+  switch (*strupr(s))
+  {
+    case 'P':
+      ++pm;                     /* post meridian */
+    case 'A':
+      ++pm;                     /* ante meridian */
+      /* strip ?M or ?.M. */
+      if (s[1] == 'M')
+        ++s;
+      else if (memcmp(s + 1, ".M.", 3) == 0)
+        s += 3;
+      /* strip whitespaces */
+      while (isspace(*++s)) ;   /* skip whitespaces */
+  }
+  if (*s)
+    return 0;                   /* too many characters on line */
 
-        // now get seconds
-        if (isdigit(*s))
+  switch (items)
         {
-          while (isdigit(*s))
-            t.second = t.second * 10 + (*s++) - '0';
+    case 0:                    /* empty line --> always OK */
+      return 1;
 
-          // get period
-          if (*s == '.')
-          {
-            s++;
+    case 1:                    /* single number --> error */
+      return 0;
 
-            // now get hundreths
-            if (isdigit(*s))
-            {
-              while (isdigit(*s))
-                t.hsecond = t.hsecond * 10 + (*s++) - '0';
-            }
-          }
-        }
-      }
-    }
+#if 0
+    default:                   /* four numbers --> 1/100s, s, min, h */
+      /* else: 1/100s and/or s default to zero */
+      break;
+#endif
   }
 
-  if (toupper(*s) == 'P')
-  {
-    t.hour += 12;
-  }
+  t.hour = nums[0];
+  t.minute = nums[1];
+  t.second = nums[2];
+  t.hsecond = nums[3];
 
-  if ((toupper(*s) == 'A') && (t.hour == 12))
+  switch (pm)
   {
+    case 2:                    /* post meridian */
+      t.hour += 12;
+      break;
+    case 1:                    /* antes meridian */
+      if (t.hour == 12)
     t.hour = 0;
+      break;
+      /* default:   not specified --> nothing to do */
   }
 
-  if (t.hour > 23 || t.minute > 60 || t.second > 60 || t.hsecond > 99)
+  if (t.hour > 23 || t.minute >= 60 || t.second >= 60 || t.hsecond > 99)
     return 0;
 
   _dos_settime(&t);
   return 1;
 }
@@ -106,48 +114,54 @@
 {
   struct dostime_t t;
   char ampm;
   char s[40];
 
-  if (!*rest)
+  if (!rest || !*rest)
   {
     _dos_gettime(&t);
 
     if (t.hour > 12)
     {
       ampm = 'p';
       t.hour -= 12;
     }
     else
+    {
       ampm = 'a';
-
     if (t.hour == 0)
       t.hour += 12;
+    }
 
     display_string(TEXT_MSG_CURRENT_TIME, t.hour, t.minute,
                    t.second, t.hsecond, ampm);
+    rest = NULL;
   }
 
   while (1)
   {
-    if (!(rest && *rest))
+    if (!rest)
     {
-      printf("Enter new time: ");
+      if ((rest = getMessage(TEXT_MSG_ENTER_TIME)) == NULL)
+        return 1;
+
+      fputs(rest, stdout);
+      free(rest);
       fgets(s, sizeof(s), stdin);
-      while (*s && s[strlen(s) - 1] < ' ')
-        s[strlen(s) - 1] = '\0';
+      if (cbreak)
+        return 1;
       if (parsetime(s))
         return 0;
     }
     else
     {
       if (parsetime(rest))
         return 0;
-      // force input the next time around.
-      *rest = '\0';
     }
     display_string(TEXT_ERROR_INVALID_TIME);
+    // force input the next time around.
+    rest = NULL;
   }
 }
 
 #endif
 
Files com076a/timefunc.c and com076b/timefunc.c are identical
Files com076a/timefunc.h and com076b/timefunc.h are identical
Files com076a/tmpnam.c and com076b/tmpnam.c are identical
Files com076a/truename.c and com076b/truename.c are identical
Files com076a/type.c and com076b/type.c are identical
diff --new-file --recursive --ignore-space-change -U 5 --report-identical-files com076a/ver.c com076b/ver.c
--- com076a/ver.c	Wed Feb 10 21:22:26 1999
+++ com076b/ver.c	Wed Mar 24 16:23:38 1999
@@ -28,12 +28,12 @@
 #include <dos.h>
 
 #include "command.h"
 #include "strings.h"
 
-char *shellver = "version 0.76a [" __DATE__ "]";
-char *shellname = "FreeDOS Command Shell";
+char *shellver = "version 0.76b [" __DATE__ "]";
+char *shellname = "FreeCom";
 
 void short_version(void)
 {
   printf("\n%s %s\n", shellname, shellver);
 }
@@ -55,11 +55,10 @@
   short_version();
 
   /* Basic copyright notice */
   if (rest[0] == 0)
   {
-    display_string(TEXT_MSG_VER_COPYRIGHT);
     display_string(TEXT_MSG_VER_BASIC);
   }
   else
   {
     for (i = 0; rest[i]; i++)
@@ -98,21 +97,18 @@
           }
         }
       }
       else if (toupper(rest[i]) == 'W')
       {                         /* Warranty notice */
-        display_string(TEXT_MSG_VER_COPYRIGHT);
         display_string(TEXT_MSG_VER_WARRANTY);
       }
       else if (toupper(rest[i]) == 'D')
       {                         /* Redistribution notice */
-        display_string(TEXT_MSG_VER_COPYRIGHT);
         display_string(TEXT_MSG_VER_REDISTRIBUTION);
       }
       else if (toupper(rest[i]) == 'C')
       {                         /* Developer listing */
-        display_string(TEXT_MSG_VER_COPYRIGHT);
         display_string(TEXT_MSG_VER_DEVELOPERS);
       }
       else
       {
         error_invalid_switch(toupper(rest[i]));
Files com076a/verify.c and com076b/verify.c are identical
Files com076a/where.c and com076b/where.c are identical
