home *** CD-ROM | disk | FTP | other *** search
/ The Pier Shareware 6 / The_Pier_Shareware_Number_6_(The_Pier_Exchange)_(1995).iso / 036 / emxfix02.zip / emx / src / os2 / init.c < prev    next >
C/C++ Source or Header  |  1994-12-21  |  26KB  |  1,025 lines

  1. /* init.c -- Initialization
  2.    Copyright (c) 1994 by Eberhard Mattes
  3.  
  4. This file is part of emx.
  5.  
  6. emx is free software; you can redistribute it and/or modify it
  7. under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 2, or (at your option)
  9. any later version.
  10.  
  11. emx is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with emx; see the file COPYING.  If not, write to
  18. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  19.  
  20. As special exception, emx.dll can be distributed without source code
  21. unless it has been changed.  If you modify emx.dll, this exception
  22. no longer applies and you must remove this paragraph from all source
  23. files for emx.dll.  */
  24.  
  25.  
  26. #define INCL_DOSPROCESS
  27. #define INCL_DOSSEMAPHORES
  28. #define INCL_DOSMODULEMGR
  29. #define INCL_DOSMISC
  30. #define INCL_DOSEXCEPTIONS
  31. #define INCL_DOSERRORS
  32. #include <os2emx.h>
  33. #include <sys/emx.h>
  34. #include "emxdll.h"
  35. #include "clib.h"
  36. #include "version.h"
  37.  
  38. /* Pointer to the layout table of the executable. */
  39.  
  40. layout_table *layout;
  41.  
  42. /* Flags from layout table. */
  43.  
  44. ULONG layout_flags;
  45.  
  46. /* Interface number, taken from the layout table. */
  47.  
  48. BYTE interface;
  49.  
  50. /* Current end address of the used part of the heap (address of first
  51.    unused byte).  While this variable is zero, no memory has been
  52.    allocated. */
  53.  
  54. ULONG brk_ptr;
  55.  
  56. /* Base address of heap. */
  57.  
  58. ULONG heap_base;
  59.  
  60. /* End address of the heap (address of the first byte after the heap). */
  61.  
  62. ULONG heap_end;
  63.  
  64. /* The size of the heap, in bytes. */
  65.  
  66. ULONG heap_size;
  67.  
  68. /* Address of the heap in executable file. */
  69.  
  70. ULONG heap_off;
  71.  
  72. /* Base address of the stack object. */
  73.  
  74. ULONG stack_base;
  75.  
  76. /* End address of the stack object (address of first byte after the
  77.    stack object). */
  78.  
  79. ULONG stack_end;
  80.  
  81. /* Run debuggee in same session as the debugger. */
  82.  
  83. BYTE debug_same_sess;
  84.  
  85. /* Flag bits for debugging, set with the -! option. */
  86.  
  87. ULONG debug_flags;
  88.  
  89. /* If this variable is non-zero, file names should be truncated to
  90.    8.3.  The user can set this variable by putting -t into the EMXOPT
  91.    environment variable. */
  92.  
  93. BYTE opt_trunc;
  94.  
  95. /* Default drive: 0=none, "A".."Z", "a".."z": prepend this drive
  96.    letter. */
  97.  
  98. BYTE opt_drive;
  99.  
  100. /* The -x option is used for getting the old (0.8h) behavior of not
  101.    quoting arguments passed using the `MKS Korn shell' method (3rd
  102.    argument string starting with `~').  Expanding arguments in that
  103.    case was wrong. */
  104.  
  105. BYTE opt_expand;
  106.  
  107. /* The -q option forces quoting of all command line arguments passed
  108.    to child processes. */
  109.  
  110. BYTE opt_quote;
  111.  
  112. /* This handle is used for diagnostic output including error
  113.    messages. */
  114.  
  115. ULONG errout_handle;
  116.  
  117. /* This flag is TRUE if this is process is a forked process. */
  118.  
  119. BYTE fork_flag;
  120.  
  121. /* fork(): EBP of parent process. */
  122.  
  123. ULONG fork_ebp;
  124.  
  125. /* fork(): Low end of the stack. */
  126.  
  127. ULONG fork_stack_page;
  128.  
  129. /* Pointer to the PIB. */
  130.  
  131. PIB *init_pib_ptr;
  132.  
  133. /* This variable points to the main exception registration record of
  134.    emx.dll. */
  135.  
  136. EXCEPTIONREGISTRATIONRECORD *exc_reg_ptr;
  137.  
  138. /* The process ID of the process using this instance of emx.dll. */
  139.  
  140. ULONG my_pid;
  141.  
  142. /* Pointer to the command line. */
  143.  
  144. char *startup_args;
  145.  
  146. /* Pointer to the environment. */
  147.  
  148. char *startup_env;
  149.  
  150. /* Number of environment strings. */
  151.  
  152. ULONG env_count;
  153.  
  154. /* Number of arguments. */
  155.  
  156. ULONG arg_count;
  157.  
  158. /* Size of argument strings (bytes). */
  159.  
  160. ULONG arg_size;
  161.  
  162. /* The third argument string of the program. */
  163.  
  164. char *argstr3;
  165.  
  166. /* The name of the EXE file. */
  167.  
  168. BYTE exe_name[257];
  169.  
  170. /* The file handle used for reading from the EXE file if the heap is
  171.    being loaded from the EXE file. */
  172.  
  173. HFILE exe_fhandle;
  174.  
  175. /* This flag is true if the heap is loaded from the EXE file. */
  176.  
  177. BYTE exe_heap;
  178.  
  179. /* This flag is true if we should ignore too small a stack.  It is set
  180.    by the -I option. */
  181.  
  182. static BYTE ignore_stack;
  183.  
  184. /* Error message to be displayed by options(). */
  185.  
  186. static const char *opt_errmsg;
  187.  
  188. /* User flags, initially 0, set by __uflags(). */
  189.  
  190. ULONG uflags;
  191.  
  192. /* Set the following variable to TRUE to avoid using DosKillThread. */
  193.  
  194. BYTE dont_doskillthread;
  195.  
  196. /* The major and minor version numbers of OS/2. */
  197.  
  198. ULONG version_major;
  199. ULONG version_minor;
  200.  
  201.  
  202. /* Prototypes. */
  203.  
  204. static void options (const char *s, const char *errmsg);
  205. static void connect_fork (void);
  206. static void copy_fork_sig (void);
  207.  
  208.  
  209. /* This REXX-callable entrypoint returns a string indicating the
  210.    revision index of emx.dll as integer. The number will be
  211.    incremented on each revision and is used only for comparing. */
  212.  
  213. ULONG emx_revision (PCSZ name, LONG argc, const RXSTRING *argv,
  214.                     PCSZ queuename, PRXSTRING retstr)
  215. {
  216.   static char const revision[] = XSTR(REV_INDEX);
  217.  
  218.   if (argc != 0)
  219.     return (1);
  220.   strcpy (retstr->strptr, revision);
  221.   retstr->strlength = strlen (revision);
  222.   return (0);
  223. }
  224.  
  225.  
  226. /* Fetch the flags and the interface level from the layout table. */
  227.  
  228. void use_layout (void)
  229. {
  230.   layout_flags = layout->flags;
  231.   interface = (BYTE)(layout_flags >> 24);
  232. }
  233.  
  234.  
  235. /* Use the heap stored in the EXE file, if present. */
  236.  
  237. static void use_heap (void)
  238. {
  239.   ULONG rc, action;
  240.  
  241.   if (layout->heap_base == 0 || layout->heap_end == 0)
  242.     return;
  243.   heap_base = layout->heap_base;
  244.   heap_end = layout->heap_end;
  245.   heap_size = heap_end - heap_base;
  246.   if (layout->heap_brk != 0)
  247.     {
  248.       brk_ptr = layout->heap_brk;
  249.       heap_off = layout->heap_off;
  250.       rc = DosOpen (exe_name, &exe_fhandle, &action, 0, 0,
  251.                     OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW,
  252.                     (OPEN_ACCESS_READONLY | OPEN_SHARE_DENYWRITE
  253.                      | OPEN_FLAGS_RANDOM | OPEN_FLAGS_NOINHERIT), NULL);
  254.       if (rc != 0)
  255.         {
  256.           otext ("Cannot open EXE file\r\n");
  257.           quit (255);
  258.         }
  259.       /* Commit the guard pages. */
  260.       if (brk_ptr != heap_base)
  261.         setmem (heap_base, brk_ptr - heap_base,
  262.                 PAG_COMMIT | PAG_GUARD | PAG_READ | PAG_WRITE, 0);
  263.       exe_heap = TRUE;
  264.     }
  265.   else
  266.     brk_ptr = heap_base;
  267. }
  268.  
  269.  
  270. /* First part of initialization.  After return from initialize1(),
  271.    stacks will be switched and, if this process has been forked, data
  272.    will be copied from the parent process. */
  273.  
  274. void initialize1 (void)
  275. {
  276.   TIB *tib_ptr;
  277.   ULONG rc, action;
  278.   PCSZ str;
  279.   char *p;
  280.   size_t len;
  281.  
  282.   __asm__ ("cld");
  283.  
  284.   use_layout ();
  285.  
  286.   /* Get pointers to the TIB and PIB, and fetch environment pointers
  287.      and the PID from the PIB. */
  288.  
  289.   DosGetInfoBlocks (&tib_ptr, &init_pib_ptr);
  290.   startup_args = init_pib_ptr->pib_pchcmd;
  291.   startup_env = init_pib_ptr->pib_pchenv;
  292.   my_pid = init_pib_ptr->pib_ulpid;
  293.  
  294.   /* Get the version number of OS/2. */
  295.  
  296.   version_major = querysysinfo (QSV_VERSION_MAJOR);
  297.   version_minor = querysysinfo (QSV_VERSION_MINOR);
  298.  
  299.   /* Set `dont_doskillthread' depending on the version number of OS/2.
  300.      Assume that DosKillThread works in OS/2 2.11 and later. */
  301.  
  302.   if (!(version_major > 20 || (version_major == 20 && version_minor >= 11)))
  303.     dont_doskillthread = TRUE;
  304.  
  305.   /* Retrieve the name of the executable file of the application. */
  306.  
  307.   rc = DosQueryModuleName (init_pib_ptr->pib_hmte,
  308.                            sizeof (exe_name), exe_name);
  309.   if (rc != 0)
  310.     exe_name[0] = 0;
  311.   exe_fhandle = (HFILE)(-1);
  312.  
  313.   /* Initialize __clock(). */
  314.  
  315.   get_clock (TRUE);
  316.  
  317.   /* Get a pointer to the 3rd argument string.  Check whether we have
  318.      been forked. */
  319.  
  320.   p = startup_args;
  321.   len = strlen (p);
  322.   p += len + 1;
  323.   len = strlen (p);
  324.   p += len + 1;
  325.   argstr3 = p;
  326.   if (*p == '^')
  327.     init_fork (p);
  328.  
  329.   /* Set default values for options. */
  330.  
  331.   heap_size = 0x02000000;       /* 32 MB */
  332.  
  333.   /* Parse emx options put into the executable by emxbind. */
  334.  
  335.   options (layout->options, "Invalid option in .exe file\r\n");
  336.  
  337.   /* Parse emx options in the EMXOPT environment variable. */
  338.  
  339.   rc = DosScanEnv ("EMXOPT", &str);
  340.   if (rc == 0)
  341.     options (str, "Invalid option in EMXOPT\r\n");
  342.  
  343.   /* Get the stack base and end addresses. */
  344.  
  345.   stack_base = (ULONG)tib_ptr->tib_pstack;
  346.   stack_end = (ULONG)tib_ptr->tib_pstacklimit;
  347.  
  348.   /* Refuse to work if the stack is too small. */
  349.  
  350.   if (stack_end - stack_base <= 16*1024 && !ignore_stack)
  351.     {
  352.       char buf[512];
  353.       ULONG written;
  354.  
  355.       if (!pm_init ())
  356.         {
  357.           len = sprintf (buf, "emx.dll: Stack size too small.  Run\r\n"
  358.                          "  emxstack -f %s\r\n"
  359.                          "and try again.\r\n", exe_name);
  360.           DosWrite (2, buf, len, &written);
  361.         }
  362.       else
  363.         {
  364.           sprintf (buf, "Stack size too small.  Run \"emxstack -f %s\" "
  365.                    "and try again.", exe_name);
  366.           pm_message_box (buf);
  367.         }
  368.  
  369.       quit (255);
  370.     }
  371.  
  372.   /* Initialize errout_handle. */
  373.  
  374.   if (debug_flags & DEBUG_STDERR)
  375.     {
  376.       rc = DosOpen ("con", &errout_handle, &action, 0, 0,
  377.                     OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW,
  378.                     (OPEN_ACCESS_WRITEONLY | OPEN_SHARE_DENYNONE
  379.                      | OPEN_FLAGS_NOINHERIT),
  380.                     NULL);
  381.       if (rc != 0)
  382.         errout_handle = 2;      /* In case it was modified by DosOpen */
  383.     }
  384.  
  385.   /* Initialize the user flags. */
  386.  
  387.   uflags = 0;
  388.  
  389.   /* Use the heap stored in the EXE file, if present. */
  390.  
  391.   use_heap ();
  392.  
  393.   /* If this process has been forked, connect to the parent process
  394.      and get the initial block of data.  Among other things, that
  395.      block contains a pointer to the lowest page of stack in use by
  396.      the parent process.  We set the stack pointer to that address
  397.      before copying the stack from the parent process.  As moving the
  398.      stack causes trouble with compiled code, initialization is split
  399.      into two parts, initialize1() and initialize2(), the stack being
  400.      switched between those parts. */
  401.  
  402.   if (fork_flag)
  403.     connect_fork ();
  404. }
  405.  
  406.  
  407. /* Second part of initialization.  Here, initialization is continued
  408.    from initialize1(), after switching stacks and, if this process has
  409.    been forked, copying data from the parent process. */
  410.  
  411. void initialize2 (void)
  412. {
  413.   init_exceptions ();
  414.   receive_signals ();
  415.   init_memory ();
  416.   init_heap ();
  417.   init_fileio ();
  418.   init_processes ();
  419.   new_thread (1, NULL);
  420.  
  421.   /* Install signal handlers copied from the parent process. */
  422.  
  423.   if (fork_flag)
  424.     copy_fork_sig ();
  425. }
  426.  
  427.  
  428. /* There's an error in an option.  Display an error message and abort.
  429.    `opt_errmsg' contains the error message, which depends on where the
  430.    options come from (emxbind or EMXOPT). */
  431.  
  432. static void opt_error (void)
  433. {
  434.   otext (opt_errmsg);
  435.   quit (1);
  436. }
  437.  
  438.  
  439. /* Parse a numeric, decimal, unsigned argument for an option.  Return
  440.    the number and update *P which initially points to the beginning of
  441.    the argument to point to the first character after the number.  The
  442.    number must be terminated by a null character or whitespace.  On
  443.    error, display an error message and abort. */
  444.  
  445. static ULONG opt_number (const char **p)
  446. {
  447.   const char *s;
  448.   ULONG n;
  449.   char flag;
  450.  
  451.   s = *p;
  452.   flag = FALSE; n = 0;
  453.   while (*s >= '0' && *s <= '9')
  454.     {
  455.       /* This check for overflow isn't perfect -- it rejects some
  456.          valid numbers.  However, those numbers aren't valid
  457.          arguments, anyway. */
  458.  
  459.       if (n >= 429496728)
  460.         opt_error ();
  461.       n = n * 10 + (*s - '0');
  462.       flag = TRUE; ++s;
  463.     }
  464.   if (!flag || (*s != 0 && *s != ' ' && *s != '\t'))
  465.     opt_error ();
  466.   *p = s;
  467.   return (n);
  468. }
  469.  
  470.  
  471. /* Parse options.  S points to the null-terminated string of options,
  472.    ERRMSG points to the error message to be used. */
  473.  
  474. static void options (const char *s, const char *errmsg)
  475. {
  476.   char c;
  477.   ULONG n;
  478.  
  479.   /* Make the pointer to the error message available to
  480.      opt_error(). */
  481.  
  482.   opt_errmsg = errmsg;
  483.  
  484.   /* Parse the options, one by one.  Options must be separated by
  485.      whitespace (blank or tab). */
  486.  
  487.   do
  488.     {
  489.       /* Skip whitespace preceding the first option or following any
  490.          other option.  Return when reaching the end of the string. */
  491.  
  492.       do
  493.         {
  494.           c = *s++;
  495.           if (c == 0)
  496.             return;
  497.         } while (c == ' ' || c == '\t');
  498.  
  499.       /* All options start with `-'. */
  500.  
  501.       if (c != '-')
  502.         opt_error ();
  503.  
  504.       /* Parse the various options. */
  505.  
  506.       c = *s++;
  507.       switch (c)
  508.         {
  509.         case 'c':
  510.  
  511.           /* Suppress core dumps. */
  512.  
  513.           nocore_flag = TRUE;
  514.           break;
  515.  
  516.         case 'f':
  517.  
  518.           /* This option was used for setting the maximum stack frame
  519.              size.  Parse the value, and discard it.  As there are
  520.              still some programs which have, say, -f0 set with
  521.              emxbind, we have to accept this option though the maximum
  522.              stack frame size is no longer used. */
  523.  
  524.           n = opt_number (&s);
  525.           if (n != 0 && (n < 4 || n > 32768))
  526.             opt_error ();
  527.           break;
  528.  
  529.         case 'h':
  530.  
  531.           /* Set the number of file handles.  Note that DosSetMaxFH
  532.              cannot lower the number of file handles below the initial
  533.              value for this process (DosSetRelMaxFH can). */
  534.  
  535.           n = opt_number (&s);
  536.           if (n < 10 || n > 256)
  537.             opt_error ();
  538.           DosSetMaxFH (n);
  539.           break;
  540.  
  541.         case 'n':
  542.  
  543.           /* Don't display a harderror popup.  This is done by
  544.              terminating the process instead of returning
  545.              XCPT_CONTINUE_SEARCH from the exception handler. */
  546.  
  547.           no_popup = TRUE;
  548.           break;
  549.  
  550.         case 'q':
  551.  
  552.           /* Quote all command line arguments passed to child
  553.              processes. */
  554.  
  555.           opt_quote = TRUE;
  556.           break;
  557.  
  558.         case 'r':
  559.  
  560.           /* Set the default drive for path names starting with `/' or
  561.              `\'.  The argument of this option is a drive letter. */
  562.  
  563.           if ((*s >= 'A' && *s <= 'Z') || (*s >= 'a' && *s <= 'z'))
  564.             opt_drive = *s++;
  565.           else
  566.             opt_error ();
  567.           break;
  568.  
  569.         case 't':
  570.  
  571.           /* Truncate all components of all path names to 8.3
  572.              format. */
  573.  
  574.           opt_trunc = TRUE;
  575.           break;
  576.  
  577.         case 'x':
  578.  
  579.           /* Expand all command line arguments passed with the `MKS
  580.              Korn shell' method to this process. */
  581.  
  582.           opt_expand = TRUE;
  583.           break;
  584.  
  585.         case 'E':
  586.  
  587.           /* Run debugees in the same session as the debugger. */
  588.  
  589.           debug_same_sess = TRUE;
  590.           break;
  591.  
  592.         case 'K':
  593.  
  594.           /* Don't use DosKillThread. */
  595.  
  596.           dont_doskillthread = TRUE;
  597.           break;
  598.  
  599.         case 'I':
  600.  
  601.           /* Don't abort when the stack size is too small.  This is
  602.              required for old programs which assume that emx.dll
  603.              creates a private stack object if the stack size is too
  604.              small. */
  605.  
  606.           ignore_stack = TRUE;
  607.           break;
  608.  
  609.         case 'V':
  610.  
  611.           /* Display the emx banner. */
  612.  
  613.           otext ("emx " VERSION " (rev " XSTR (REV_INDEX) ")"
  614.                  " -- Copyright (c) 1992-1994 by Eberhard Mattes\r\n");
  615.           break;
  616.  
  617.         case '!':
  618.  
  619.           /* Set debugging flags.  The argument as a (decimal) number,
  620.              interpreted bitwise.  See emxdll.h for the meaning of the
  621.              bits (DEBUG_STDERR, for instance). */
  622.  
  623.           debug_flags = opt_number (&s);
  624.           break;
  625.  
  626.         default:
  627.  
  628.           /* The option is not known, abort. */
  629.  
  630.           opt_error ();
  631.         }
  632.     } while (*s == ' ' || *s == '\t');
  633.  
  634.   /* If there are characters remaining after an option, complain and
  635.      abort. */
  636.  
  637.   if (*s != 0)
  638.     opt_error ();
  639. }
  640.  
  641.  
  642. /* Parse the command line.  Store the argument strings at POOL (unless
  643.    POOL is NULL).  The pointers are stored at VEC (unless VEC is
  644.    NULL). */
  645.  
  646. #define BEGIN    do {
  647. #define END      } while (0)
  648. #define WHITE(C) ((C) == ' ' || (C) == '\t')
  649. #define PUTC(C)  BEGIN ++arg_size; if (pool != NULL) *pool++ = (C); END
  650. #define PUTV     BEGIN ++arg_count; if (vec != NULL) *vec++ = pool; END
  651.  
  652. static void parse_arg (char *pool, char **vec, const char *str1,
  653.                        const char *str3)
  654. {
  655.   const char *s;
  656.   char *flag_ptr;
  657.   int bs, quote;
  658.  
  659.   arg_count = 0; arg_size = 0;
  660.  
  661.   /* Look for the 3rd string -- if it starts with ~ we can get the
  662.      parsed argument words from that string and the following
  663.      strings. */
  664.  
  665.   if (str3[0] == '~' && strcmp (str3 + 1, str1) == 0)
  666.     {
  667.       char flag;
  668.  
  669.       if (opt_expand)
  670.         flag = _ARG_NONZERO;
  671.       else
  672.         flag = _ARG_NONZERO|_ARG_DQUOTE;
  673.       s = str3 + 1;
  674.       while (*s != 0)
  675.         {
  676.           if (*s == '~')
  677.             ++s;
  678.           PUTC (flag); PUTV;
  679.           do
  680.             {
  681.               PUTC (*s);
  682.             } while (*s++ != 0);
  683.         }
  684.     }
  685.   else
  686.     {
  687.       s = str1;
  688.  
  689.       /* argv[0] contains the program name. */
  690.  
  691.       PUTC (_ARG_NONZERO); PUTV;
  692.       do
  693.         {
  694.           PUTC (*s);
  695.         } while (*s++ != 0);
  696.  
  697.       /* Now scan the arguments, one by one. */
  698.  
  699.       for (;;)
  700.         {
  701.           /* Skip leading whitespace. */
  702.  
  703.           while (WHITE (*s))
  704.             ++s;
  705.  
  706.           /* Work is completed when reaching the end of the string. */
  707.  
  708.           if (*s == 0)
  709.             break;
  710.  
  711.           /* Flags will be stored to `*flag_ptr' while parsing the
  712.              current argument.  Initially, no flags are set. */
  713.  
  714.           flag_ptr = pool;
  715.           PUTC (_ARG_NONZERO);
  716.           PUTV;
  717.           bs = 0; quote = 0;
  718.           for (;;)
  719.             {
  720.               if (*s == '"')
  721.                 {
  722.                   /* Backslashes preceding a double quote character
  723.                      are treated specially: A backslash escapes either
  724.                      a backslash or a double quote character. */
  725.  
  726.                   while (bs >= 2)
  727.                     {
  728.                       PUTC ('\\');
  729.                       bs -= 2;
  730.                     }
  731.                   if (bs & 1)
  732.                     PUTC ('"');
  733.                   else
  734.                     {
  735.                       /* The number of backslashes preceding the
  736.                          double quote character is even (including
  737.                          zero), therefore this double quote character
  738.                          starts or ends a quoted string. */
  739.  
  740.                       quote = !quote;
  741.                       if (flag_ptr != NULL)
  742.                         *flag_ptr |= _ARG_DQUOTE;
  743.                     }
  744.  
  745.                   /* We have eaten all backslashes. */
  746.  
  747.                   bs = 0;
  748.                 }
  749.               else if (*s == '\\')
  750.                 {
  751.                   /* Instead of looking ahead to learn whether the
  752.                      backslash is followed by (backslashes and) a
  753.                      double quote character, we count the number of
  754.                      successive backslashes and consider them when
  755.                      processing another character. */
  756.  
  757.                   ++bs;
  758.                 }
  759.               else
  760.                 {
  761.                   /* Process backslashes preceding this character. */
  762.  
  763.                   while (bs != 0)
  764.                     {
  765.                       PUTC ('\\');
  766.                       --bs;
  767.                     }
  768.  
  769.                   /* Whitespace ends the current argument unless we
  770.                      are inside a quoted string. */
  771.  
  772.                   if (*s == 0 || (WHITE (*s) && !quote))
  773.                     break;
  774.                   PUTC (*s);
  775.                 }
  776.               ++s;
  777.             }
  778.  
  779.           /* Mark the end of the argument string. */
  780.  
  781.           PUTC (0);
  782.         }
  783.     }
  784.  
  785.   /* Mark the end of the vector of pointers to argument strings. */
  786.  
  787.   if (vec != NULL)
  788.     *vec = NULL;
  789. }
  790.  
  791.  
  792. /* Build the table of environment pointers; don't store if VEC is
  793.    NULL. */
  794.  
  795. static char **parse_env (char **vec)
  796. {
  797.   char *s;
  798.  
  799.   env_count = 0;
  800.   s = startup_env;
  801.   while (*s != 0)
  802.     {
  803.       ++env_count;
  804.       if (vec != NULL)
  805.         *vec++ = s;
  806.       while (*s != 0)
  807.         ++s;
  808.       ++s;
  809.     }
  810.   if (vec != NULL)
  811.     *vec++ = NULL;
  812.   return (vec);
  813. }
  814.  
  815.  
  816. /* Compute the number and size of argument strings and environment
  817.    strings. */
  818.  
  819. void count_arg_env (void)
  820. {
  821.   parse_env (NULL);
  822.   parse_arg (NULL, NULL, startup_args, argstr3);
  823. }
  824.  
  825.  
  826. /* Build the tables of argument strings and environment strings. */
  827.  
  828. void build_arg_env (char *str, char **vec)
  829. {
  830.   vec = parse_env (vec);
  831.   parse_arg (str, vec, startup_args, argstr3);
  832. }
  833.  
  834.  
  835. /* Temporary table of copied signal handlers, for initializing forked
  836.    process. */
  837.  
  838. static struct signal_entry fork_signals[NSIG];
  839.  
  840. /* Semaphores for communicating with the parent process after a fork. */
  841.  
  842. static HEV fork_req_sem;
  843. static HEV fork_ack_sem;
  844.  
  845. /* Various values reveived from the parent process. */
  846.  
  847. static ULONG fork_msize;
  848. static ULONG fork_addr;
  849. static ULONG fork_ppid;
  850.  
  851.  
  852. /* Convert a hexadecimal number consisting of 8 digits, for
  853.    init_fork().  Return FALSE on failure.  The converted number is
  854.    stored to *DST.  Only lower-case letters are supported. */
  855.  
  856. static int conv_hex8 (const char *s, ULONG *dst)
  857. {
  858.   int i;
  859.   ULONG n;
  860.   unsigned char c;
  861.  
  862.   n = 0;
  863.   for (i = 0; i < 8; ++i)
  864.     {
  865.       c = (unsigned char)*s++;
  866.       if (c >= '0' && c <= '9')
  867.         c -= '0';
  868.       else if (c >= 'a' && c <= 'f')
  869.         c -= 'a' - 10;
  870.       else
  871.         return (FALSE);
  872.       n = (n << 4) | c;
  873.     }
  874.   *dst = n;
  875.   return (TRUE);
  876. }
  877.  
  878.  
  879. /* This process seems to have been forked off; parse the arguments
  880.    passed on the command line. */
  881.  
  882. void init_fork (const char *s)
  883. {
  884.   ++s;                          /* Skip the caret */
  885.   if (!conv_hex8 (s, &fork_addr))
  886.     return;
  887.   s += 8;
  888.   if (*s != ' ')
  889.     return;
  890.   ++s;
  891.   if (!conv_hex8 (s, &fork_ppid))
  892.     return;
  893.   s += 8;
  894.   if (*s != 0)
  895.     return;
  896.   fork_flag = TRUE;
  897. }
  898.  
  899.  
  900. /* Copy the signal handlers from the temporary table into the thread
  901.    data block for thread 1. */
  902.  
  903. void copy_fork_sig (void)
  904. {
  905.   thread_data *td;
  906.  
  907.   td = threads[1];              /* Copy signal handlers of thread 1 */
  908.   memcpy (td->sig_table, fork_signals, sizeof (td->sig_table));
  909. }
  910.  
  911.  
  912. /* Connect to the parent process and process the initial block of
  913.    data. */
  914.  
  915. static void connect_fork (void)
  916. {
  917.   ULONG rc;
  918.   struct fork_data_init *pi;
  919.  
  920.   rc = DosGetSharedMem ((void *)fork_addr, PAG_READ);
  921.   if (rc != 0)
  922.     {
  923.       error (rc, "DosGetSharedMem");
  924.       quit (255);
  925.     }
  926.  
  927.   pi = (struct fork_data_init *)fork_addr;
  928.   fork_msize = pi->msize;
  929.  
  930.   fork_req_sem = pi->req_sem;
  931.   rc = DosOpenEventSem (NULL, &fork_req_sem);
  932.   if (rc != 0)
  933.     {
  934.       error (rc, "DosOpenEventSem");
  935.       quit (255);
  936.     }
  937.  
  938.   fork_ack_sem = pi->ack_sem;
  939.   rc = DosOpenEventSem (NULL, &fork_ack_sem);
  940.   if (rc != 0)
  941.     {
  942.       error (rc, "DosOpenEventSem");
  943.       quit (255);
  944.     }
  945.  
  946.   brk_ptr = pi->brk;
  947.   if (brk_ptr != 0)
  948.     {
  949.       ULONG size;
  950.  
  951.       size = brk_ptr - heap_base;
  952.       if (size != 0)
  953.         setmem (heap_base, size, PAG_DEFAULT | PAG_COMMIT, 0);
  954.     }
  955.  
  956.   if (pi->stack_base != stack_base)
  957.     {
  958.       /* Moving the stack is no longer supported. */
  959.       error (0, "fork stack problem");
  960.       quit (255);
  961.     }
  962.   fork_stack_page = pi->stack_page;
  963.   fork_ebp = pi->reg_ebp;
  964.   memcpy (fork_signals, pi->sig_actions, sizeof (fork_signals));
  965.   umask_bits = pi->umask;
  966.   umask_bits1 = pi->umask1;
  967.   uflags = pi->uflags;
  968. }
  969.  
  970.  
  971. /* Copy data from the parent process. */
  972.  
  973. void copy_fork (void)
  974. {
  975.   fork_data *p;
  976.   ULONG rc;
  977.  
  978.   for (;;)
  979.     {
  980.       reset_event_sem (fork_req_sem);
  981.       DosPostEventSem (fork_ack_sem);
  982.       do
  983.         {
  984.           rc = DosWaitEventSem (fork_req_sem, 500);
  985.  
  986.           /* Repeat if interrupted by a signal.  Repeat if timed out
  987.              and the parent process is still the same (that is, it is
  988.              still alive). */
  989.  
  990.         } while (rc == ERROR_INTERRUPT
  991.                  || (rc == ERROR_TIMEOUT
  992.                      && init_pib_ptr->pib_ulppid == fork_ppid));
  993.       if (rc != 0)
  994.         {
  995.           error (rc, "DosWaitEventSem");
  996.           quit (255);
  997.         }
  998.  
  999.       /* Don't move this assignment outside the loop, unless you make
  1000.          `p' a volatile pointer. */
  1001.  
  1002.       p = (fork_data *)fork_addr;
  1003.  
  1004.       switch (p->req_code)
  1005.         {
  1006.         case FORK_REQ_DONE:
  1007.           /* All data copied, fork completed. */
  1008.           DosPostEventSem (fork_ack_sem);
  1009.           DosCloseEventSem (fork_req_sem);
  1010.           DosCloseEventSem (fork_ack_sem);
  1011.           free_obj ((void *)fork_addr);
  1012.           return;
  1013.  
  1014.         case FORK_REQ_MEM:
  1015.           /* Receive memory from parent process. */
  1016.           memcpy ((void *)p->mem.address, p->mem.shared, p->mem.count);
  1017.           break;
  1018.  
  1019.         default:
  1020.           /* Ignore unknown request codes. */
  1021.           break;
  1022.         }
  1023.     }
  1024. }
  1025.