home *** CD-ROM | disk | FTP | other *** search
/ Aminet 18 / aminetcdnumber181997.iso / Aminet / dev / gcc / ixemulsrc.lha / ixemul / library / _cli_parse.c < prev    next >
C/C++ Source or Header  |  1996-12-11  |  14KB  |  435 lines

  1. /*
  2.  *  This file is part of ixemul.library for the Amiga.
  3.  *  Copyright (C) 1991, 1992  Markus M. Wild
  4.  *  Portions (C) 1995 Jeff Shepherd
  5.  *
  6.  *  This library is free software; you can redistribute it and/or
  7.  *  modify it under the terms of the GNU Library General Public
  8.  *  License as published by the Free Software Foundation; either
  9.  *  version 2 of the License, or (at your option) any later version.
  10.  *
  11.  *  This library 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 GNU
  14.  *  Library General Public License for more details.
  15.  *
  16.  *  You should have received a copy of the GNU Library General Public
  17.  *  License along with this library; if not, write to the Free
  18.  *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  19.  *
  20.  *  _cli_parse.c,v 1.1.1.1 1994/04/04 04:29:41 amiga Exp
  21.  *
  22.  *  _cli_parse.c,v
  23.  * Revision 1.1.1.1  1994/04/04  04:29:41  amiga
  24.  * Initial CVS check in.
  25.  *
  26.  *  Revision 1.3  1992/08/09  20:41:17  amiga
  27.  *  change to use 2.x header files by default
  28.  *
  29.  *  Revision 1.2  1992/07/04  19:09:27  mwild
  30.  *  make stderr (desc 2) *really* read/write, don't just say so...
  31.  *
  32.  * Revision 1.1  1992/05/14  19:55:40  mwild
  33.  * Initial revision
  34.  *
  35.  */
  36.  
  37. /*
  38.  *    This routine is called from the _main() routine and is used to
  39.  *    parse the arguments passed from the CLI to the program. It sets
  40.  *    up an array of pointers to arguments in the global variables and
  41.  *    and sets up _argc and _argv which will be passed by _main() to
  42.  *    the main() procedure. If no arguments are ever going to be
  43.  *    parsed, this routine may be replaced by a stub routine to reduce
  44.  *    program size.
  45.  *
  46.  */
  47.  
  48. #define _KERNEL
  49. #include "ixemul.h"
  50. #include "kprintf.h"
  51. #include <stdio.h>
  52. #include <ctype.h>
  53. #include <string.h>
  54. #include <glob.h>
  55.  
  56. #ifndef MAXPATHLEN
  57. #define MAXPATHLEN 1024
  58. #endif
  59.  
  60. extern int __read(), __write(), __ioctl(), __fselect(), __close();
  61.  
  62. /* We will store all arguments in this double linked list, the list
  63.  * is always sorted according to elements, ie. order of given arguments
  64.  * is preserved, but all elements, that get expanded will be sorted
  65.  * alphabetically in this list. */
  66.  
  67. struct ArgList {
  68.   struct MinList al_list;  /* the list - head */
  69.   long         al_num;   /* number of arguments in the whole list */
  70. };
  71.  
  72. struct Argument {
  73.   struct MinNode a_node;   /* the link in the arg-list */
  74.   char        *a_arg;       /* a malloc'd string, the argument */
  75. };
  76.  
  77. /* insert a new argument into the argument vector, we have to keep the
  78.  * vector sorted, but only element-wise, otherwise we would break the
  79.  * order of arguments, and "copy b a" is surely not the same as "copy a b"..
  80.  * so we don't scan the whole list, but start with element "start",
  81.  * if set, else we start at the list head */
  82.  
  83. static void 
  84. AddArgument (struct ArgList *ArgList,
  85.          struct Argument *start, struct Argument *arg, long size)
  86. {
  87.   register struct Argument *el, *nel;
  88.  
  89.   /* depending on "start", start scan for right position in list at
  90.    * successor of start or at head of list  */
  91.   for (el = (struct Argument *)
  92.         (start ? start->a_node.mln_Succ : ArgList->al_list.mlh_Head);
  93.        (nel = (struct Argument *) el->a_node.mln_Succ);
  94.        el = nel)
  95.     if (strcmp (el->a_arg, arg->a_arg) > 0) break;
  96.  
  97.   Insert ((struct List *) ArgList, (struct Node *) arg, (struct Node *) el);
  98.  
  99.   /* and bump up the argument counter once */
  100.   ++ ArgList->al_num;
  101. }
  102.  
  103. /* if an argument contains one or more of these characters, we have to
  104.  * call the glob() stuff, else don't bother expanding and
  105.  * quickly append to list. Here's the meaning of all these characters, some
  106.  * seem to be not widely used:
  107.  * *    match any number (incl. zero) of characters
  108.  * #?    match any number (incl. zero) of characters (for Amiga compatibility)
  109.  * []   match any character that's contained in the set inside the brackets
  110.  * ?    match any character (exactly one)
  111.  * !    negate the following expression
  112.  */
  113.  
  114. #define iswild(ch) (index ("*[!?#", ch) ? 1 : 0)
  115.  
  116. void
  117. _cli_parse(struct Process *this_proc, long alen, char *_aptr,
  118.        int *argc, char ***argv)
  119. {
  120.   char *arg0;
  121.   struct CommandLineInterface *cli;
  122.   struct Argument *arg, *narg;
  123.   char *line, *next, *lmax, **cpp;
  124.   int do_expand;
  125.   int arglen;
  126.   char *aptr;
  127.   struct ArgList ArgList;
  128.   int expand_cmd_line = u.u_expand_cmd_line;
  129.   struct file *fin, *fout;
  130.   int fd;  
  131.   BPTR fh;
  132.   int omask;
  133.  
  134.   KPRINTF (("entered _cli_parse()\n"));
  135.   KPRINTF (("command line length = %ld\n", alen));
  136.   KPRINTF (("command line = '%s'\n", _aptr));
  137.  
  138.   /* this stuff has been in ix_open before, but it really belongs here, since
  139.    * I don't want it to happen by default on OpenLibrary, since it would
  140.    * disturb any vfork() that wants to inherit files from its parent 
  141.    */
  142.  
  143.   omask = syscall (SYS_sigsetmask, ~0);
  144.  
  145.   if (! falloc (&fin, &fd))
  146.     {
  147.       /*
  148.        * NOTE: if there's an error creating one of the standard
  149.        *       descriptors, we just go on, the descriptor in
  150.        *       question will then not be set up, no problem ;-)
  151.        */
  152.       if (fd != 0)
  153.     ix_warning("allocated stdin is not fd #0!");
  154.                
  155.       if (! falloc (&fout, &fd))
  156.     {
  157.       if (fd != 1)
  158.         ix_warning("allocated stdout is not fd #1!");
  159.  
  160.       if ((fh = Input ()))
  161.         {
  162.           fin->f_name = "<Standard Input>";
  163.           fin->f_fh   = (struct FileHandle *)BTOCPTR(fh);
  164.           __init_std_packet(&fin->f_sp);
  165.           __init_std_packet(&fin->f_select_sp);
  166.           __fstat(fin);
  167.           fin->f_flags = FREAD | FEXTOPEN;
  168.           fin->f_ttyflags = IXTTY_ICRNL;
  169.           fin->f_type  = DTYPE_FILE;
  170.           fin->f_read  = __read;
  171.           fin->f_write = 0;
  172.           fin->f_ioctl = __ioctl;
  173.           fin->f_close = __close;
  174.           fin->f_select= __fselect;
  175.         }
  176.       else
  177.         {
  178.           u.u_ofile[0] = 0;
  179.           fin->f_count--;
  180.         }
  181.                     
  182.       if ((fh = Output ()))
  183.         {
  184.           fout->f_name = "<Standard Output>";
  185.           fout->f_fh   = (struct FileHandle *)BTOCPTR(fh);
  186.           __init_std_packet(&fout->f_sp);
  187.           __init_std_packet(&fout->f_select_sp);
  188.           __fstat(fout);
  189.           fout->f_flags = FWRITE|FEXTOPEN;
  190.           fout->f_ttyflags = IXTTY_OPOST | IXTTY_ONLCR;
  191.           fout->f_type  = DTYPE_FILE;
  192.           fout->f_read  = 0;
  193.           fout->f_write = __write;
  194.           fout->f_ioctl = __ioctl;
  195.           fout->f_close = __close;
  196.           fout->f_select= __fselect;
  197.         }
  198.       else
  199.         {
  200.           u.u_ofile[1] = 0;
  201.           fout->f_count--;
  202.         }
  203.  
  204.       /* deal with stderr. Seems this was a last minute addition to 
  205.          dos 2, it's hardly documented, there are no access functions,
  206.          nobody seems to know what to do with pr_CES...
  207.          If pr_CES is valid, then we use it, otherwise we open the
  208.          console. */
  209.  
  210.       fd = -1;
  211.       if ((fh = this_proc->pr_CES))
  212.         {
  213.           struct file *fp;
  214.  
  215.           if (!falloc (&fp, &fd))
  216.             {
  217.           fp->f_name = "<Standard Error>";
  218.           fp->f_fh   = (struct FileHandle *)BTOCPTR(fh);
  219.           __init_std_packet(&fp->f_sp);
  220.               __init_std_packet(&fp->f_select_sp);
  221.           __fstat(fp);
  222.           fp->f_flags = FREAD|FWRITE|FEXTOPEN;
  223.               fp->f_ttyflags = IXTTY_OPOST | IXTTY_ONLCR;
  224.           fp->f_type  = DTYPE_FILE;
  225.           fp->f_read  = __read;
  226.           fp->f_write = __write;
  227.           fp->f_ioctl = __ioctl;
  228.           fp->f_close = __close;
  229.           fp->f_select= __fselect;
  230.             }
  231.         }
  232.         /* Apparently use of CONSOLE: gave problems with
  233.                Emacs, so we continue to use "*" instead. */
  234.  
  235.             /* Here is some more information on this from Joerg Hoehle:
  236.              *
  237.          * While writing fifolib38_1 I found that console handlers are sent
  238.          * ACTION_FIND* packets with names of either "*" or "Console:", depending
  239.          * on what the user typed. Old handlers that do not recognize "CONSOLE:"
  240.          * will produce strange results which could explain the above problems.
  241.              *
  242.          * That's the reason why
  243.          *     echo foo >*    (beware of * expansion in a non-AmigaOS shell)
  244.          * works in an Emacs shell buffer, whereas
  245.          *     echo foo >console:
  246.          * won't with fifolib prior to version 38.1.
  247.              *
  248.          * I believe that programs opening stderr should continue to open "*" for
  249.          * compatibility reasons. Opening "CONSOLE:" first and "*" if it fails is
  250.          * _not_ a solution: for example, FIFO: (prior to 38.1) accepts the
  251.          * Open("CONSOLE:") call, giving a FIFO that can be neither read nor
  252.          * written to :-(
  253.          */
  254.       if (fd == -1)
  255.         fd = syscall(SYS_open, "*", 2);
  256.       if (fd > -1 && fd != 2)
  257.         {
  258.           syscall(SYS_dup2, fd, 2);
  259.           syscall(SYS_close, fd);
  260.         }
  261.  
  262.     } /* falloc (&fout, &fd) */
  263.     } /* falloc (&fin, &fd) */
  264.  
  265.   aptr = alloca (alen + 1);
  266.   memcpy(aptr, _aptr, alen + 1);
  267.  
  268.   cli = (struct CommandLineInterface *) BTOCPTR (this_proc->pr_CLI);
  269.   arg0 = (char *) BTOCPTR (cli->cli_CommandName);
  270.  
  271.   /* init our argument list */
  272.   NewList ((struct List *) &ArgList);
  273.  
  274.   /* lets start humble.. no arguments at all:-)) */
  275.   ArgList.al_num = 0;
  276.  
  277.   /* find end of command-line, stupid BCPL-stuff.. line can end
  278.    * either with \n or with \0 .. */
  279.   for (lmax = aptr; *lmax && *lmax != '\n' && *lmax != '\r'; ++lmax) ;
  280.   *lmax = 0;
  281.  
  282.   /* loop over all arguments, expand all */
  283.   for (line = aptr, narg = arg = 0; line < lmax; )
  284.     {
  285.       do_expand = 0;
  286.  
  287.       KPRINTF (("remaining cmd line = '%s'\n", aptr));
  288.       /* skip over leading whitespace */
  289.       while (isspace (*line)) ++line;
  290.       if (line >= lmax)
  291.     break;
  292.  
  293.       /* if argument starts with ", don't expand it and remove the " */
  294.       if (*line == '\"')
  295.     {
  296.       KPRINTF (("begin quoted argument at '%s'\n", line));
  297.       /* scan for end of quoted argument, this can be either at
  298.        * end of argumentline or at a second " */
  299.       line++;
  300.       next = line;
  301.       while (next < lmax && *next != '\"')
  302.         {
  303.           /* Prevent that the loop terminates due to an escaped quote.
  304.            * However, if the character after the quote is a space, then
  305.            * it is ambiguous whether or not the quote is escaped or is
  306.            * the end of the argument.  Consider what happens when you give
  307.            * /bin/sh a 'FS=\' argument.  This gets passed to ixemul.library
  308.            * as "FS=\" <other args> */
  309.           if ((*next == '\'' || *next == '\\') && next[1] == '\"')
  310.         {
  311.           /* in this case we have to shift the whole remaining
  312.            * line one position to the left to skip the 
  313.            * escape-character */
  314.           bcopy (next + 1, next, (lmax - next) + 1);
  315.           --lmax;
  316.         }
  317.  
  318.           ++next;
  319.         }
  320.       *next = 0;
  321.       KPRINTF (("got arg '%s'\n", line));
  322.     }
  323.       else
  324.     {
  325.           /* strange kind of BCPL-quoting, if you want to get a " thru,
  326.            * you have to quote it with a ', eq. HELLO'"WORLD'" will preserve
  327.            * the " inside the argument. Since hardly anyone knows this
  328.            * "feature", I allow for the more common Unix-like escaping, ie
  329.            * \" will give you the same effect as '". */
  330.           if ((*line == '\'' || *line == '\\') && line[1] == '\"')
  331.             {
  332.             KPRINTF (("found escaped quote at '%s'\n", line));
  333.             line++;
  334.           }
  335.       /* plain, vanilla argument.. */
  336.       next = line + 1;
  337.       /* check, whether we have to run thru the expander, or
  338.        * if we rather can just copy over the whole argument */
  339.       do_expand = iswild (*line);
  340.       /* skip over element and make it 0-terminated .. */
  341.       while (next < lmax && !isspace (*next))
  342.         {
  343.           do_expand |= iswild (*next);
  344.           if ((*next == '\'' || *next == '\\') && next[1] == '\"')
  345.         {
  346.           bcopy (next + 1, next, (lmax - next) + 1);
  347.           --lmax;
  348.         }
  349.  
  350.           ++next;
  351.         }
  352.       *next = 0;
  353.     }
  354.  
  355.       if (expand_cmd_line && do_expand)
  356.     {
  357.           glob_t g;
  358.           char **p;
  359.           
  360.           syscall (SYS_sigsetmask, omask);
  361.           syscall (SYS_glob, line,
  362.                    ((ix.ix_flags & ix_unix_pattern_matching_case_sensitive) ? 0 : GLOB_NOCASE) |
  363.                    ((ix.ix_flags & ix_allow_amiga_wildcard) ? GLOB_AMIGA : 0) |
  364.                GLOB_NOCHECK, NULL, &g);
  365.           omask = syscall (SYS_sigsetmask, ~0);
  366.           for (p = g.gl_pathv; *p; p++)
  367.             {
  368.               arg = (struct Argument *)syscall(SYS_malloc, sizeof(*arg));
  369.               arg->a_arg = *p;
  370.               AddArgument(&ArgList, narg, arg, strlen(*p));
  371.               narg = (struct Argument *) ArgList.al_list.mlh_TailPred;
  372.             }
  373.           syscall(SYS_free, g.gl_pathv);
  374.     }
  375.       else  /* ! do_expand */
  376.     {
  377.       /* just add the argument "as is" */
  378.       arg = (struct Argument *) syscall (SYS_malloc, sizeof (*arg));
  379.       arglen = strlen (line);
  380.       arg->a_arg = (char *) syscall (SYS_malloc, arglen+1);
  381.       strcpy (arg->a_arg, line);
  382.       AddArgument (&ArgList, narg, arg, arglen);
  383.     }
  384.  
  385.       narg = (struct Argument *) ArgList.al_list.mlh_TailPred;
  386.       line = next + 1;
  387.     } /* for */
  388.  
  389.   /* prepend the program name */
  390.   arg = (struct Argument *) syscall (SYS_malloc, sizeof (*arg));
  391.  
  392.   /* some stupid shells (like Wsh...) pass the WHOLE path of the
  393.    * started program. We simply cut off what we don't want ;-)) */
  394.   for (arglen = arg0[0]; arglen; --arglen)
  395.     if (arg0[arglen] == ':' || arg0[arglen] == '/')
  396.       break;
  397.  
  398.   line = &arg0[arglen+1];
  399.   arglen = arg0[0] - arglen;
  400.  
  401.   arg->a_arg = (char *) syscall (SYS_malloc, arglen + 1);
  402.  
  403.   strncpy (arg->a_arg, line, arglen);
  404.   arg->a_arg[arglen] = 0;
  405.   /* did I tell you, that I like those kernel list calls ?? */
  406.   AddHead ((struct List*) &ArgList, (struct Node*) arg);
  407.   ++ ArgList.al_num;
  408.  
  409.   /* build _argv array */
  410.   *argv = (char **) syscall (SYS_malloc, (ArgList.al_num+1) * sizeof(char *));
  411.   for (cpp = *argv, arg = (struct Argument *) ArgList.al_list.mlh_Head;
  412.        (narg = (struct Argument *) arg->a_node.mln_Succ);
  413.        arg = narg)
  414.     *cpp++ = arg->a_arg;
  415.  
  416.   /* guarantee last element == 0 */
  417.   *cpp = 0;
  418.   KPRINTF_ARGV ("argv", *argv);
  419.   *argc = ArgList.al_num;
  420.   
  421.   if (u.u_ixnetbase)
  422.     {
  423.       int daemon = netcall(NET_init_inet_daemon, argc, argv);
  424.  
  425.       if (daemon >= 0)
  426.         set_socket_stdio(daemon);
  427.     }
  428.  
  429.   KPRINTF (("argc = %ld\n", *argc));
  430.   KPRINTF (("leaving _cli_parse()\n"));
  431.  
  432.   syscall (SYS_setsid); /* setup new session */
  433.   syscall (SYS_sigsetmask, omask);
  434. }
  435.