home *** CD-ROM | disk | FTP | other *** search
/ Aminet 18 / aminetcdnumber181997.iso / Aminet / dev / gcc / ixemulsrc.lha / ixemul / stdlib / execve.c < prev    next >
C/C++ Source or Header  |  1996-12-15  |  19KB  |  665 lines

  1. /*
  2.  *  This file is part of ixemul.library for the Amiga.
  3.  *  Copyright (C) 1991, 1992  Markus M. Wild
  4.  *
  5.  *  This library is free software; you can redistribute it and/or
  6.  *  modify it under the terms of the GNU Library General Public
  7.  *  License as published by the Free Software Foundation; either
  8.  *  version 2 of the License, or (at your option) any later version.
  9.  *
  10.  *  This library is distributed in the hope that it will be useful,
  11.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13.  *  Library General Public License for more details.
  14.  *
  15.  *  You should have received a copy of the GNU Library General Public
  16.  *  License along with this library; if not, write to the Free
  17.  *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  *
  19.  */
  20.  
  21. #define _KERNEL
  22. #include <string.h>
  23. #include "ixemul.h"
  24. #include "kprintf.h"
  25. #include <hardware/intbits.h>
  26. #include <ctype.h>
  27. #include <sys/wait.h>
  28. #include <stdio.h>
  29.  
  30. #include <sys/exec.h>
  31.  
  32. #include "atexit.h"
  33. #define __atexit (u.u_atexit)
  34.  
  35. #define JMP_MAGIC_16(code) ((code[0] & 0xffff0000) == 0x4efa0000)
  36. #define JMP_MAGIC_32(code) ((code[0] & 0xffff0000) == 0x4efb0000)
  37. /* The following define tests for the 'bra' instruction which the
  38.    current assembler generates at the start of a program that is
  39.    compiled with crt0.o. The previous defines are for backwards
  40.    compatibility with older assemblers. */
  41. #define JRA_MAGIC_16(code) ((code[0] & 0xffff0000) == 0x60000000)
  42.  
  43. #define MAGIC_16(code) \
  44.   ((JRA_MAGIC_16 (code) || JMP_MAGIC_16 (code)) && \
  45.    (code[1] & 0xffff) == OMAGIC)
  46.  
  47. #define MAGIC_32(code) \
  48.   (JMP_MAGIC_32 (code) && (code[2] & 0xffff) == OMAGIC)
  49.  
  50. static int compatible_startup (void *code, int argc, char **argv);
  51. static char *quote (char *orig);
  52. static void volatile on_real_stack (BPTR *segs, char **argv, char **environ, int omask);
  53.  
  54. BPTR dup2_BPTR (int);
  55. void readargs_kludge (BPTR);
  56.  
  57. int
  58. execve (const char *path, char * const *argv, char * const *environ)
  59. {
  60.   BPTR *segs;
  61.   u_int omask, err;
  62.   char *extra_args = 0;
  63.  
  64.   KPRINTF (("execve (%s,...)\n", path));
  65.   KPRINTF_ARGV ("argv", argv);
  66.   KPRINTF_ARGV ("environ", environ);
  67.  
  68.   omask = syscall (SYS_sigsetmask, ~0);
  69.  
  70.   segs = __load_seg ((char *)path, &extra_args);
  71.  
  72.   if (segs)
  73.     {
  74.       /* Now it gets somewhat nasty... since I have to revert to the `real'
  75.          stack (since the parent will want its sp back ;-)), I have to save
  76.          the values of this stack frame into registers, or I'll never be
  77.          able to access them afterwards again.. */
  78.       register BPTR *_segs asm ("d2");
  79.       register char **_argv asm ("d3");
  80.       register char **_environ asm ("d4");
  81.  
  82.       /* if we got extra arguments, split them into a 2 el argument vector, and join
  83.        * `argv' */
  84.       if (extra_args && *extra_args)
  85.     {
  86.       char **ap;
  87.           char **nargv;
  88.           int size;
  89.  
  90.           for (size = 0, ap = (char **)argv; *ap; size++, ap++) ;
  91.           nargv = (char **) syscall (SYS_malloc, (size + 4) * 4);
  92.           ap = nargv;
  93.           *ap++ = *argv++;    /* keep the program name */
  94.           *ap++ = extra_args;
  95.           *ap = index (extra_args, ' ');
  96.           if (*ap)
  97.             {
  98.               **ap = 0;
  99.               ++*ap;
  100.               ++ap;
  101.          }
  102.           while ((*ap++ = *argv++)) ;
  103.           argv = (char * const *)nargv;
  104.     }
  105.  
  106.       _segs = segs;
  107.       _argv = (char **)argv;
  108.       _environ = (char **)environ;
  109.  
  110.       KPRINTF (("execve: about to call on_real_stack ()\n"));
  111.       if (u.p_vfork_msg)
  112.         {
  113.           set_sp ((u_int) u.u_save_sp);
  114.           /* fool the optimizer... */
  115.           asm volatile ("" : "=g" (_segs), "=g" (_argv), "=g" (_environ) : "0" (_segs), "1" (_argv), "2" (_environ));
  116.           KPRINTF (("execve () restored native sp\n"));
  117.     }
  118.       on_real_stack (_segs, _argv, _environ, omask);
  119.       /* never reached */
  120.     }
  121.  
  122.   err = errno;
  123.  
  124.   syscall (SYS_sigsetmask, omask);
  125.  
  126.   errno = err;
  127.   KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
  128.   return -1;
  129. }
  130.  
  131.  
  132. char **
  133. dupvec (char **vec)
  134. {
  135.   int n;
  136.   char **vp;
  137.   char **res;
  138.   static char *empty[] = { NULL };
  139.   
  140.   if (! vec)
  141.     return empty;
  142.  
  143.   for (n = 0, vp = vec; *vp; n++, vp++) ;
  144.  
  145.   /* contrary to `real' vfork(), malloc() works in the child on its own
  146.      data, that is it won't clobber anything in the parent  */
  147.   
  148.   res = (char **) syscall (SYS_malloc, (n + 1) * 4);
  149.   if (res)
  150.     {
  151.       for (vp = res; n-- > 0; vp++, vec++)
  152.         *vp = (char *) syscall (SYS_strdup, *vec);
  153.  
  154.       *vp = 0;
  155.     }
  156.  
  157.   return res;
  158. }
  159.  
  160. static void volatile 
  161. on_real_stack (BPTR *segs, char **argv, char **environ, int omask)
  162. {
  163.   int private_startup;
  164.   u_int *code;
  165.   int (*entry) (struct ixemul_base *, int, char **, char **) = NULL;
  166.   struct exec *hdr = NULL;
  167.   int f, sg;
  168.   jmp_buf old_exit;
  169.   u_int old_a4 = 0;
  170.  
  171.   KPRINTF (("entered on_real_stack()\n"));
  172.   /* first make sure that we're later passing on `safe' data to our child, ie.
  173.      copy it from wherever the data is currently stored into child malloc space */
  174.   vfork_own_malloc ();
  175.   if (environ)
  176.     *u.u_environ = dupvec(environ);
  177.   environ = *u.u_environ;
  178.   argv = dupvec(argv);
  179.   KPRINTF_ARGV ("copy of argv", argv);
  180.     
  181.   u.u_segs = (struct my_seg *)segs;
  182.   sg = (long)*segs;
  183.   sg <<= 2;
  184.   u.u_start_pc = sg + 4;
  185.   u.u_end_pc = sg + *(long *)(sg - 4) - 8;
  186.   code = BTOCPTR (*segs);
  187.   code++;    /* code starts at offset 4 */
  188.   
  189.   /* Check whether this program has our magic header.  See crt0.c for details. */
  190.  
  191.   if (MAGIC_16 (code))
  192.     {
  193.       private_startup = 1;
  194.       hdr = (struct exec *) &code[1];
  195.     }
  196.   else if (MAGIC_32 (code))
  197.     {
  198.       private_startup = 1;
  199.       hdr = (struct exec *) &code[2];
  200.     }
  201.   else
  202.     {
  203.       private_startup = 0;
  204.     }
  205.  
  206.   KPRINTF (("magic header %s\n", private_startup ? "found" : "NOT found"));
  207.   KPRINTF (("code[0] = %lx; code[1] = %lx; code[2] = %lx\n", code[0], code[1], code[2]));
  208.  
  209. #if 0
  210.   {
  211.     char **cp;
  212.     KPRINTF (("execve ["));
  213.     for (cp = argv; *cp; cp++) KPRINTF (("%s%s", *cp, cp[1] ? ", " : "], ["));
  214.     for (cp = environ; *cp; cp++) KPRINTF (("%s%s", *cp, cp[1] ? ", " : "]\n"));
  215.   }
  216. #endif
  217.  
  218.   if (private_startup)
  219.     {
  220.       entry = (void *) hdr->a_entry;
  221.       
  222.       if (! entry) private_startup = 0;
  223.     }
  224.       
  225.   /* okay, get ready to turn us into a new process, as much as
  226.      we can actually.. */
  227.  
  228.   /* close all files with the close-on-exec flag set */
  229.   for (f = 0; f < NOFILE; f++)
  230.     {
  231.       if (u.u_ofile[f] && (u.u_pofile[f] & UF_EXCLOSE))
  232.       syscall (SYS_close, f);
  233.     }
  234.  
  235.   /* BIG question what to do with registered atexit() handlers before
  236.      an exec.. Unix for sure does nothing, since the process space is
  237.      physically written over. In the AmigaOS I could (!) imagine
  238.      cases where calling some atexit() handlers (mostly in the case
  239.      of destructors for C++ objects) would result in erronous
  240.      behaving of the program. However, since atexit() handlers also
  241.      serve to get rid of acquired Amiga resources, I morally feel
  242.      obliged to call the handlers.. lets see if this results in
  243.      incompatibilities with programs that expect Unix behavior. (Note
  244.      that I don't call exit() after exeve() returns, I call _exit(),
  245.      and _exit() does not walk the atexit() list).
  246.  
  247.      There is one special case that I catch here, this is stdio. No
  248.      Unix program would ever expect stdio buffers to be flushed by
  249.      an execve() call. So since stdio is in the library I know the
  250.      address of the handler to skip ;-)) */
  251.      
  252.   while (__atexit)
  253.     {
  254.       while (__atexit->ind --)
  255.     {
  256.       /* this is the stdio function to flush all buffers */
  257.       extern void _cleanup();
  258.     
  259.       if (__atexit->fns[__atexit->ind] != _cleanup)
  260.         {
  261.           if (u.u_a4)
  262.             asm volatile ("movel %0, a4" : : "g" (u.u_a4));
  263.           __atexit->fns[__atexit->ind] ();
  264.         }
  265.     }
  266.       __atexit = __atexit->next;
  267.     }
  268.  
  269.   /* `ignored signals remain ignored across an execve, but
  270.       signals that are caught are reset to their default values.
  271.       Blocked signals remain blocked regardless of changes to
  272.       the signal action. The signal stack is reset to be
  273.       undefined'. */
  274.  
  275.   u.u_sigonstack = 0;    /* default = on normal stack */
  276.   u.u_sigintr    = 0;    /* default = don't interrupt syscalls */
  277.   u.p_sigcatch   = 0;    /* no signals caught by user -> SIG_DFL */
  278.   for (f = 0; f < NSIG; f++)  /* reset handlers to SIG_DFL, except for SIG_IGN */
  279.     if (u.u_signal[f] != SIG_IGN)
  280.       u.u_signal[f] = SIG_DFL;
  281.  
  282.   /* what happens when we execute execve() from a signal handler
  283.      that executes on the signal stack? Better don't do this... */
  284.  
  285.   /* deinstall our sigwinch input-handler */
  286.   ix_remove_sigwinch ();
  287.  
  288.   /* clear the a4 pointers */
  289.   bzero((char *)&u - u.u_a4_pointers_size * 4, u.u_a4_pointers_size * 4);
  290.           
  291.   /* save the original exit-jmpbuf, as ix_exec_entry() will destroy
  292.    * it later */
  293.   bcopy (u.u_jmp_buf, old_exit, sizeof (old_exit));
  294.   if (u.p_flag & SFREEA4)
  295.     {
  296.       old_a4 = u.u_a4;
  297.       u.p_flag &= ~SFREEA4;
  298.     }
  299.           
  300.   /* count the arguments */
  301.   for (f = 0; argv[f]; f++) ;
  302.   KPRINTF (("found %ld args\n", f));
  303.  
  304.   KPRINTF (("execve() having parent resume\n"));
  305.   if (u.p_vfork_msg)
  306.     {
  307.       /* make the parent runable again */
  308.       ReplyMsg ((struct Message *) u.p_vfork_msg);
  309.       u.p_vfork_msg = 0;
  310.     }
  311.  
  312.   KPRINTF (("execve() calling entry()\n"));
  313.   {
  314.     char *orig, **name;
  315.     struct Process *me = (struct Process *) FindTask (0);
  316.     struct CommandLineInterface *CLI = BTOCPTR (me->pr_CLI);
  317.     char *bcpl_argv0;
  318.     
  319.     bcpl_argv0 = alloca (strlen (argv[0]) + 4);
  320.     bcpl_argv0 = LONG_ALIGN (bcpl_argv0);
  321.     
  322.     if (CLI)
  323.       {
  324.     name = (char **)&CLI->cli_CommandName;
  325.     orig = *name;
  326.     bcpl_argv0[0] = strlen (argv[0]);
  327.     bcopy (argv[0], &bcpl_argv0[1], bcpl_argv0[0] + 1);
  328.     *name = (char *) CTOBPTR (bcpl_argv0);
  329.       }
  330.     else
  331.       {
  332.     name = (char **)&me->pr_Task.tc_Node.ln_Name;
  333.     orig = *name;
  334.     *name = argv[0];
  335.       }
  336.  
  337.     u.u_oldmask = omask;
  338.     u.u_a4 = 0; /* assume it's not baserelative */
  339.     if (private_startup)
  340.       {
  341.     u.p_xstat = entry (ixemulbase, f, argv, environ);
  342.     if (u.p_flag & SFREEA4) /* free data segment allocated by a pure executable */
  343.       {
  344.         kfree ((void *)(u.u_a4 - 0x7ffe));
  345.         u.p_flag &= ~SFREEA4;
  346.       }
  347.       }
  348.     else
  349.       {
  350.     /*  Disable ctrl-C handling, otherwise it would be possible that
  351.      *  this process would be killed, while the child was still running.
  352.      */
  353.         omask = syscall(SYS_sigsetmask, ~0);
  354.         compatible_startup (code, f, argv);
  355.         syscall(SYS_sigsetmask, omask);
  356.       }
  357.  
  358.     *name = orig;
  359.   }
  360.  
  361.   __free_seg (segs);
  362.  
  363.   if (old_a4)
  364.     {
  365.       u.u_a4 = old_a4; 
  366.       u.p_flag |= SFREEA4;
  367.     }
  368.   KPRINTF (("old program doing _exit(%ld)\n", f));
  369.   /* and fake an _exit */
  370.   _clean_longjmp (old_exit, 1);
  371. }  
  372.  
  373.  
  374. /* some rather rude support to start programs that don't have a struct exec
  375.  * information at the beginning.
  376.  * 1.3 NOTE: This will only start plain C programs, nothing that smells like
  377.  *           BCPL. Limited support for ReadArgs() style parsing is here, but not
  378.  *         everything is set up that would have to be set up for BCPL programs
  379.  *         to feel at home. Also don't use Exit() in those programs, it wouldn't
  380.  *         find what it expects on the stack....
  381.  */
  382. static int
  383. compatible_startup (void *code, int argc, char **argv)
  384. {
  385.   char *al;
  386.   int max, res;
  387.   u_int oldsigalloc;
  388.   struct Process *me = (struct Process *) FindTask (0);
  389.   
  390.   KPRINTF (("entered compatible_startup()\n"));
  391.   KPRINTF (("argc = %ld\n", argc));
  392.   KPRINTF_ARGV ("argv", argv);
  393.  
  394.   /* ignore the command name ;-) */
  395.   argv++;
  396.  
  397.   max = 1024;
  398.   al = (char *) kmalloc (max);
  399.   res = -1;
  400.   if (al)
  401.     {
  402.       char *cp;
  403.       BPTR old_cis, old_cos, old_ces;
  404.       BPTR dup_cis, dup_cos, dup_ces;
  405.       void *old_trapdata, *old_trapcode;
  406.       int old_flags;
  407.       void *old_launch, *old_switch;
  408.       struct file *f;
  409.  
  410.       for (cp = al; *argv; )
  411.         {
  412.       char *newel = quote (*argv);
  413.           int elsize = strlen (newel ? newel : *argv) + 2;
  414.       KPRINTF (("arg [%s] quoted as [%s]\n", *argv, newel ? newel : *argv));
  415.           
  416.           if (cp + elsize >= al + max)
  417.             {
  418.           char *nal;
  419.               max <<= 1;
  420.               nal = (char *) krealloc (al, max);
  421.               if (! nal) break;
  422.               cp = nal + (cp-al);
  423.               al = nal;
  424.         }
  425.  
  426.       strcpy (cp, newel ? newel : *argv);
  427.       cp += elsize - 2;
  428.       *cp++ = ' ';
  429.       *cp = 0;
  430.       if (newel) kfree (newel);
  431.       ++argv;
  432.         }
  433.       
  434.       /* BCPL weirdness ... */
  435.       *cp++ = '\n';
  436.       *cp = 0;
  437.  
  438.       KPRINTF (("BCPL cmd line = [%s]\n", al));
  439.  
  440.       /* problem with RunCommand: the allocated signal mask is not reset
  441.      for the new process, thus if several RunCommands are nested, a
  442.      late started process might run out of signals. This behavior makes
  443.      no sense, since the starting process is *suspended* while the `child'
  444.      is running, thus it doesn't need its signals in the meantime ! */
  445.  
  446.       oldsigalloc = me->pr_Task.tc_SigAlloc & 0xffff0000;    /* hacky...*/
  447.       me->pr_Task.tc_SigAlloc &= 0xffff;
  448.  
  449.       /* cleanup as much of ixemul.library as possible, so that the started
  450.          process can take over */
  451.       old_flags             = me->pr_Task.tc_Flags;
  452.       me->pr_Task.tc_Flags  = u.u_otask_flags;
  453.       old_launch            = me->pr_Task.tc_Launch;
  454.       me->pr_Task.tc_Launch = u.u_olaunch; /* restoring this disables our signals */
  455.       old_switch            = me->pr_Task.tc_Switch;
  456.       me->pr_Task.tc_Switch = u.u_oswitch;
  457.       RemIntServer (INTB_VERTB, & u.u_itimerint);
  458.  
  459.       /* limited support (part 2 ;-)) for I/O redirection on old programs
  460.          If we're redirecting to a plain file, don't go thru a IXPIPE, 
  461.          temporarily use our DOS files in that case. Any other file type
  462.          is routed thru an IXPIPE though. */
  463.       
  464.       f = u.u_ofile[0];
  465.       old_cis = dup_cis = 0;
  466.       
  467.       /* I do wish I knew why pty's need to go though ixpipe:. But if I don't
  468.          do this, the output simply disappears :-( */
  469.  
  470.       if (f && f->f_type == DTYPE_FILE && memcmp(f->f_name, "/fifo/pty", 9))
  471.         {
  472.           old_cis = SelectInput (CTOBPTR (f->f_fh));
  473.           readargs_kludge (CTOBPTR (f->f_fh));
  474.         }
  475.       else if (!f)
  476.         {
  477.           int fd = syscall(SYS_open, "/dev/null", 0);
  478.     
  479.           dup_cis = dup2_BPTR (fd);
  480.           syscall(SYS_close, fd);
  481.         }
  482.       else
  483.         dup_cis = dup2_BPTR (0);
  484.       if (dup_cis)
  485.         {
  486.           old_cis = SelectInput (dup_cis);
  487.           readargs_kludge (dup_cis);
  488.         }
  489.  
  490.       f = u.u_ofile[1];
  491.       old_cos = dup_cos = 0;
  492.       if (f && f->f_type == DTYPE_FILE && memcmp(f->f_name, "/fifo/pty", 9))
  493.         {
  494.           old_cos = SelectOutput (CTOBPTR (f->f_fh));
  495.         }
  496.       else if (!f)
  497.         {
  498.           int fd = syscall(SYS_open, "/dev/null", 1);
  499.     
  500.           dup_cos = dup2_BPTR (fd);
  501.           syscall(SYS_close, fd);
  502.         }
  503.       else
  504.         dup_cos = dup2_BPTR (1);
  505.       if (dup_cos)
  506.         old_cos = SelectOutput (dup_cos);
  507.  
  508.       old_ces = me->pr_CES;
  509.       dup_ces = 0;
  510.       f = u.u_ofile[2];
  511.       if (f && f->f_type == DTYPE_FILE && memcmp(f->f_name, "/fifo/pty", 9))
  512.         {
  513.       me->pr_CES = CTOBPTR (f->f_fh);
  514.         }
  515.       else if (!f)
  516.         {
  517.           int fd = syscall(SYS_open, "/dev/null", 2);
  518.     
  519.           dup_ces = dup2_BPTR (fd);
  520.           syscall(SYS_close, fd);
  521.         }
  522.       else
  523.         dup_ces = dup2_BPTR (2);
  524.       me->pr_CES = dup_ces ? : old_ces;
  525.  
  526.       /* BEWARE that after this reset no library functions can be
  527.          called any longer until the moment where trapdata is 
  528.          reinstalled !! */
  529.  
  530. #ifndef NOTRAP
  531.       old_trapcode = me->pr_Task.tc_TrapCode;
  532.       me->pr_Task.tc_TrapCode = u.u_otrap_code;
  533. #endif
  534.       old_trapdata = getuser(me);
  535.       getuser(me) = 0;
  536.  
  537.       {
  538.     struct CommandLineInterface *CLI = BTOCPTR (me->pr_CLI);
  539.     u_int stack_size = CLI ? CLI->cli_DefaultStack * 4 : me->pr_StackSize;
  540.  
  541.     /* Note: The use of RunCommand() here means, that we *waste* the
  542.              entire stack space allocated for this process! If someone
  543.              comes up with a clever trick (probably involving StackSwap ())
  544.              where the stack of this process can be freed before calling
  545.              RunCommand (), lots of users with memory problems would be
  546.              thankful! */
  547.     res = RunCommand (CTOBPTR (code) - 1, stack_size, al, cp - al);
  548.       }
  549.       /* reinstall enough of ixemul to be able to finish cleanly 
  550.          (the recent addition of an ix_sleep() at the end of a vfork'd
  551.           process makes it necessary to reinstall the signalling facilities!) */
  552.       getuser(me) = old_trapdata;
  553. #ifndef NOTRAP
  554.       me->pr_Task.tc_TrapCode = old_trapcode;
  555. #endif
  556.       /* have to do this, or ix_close() is not able to RemoveIntServer .. */
  557.       AddIntServer (INTB_VERTB, & u.u_itimerint);
  558.       me->pr_Task.tc_Launch = old_launch;
  559.       me->pr_Task.tc_Switch = old_switch;
  560.       me->pr_Task.tc_Flags  = old_flags;
  561.  
  562.       /* Some programs can set ixemul Signals. This can happen because
  563.          ixemul resets the allocated signal mask in the Task structure.
  564.          Make very sure they're off, or Enforcer hits will be the result
  565.          because the 'deathmessage-handshake' returns too early. */
  566.       SetSignal(0, ~0);
  567.  
  568.       kfree (al);
  569.  
  570.       if (old_cis)
  571.     SelectInput (old_cis);
  572.       if (old_cos)
  573.     Flush (SelectOutput (old_cos));
  574.       me->pr_CES = old_ces;
  575.  
  576.       if (dup_cis)
  577.         Close (dup_cis);
  578.       if (dup_cos)
  579.         Close (dup_cos);
  580.       if (dup_ces)
  581.         Close (dup_ces);
  582.  
  583.       me->pr_Task.tc_SigAlloc |= oldsigalloc;
  584.     }
  585.  
  586.   u.p_xstat = W_EXITCODE(res, 0);
  587.   return res;
  588. }
  589.  
  590. static char *
  591. quote (char *orig)
  592. {
  593.   int i;
  594.   char *new, *cp;
  595.   
  596.   i = strlen (orig);
  597.   
  598.   if (strpbrk (orig, "\"\'\\ \t\n"))
  599.     {
  600.       /* worst case, each character needs quoting plus starting and ending " */
  601.       new = (char *) kmalloc (i * 2 + 3);
  602.       if (! new) return 0;
  603.  
  604.       cp = new;
  605.       *cp++ = '"';
  606.       while (*orig)
  607.         {
  608.           if (index ("\"\\", *orig))
  609.             *cp++ = '\\';
  610.       *cp++ = *orig++;
  611.     }
  612.       *cp++ = '"';
  613.       *cp = 0;
  614.       
  615.       return new;
  616.     }
  617.   else
  618.     return 0;    /* means `just use the original string' */
  619. }   
  620.  
  621. /* try to obtain a DOS filehandle on the specified descriptor. This only
  622.    works, if the user has mounted IXPIPE: */
  623. BPTR
  624. dup2_BPTR (int fd)
  625. {
  626.   long id;
  627.   char name[20];
  628.   
  629.   id = syscall(SYS_fcntl, fd, F_EXTERNALIZE, 0);
  630.   if (id >= 0)
  631.     {
  632.       sprintf (name, "IXPIPE:%x", (unsigned int)id);
  633.       /* 0x4242 is a magic packet understood by IXPIPE: to F_INTERNALIZE id */
  634.       return Open (name, 0x4242);
  635.     }
  636.  
  637.   return 0;
  638. }
  639.  
  640.  
  641. /* the mysteries of DOS seem to never want to take an end... */
  642. void
  643. readargs_kludge (BPTR bp)
  644. {
  645.   int ch;
  646.   static const int EOS_CHAR = -1;
  647.  
  648. #if 0
  649.   /* the autodocs say this bug is fixed after v37, well, perhaps that was a
  650.      very deep wish, nevertheless unheard by dos...
  651.      Without this kludge, you have to actually press return if stdin is not
  652.      redirected...
  653.      Thanks mbs: without your shell code I would never have guessed that 
  654.                  something that weird could be possible....
  655.    */
  656.   if (ix.ix_dos_base->lib_Version <= 37)
  657. #endif
  658.     {
  659.       ch = UnGetC (bp, EOS_CHAR) ? 0 : '\n';
  660.       while ((ch != '\n') && (ch != EOS_CHAR))
  661.         ch = FGetC (bp);
  662.       Flush (bp);
  663.     }
  664. }
  665.