home *** CD-ROM | disk | FTP | other *** search
/ Aminet 18 / aminetcdnumber181997.iso / Aminet / misc / emu / AROSdev.lha / AROS / rom / dos / readargs.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-01-27  |  13.2 KB  |  496 lines

  1. /*
  2.     (C) 1995-96 AROS - The Amiga Replacement OS
  3.     $Id: readargs.c,v 1.9 1997/01/27 00:36:28 ldp Exp $
  4.     $Log: readargs.c,v $
  5.     Revision 1.9  1997/01/27 00:36:28  ldp
  6.     Polish
  7.  
  8.     Revision 1.8  1996/12/09 13:53:38  aros
  9.     Added empty templates for all missing functions
  10.  
  11.     Moved #include's into first column
  12.  
  13.     Revision 1.7  1996/11/14 08:54:18  aros
  14.     Some more changes
  15.  
  16.     Revision 1.6  1996/10/24 15:50:35  aros
  17.     Use the official AROS macros over the __AROS versions.
  18.  
  19.     Revision 1.5  1996/09/13 17:50:08  digulla
  20.     Use IPTR
  21.  
  22.     Revision 1.4  1996/08/16 14:04:08  digulla
  23.     Fixed bug in docs. Bugfix in code is still pending.
  24.  
  25.     Revision 1.3  1996/08/13 13:52:50  digulla
  26.     Replaced <dos/dosextens.h> by "dos_intern.h" or added "dos_intern.h"
  27.     Replaced AROS_LA by AROS_LHA
  28.  
  29.     Revision 1.2  1996/08/01 17:40:56  digulla
  30.     Added standard header for all files
  31.  
  32.     Desc:
  33.     Lang: english
  34. */
  35. #include <exec/memory.h>
  36. #include <proto/exec.h>
  37. #include <dos/rdargs.h>
  38. #include <dos/dosextens.h>
  39. #include "dos_intern.h"
  40.  
  41. /*****************************************************************************
  42.  
  43.     NAME */
  44. #include <proto/dos.h>
  45.  
  46.     AROS_LH3(struct RDArgs *, ReadArgs,
  47.  
  48. /*  SYNOPSIS */
  49.     AROS_LHA(STRPTR,          template, D1),
  50.     AROS_LHA(IPTR *,          array,    D2),
  51.     AROS_LHA(struct RDArgs *, rdargs,   D3),
  52.  
  53. /*  LOCATION */
  54.     struct DosLibrary *, DOSBase, 133, Dos)
  55.  
  56. /*  FUNCTION
  57.     Parses the commandline, a given string or Input() and fills
  58.     an argument array according to the options template given.
  59.     The array must be initialized to the wanted defaults before
  60.     each call to ReadArgs(). If the rdargs argument is NULL
  61.     ReadArgs() tries to parse the commandline and continues
  62.     on the input channel if it just consists of a single '?',
  63.     prompting the user for input.
  64.  
  65.     INPUTS
  66.     template - Template string. The template string is given as
  67.            a number of options separated by ',' and modified
  68.            by '/' modifiers, e.g. 'NAME,WIDTH/N,HEIGHT/N'
  69.            means get a name string and two numbers (width and
  70.            height). The possible modifiers are:
  71.            /S Option is a switch. It may be either set or
  72.               left out.
  73.            /T Option is a boolean value. Requires an argument
  74.               which may be "ON", "YES" (setting the respective
  75.               argument to 1), "OFF" or "NO" (setting the
  76.               respective argument to 0).
  77.            /N Option is a number. Strings are not allowed.
  78.               If the option is optional, a pointer to the
  79.               actual number is returned. This is how you know
  80.               if it was really given.
  81.            /A Argument is required. If it is left out ReadArgs()
  82.               fails.
  83.            /K The keyword must be given when filling the option.
  84.               Normally it's skipped.
  85.            /M Multiple strings. The result is returned as a string
  86.               pointer array terminated with NULL. /M eats all strings
  87.               that don't fit into any other option. If there are
  88.               unfilled /A arguments after parsing they steal strings
  89.               from /M. This makes it possible to e.g. write a COPY
  90.               template like 'FROM/A/M,TO/A'. There may be only one
  91.               /M option in a template.
  92.            /F Eats the rest of the line even if there are option
  93.               keywords in it.
  94.     array     - Array to be filled with the result values. The array must
  95.            be intialized to the default values before calling
  96.            ReadArgs().
  97.     rdargs     - An optional RDArgs structure determinating the type of
  98.            input to process.
  99.  
  100.     RESULT
  101.     A handle for the memory allocated by ReadArgs(). Must be freed
  102.     with FreeArgs() later.
  103.  
  104.     NOTES
  105.  
  106.     EXAMPLE
  107.  
  108.     SEE ALSO
  109.     FreeArgs(), Input()
  110.  
  111.     INTERNALS
  112.  
  113.     HISTORY
  114.     29-10-95    digulla automatically created from
  115.                 dos_lib.fd and clib/dos_protos.h
  116.  
  117. *****************************************************************************/
  118. {
  119.     AROS_LIBFUNC_INIT
  120.     AROS_LIBBASE_EXT_DECL(struct DosLibrary *,DOSBase)
  121.  
  122.     /* Allocated resources */
  123.     struct RDArgs *rda=NULL;
  124.     struct DAList *dalist=NULL;
  125.     UBYTE *flags=NULL;
  126.     STRPTR strbuf=NULL, iline=NULL;
  127.     STRPTR *multvec=NULL, *argbuf=NULL;
  128.     ULONG multnum=0, multmax=0;
  129.  
  130.     /* Some variables */
  131.     STRPTR s1, s2, *newmult;
  132.     ULONG arg, numargs, nextarg;
  133.     LONG it, item, chars, value;
  134.     struct CSource lcs, *cs;
  135.  
  136.     /* Get pointer to process structure. */
  137.     struct Process *me=(struct Process *)FindTask(NULL);
  138.  
  139.     /* Error recovery. C has no exceptions. This is a simple replacement. */
  140.     LONG error;
  141. #define ERROR(a) { error=a; goto end; }
  142.  
  143. /* Template options */
  144. #define REQUIRED 0x80 /* /A */
  145. #define KEYWORD  0x40 /* /K */
  146. #define TYPEMASK 0x07
  147. #define NORMAL     0x00 /* No option */
  148. #define SWITCH     0x01 /* /S, implies /K */
  149. #define TOGGLE     0x02 /* /T, implies /K */
  150. #define NUMERIC  0x03 /* /N */
  151. #define MULTIPLE 0x04 /* /M */
  152. #define REST     0x05 /* /F */
  153.  
  154.     /* Flags for each possible character. */
  155.     static const UBYTE argflags[]=
  156.     { REQUIRED, 0, 0, 0, 0, REST, 0, 0, 0, 0, KEYWORD, 0, MULTIPLE,
  157.       NUMERIC, 0, 0, 0, 0, SWITCH|KEYWORD, TOGGLE|KEYWORD, 0, 0, 0, 0, 0, 0 };
  158.  
  159.     /* Allocate readargs structure (and private internal one) */
  160.     rda=(struct RDArgs *)AllocVec(sizeof(struct RDArgs),MEMF_ANY);
  161.     dalist=(struct DAList *)AllocVec(sizeof(struct DAList),MEMF_ANY);
  162.     if(rda==NULL||dalist==NULL)
  163.     ERROR(ERROR_NO_FREE_STORE);
  164.  
  165.     /* Init character source. */
  166.     if(rdargs!=NULL)
  167.     cs=&rdargs->RDA_Source;
  168.     else
  169.     {
  170.     lcs.CS_Buffer=me->pr_Arguments;
  171.     s1=lcs.CS_Buffer;
  172.     while(*s1++)
  173.         ;
  174.     lcs.CS_Length=(IPTR)s1-(IPTR)lcs.CS_Buffer-1;
  175.     lcs.CS_CurChr=0;
  176.     cs=&lcs;
  177.     }
  178.  
  179.     /* Check for optional reprompting */
  180.     if(rdargs==NULL||!(rdargs->RDA_Flags&RDAF_NOPROMPT))
  181.     {
  182.     /* Check commandline for a single '?' */
  183.     s1=cs->CS_Buffer;
  184.     /* Skip leading whitespace */
  185.     while(*s1==' '||*s1=='\t')
  186.         s1++;
  187.     /* Check for '?' */
  188.     if(*s1++=='?')
  189.     {
  190.         /* Skip whitespace */
  191.         while(*s1==' '||*s1=='\t')
  192.         s1++;
  193.         /* Check for EOL */
  194.         if(*s1=='\n'||!*s1)
  195.         {
  196.         /* Only a single '?' on the commandline. */
  197.         BPTR input=me->pr_CIS, output=me->pr_COS;
  198.         ULONG isize=0, ibuf=0;
  199.         LONG c;
  200.         /* Prompt for more input */
  201.         if(rdargs!=NULL&&rdargs->RDA_ExtHelp!=NULL)
  202.         {
  203.             if(FPuts(output,rdargs->RDA_ExtHelp))
  204.             ERROR(me->pr_Result2);
  205.         }else if(FPuts(output,template)||FPuts(output,": "))
  206.            ERROR(me->pr_Result2);
  207.         if(!Flush(output))
  208.            ERROR(me->pr_Result2);
  209.         /* Read a line in. */
  210.         for(;;)
  211.         {
  212.             if(isize>=ibuf)
  213.             {
  214.             /* Buffer too small. Get a new one. */
  215.             STRPTR newiline;
  216.             ibuf+=256;
  217.             newiline=(STRPTR)AllocVec(ibuf,MEMF_ANY);
  218.             if(newiline==NULL)
  219.                 ERROR(ERROR_NO_FREE_STORE);
  220.             CopyMemQuick((ULONG *)iline,(ULONG *)newiline,isize);
  221.             FreeVec(iline);
  222.             iline=newiline;
  223.             }
  224.             /* Read character */
  225.             c=FGetC(input);
  226.             /* Check and write it. */
  227.             if(c==EOF&&me->pr_Result2)
  228.             ERROR(me->pr_Result2);
  229.             if(c==EOF||c=='\n'||!c)
  230.             break;
  231.             iline[isize++]=c;
  232.         }
  233.         /* Prepare input source for new line. */
  234.         cs->CS_Buffer=iline;
  235.         cs->CS_Length=isize;
  236.         }
  237.     }
  238.     }
  239.  
  240.     /*
  241.     Get enough space for string buffer.
  242.     It's always smaller than the size of the input line+1.
  243.     */
  244.     strbuf=(STRPTR)AllocVec(cs->CS_Length+1,MEMF_ANY);
  245.     if(strbuf==NULL)
  246.     ERROR(ERROR_NO_FREE_STORE);
  247.  
  248.     /* Count the number of items in the template (number of ','+1). */
  249.     numargs=1;
  250.     s1=template;
  251.     while(*s1)
  252.     if(*s1++==',')
  253.         numargs++;
  254.  
  255.     /* Use this count to get space for temporary flag array and result buffer. */
  256.     flags=(UBYTE *)AllocVec(numargs+1,MEMF_CLEAR);
  257.     argbuf=(STRPTR *)AllocVec((numargs+1)*sizeof(STRPTR),MEMF_CLEAR);
  258.     if(flags==NULL||argbuf==NULL)
  259.     ERROR(ERROR_NO_FREE_STORE);
  260.  
  261.     /* Fill the flag array. */
  262.     s1=template;
  263.     s2=flags;
  264.     while(*s1)
  265.     {
  266.     /* A ',' means: goto next item. */
  267.     if(*s1==',')
  268.         s2++;
  269.     /* In case of a '/' use the next character as option. */
  270.     if(*s1++=='/')
  271.         *s2|=argflags[*s1-'A'];
  272.     }
  273.     /* Add a dummy so that the whole line is processed. */
  274.     *++s2=MULTIPLE;
  275.  
  276.     /*
  277.     Now process commandline for the first time:
  278.     * Go from left to right and fill all items that need filling.
  279.     * If an item is given as 'OPTION=VALUE' or 'OPTION VALUE' fill
  280.       it out of turn.
  281.     */
  282.     s1=strbuf;
  283.     for(arg=0;arg<=numargs;arg=nextarg)
  284.     {
  285.     nextarg=arg+1;
  286.  
  287.     /* Skip /K options and options that are already done. */
  288.     if(flags[arg]&KEYWORD||argbuf[arg]!=NULL)
  289.         continue;
  290.  
  291.     /* If the current option is of type /F do not look for keywords */
  292.     if((flags[arg]&TYPEMASK)!=REST)
  293.     {
  294.         /* Get item. Quoted items are no keywords. */
  295.         it=ReadItem(s1,~0ul/2,cs);
  296.         if(it==ITEM_UNQUOTED)
  297.         {
  298.         /* Not quoted. Check if it's a keyword. */
  299.         item=FindArg(template,s1);
  300.         if(item>=0&&argbuf[item]==NULL)
  301.         {
  302.             /*
  303.             It's a keyword. Fill it and retry the current option
  304.             at the next turn
  305.             */
  306.             nextarg=arg;
  307.             arg=item;
  308.  
  309.             /* /S /T and /F may not be given as 'OPTION=VALUE'. */
  310.             if((flags[item]&TYPEMASK)!=SWITCH&&
  311.                (flags[item]&TYPEMASK)!=TOGGLE&&
  312.                (flags[item]&TYPEMASK)!=REST)
  313.             {
  314.             /* Get value. */
  315.             it=ReadItem(s1,~0ul/2,cs);
  316.             if(it==ITEM_EQUAL)
  317.                 it=ReadItem(s1,~0ul/2,cs);
  318.             }
  319.         }
  320.         }
  321.         /* Check returncode of ReadItem(). */
  322.         if(it==ITEM_EQUAL)
  323.         ERROR(ERROR_BAD_TEMPLATE);
  324.         if(it==ITEM_ERROR)
  325.         ERROR(me->pr_Result2);
  326.         if(it==ITEM_NOTHING)
  327.         break;
  328.     }
  329.     /* /F takes all the rest */
  330.     if((flags[arg]&TYPEMASK)==REST)
  331.     {
  332.         /* Skip leading whitespace */
  333.         while(cs->CS_CurChr<cs->CS_Length&&
  334.           (cs->CS_Buffer[cs->CS_CurChr]==' '||
  335.            cs->CS_Buffer[cs->CS_CurChr]=='\t'))
  336.         cs->CS_CurChr++;
  337.  
  338.         /* Find the last non-whitespace character */
  339.         s2=s1-1;
  340.         argbuf[arg]=s1;
  341.         while(cs->CS_CurChr<cs->CS_Length&&
  342.           cs->CS_Buffer[cs->CS_CurChr]&&
  343.           cs->CS_Buffer[cs->CS_CurChr]!='\n')
  344.         {
  345.         if(cs->CS_Buffer[cs->CS_CurChr]!=' '&&
  346.            cs->CS_Buffer[cs->CS_CurChr]!='\t')
  347.             s2=s1;
  348.         /* Copy string by the way. */
  349.         *s1++=cs->CS_Buffer[cs->CS_CurChr++];
  350.         }
  351.         /* Add terminator (1 after the character found). */
  352.         s2[1]=0;
  353.         it=ITEM_NOTHING;
  354.         break;
  355.     }
  356.     /* /S or /T just set a flag */
  357.     if((flags[arg]&TYPEMASK)==SWITCH||(flags[arg]&TYPEMASK)==TOGGLE)
  358.         argbuf[arg]=(char *)1;
  359.     else if((flags[arg]&TYPEMASK)==MULTIPLE)
  360.     {
  361.         /* All /M arguments are stored in a buffer. */
  362.         if(multnum>=multmax)
  363.         {
  364.         /* Buffer too small. Get a new one. */
  365.         multmax+=16;
  366.         newmult=(STRPTR *)AllocVec(multmax*sizeof(char *),MEMF_ANY);
  367.         if(newmult==NULL)
  368.             ERROR(ERROR_NO_FREE_STORE);
  369.         CopyMemQuick((ULONG *)multvec,(ULONG *)newmult,multnum*sizeof(char *));
  370.         FreeVec(multvec);
  371.         multvec=newmult;
  372.         }
  373.         /* Put string into the buffer. */
  374.         multvec[multnum++]=s1;
  375.         while(*s1++)
  376.         ;
  377.         /* /M takes more than one argument, so retry. */
  378.         nextarg=arg;
  379.     }else /* NORMAL || NUMERIC */
  380.     {
  381.         /* Put argument into argument buffer. */
  382.         argbuf[arg]=s1;
  383.         while(*s1++)
  384.         ;
  385.     }
  386.     }
  387.  
  388.     /* Unfilled /A options steal Arguments from /M */
  389.     for(arg=numargs;arg-->0;)
  390.     if(flags[arg]&REQUIRED&&argbuf[arg]==NULL&&
  391.        (flags[arg]&TYPEMASK)!=MULTIPLE)
  392.     {
  393.         if(!multnum)
  394.         /* No arguments left? Oh dear! */
  395.         ERROR(ERROR_REQUIRED_ARG_MISSING);
  396.         argbuf[arg]=multvec[--multnum];
  397.     }
  398.  
  399.     /* Put the rest of /M where it belongs */
  400.     for(arg=0;arg<numargs;arg++)
  401.     if((flags[arg]&TYPEMASK)==MULTIPLE)
  402.     {
  403.         if(flags[arg]&REQUIRED&&!multnum)
  404.         ERROR(ERROR_REQUIRED_ARG_MISSING);
  405.  
  406.         /* NULL terminate it. */
  407.         if(multnum>=multmax)
  408.         {
  409.         multmax+=16;
  410.         newmult=(STRPTR *)AllocVec(multmax*sizeof(STRPTR),MEMF_ANY);
  411.         if(newmult==NULL)
  412.             ERROR(ERROR_NO_FREE_STORE);
  413.         CopyMemQuick((ULONG *)multvec,(ULONG *)newmult,multnum*sizeof(char *));
  414.         FreeVec(multvec);
  415.         multvec=newmult;
  416.         }
  417.         multvec[multnum++]=NULL;
  418.         argbuf[arg]=(STRPTR)multvec;
  419.         break;
  420.     }
  421.  
  422.     /* There are some arguments left? Return error. */
  423.     if(multnum&&arg==numargs)
  424.     ERROR(ERROR_TOO_MANY_ARGS);
  425.  
  426.     /*
  427.     The commandline is processed now. Put the results in the result array.
  428.     Convert /N arguments by the way.
  429.     */
  430.     for(arg=0;arg<numargs;arg++)
  431.     {
  432.     /* Just for the arguments given. */
  433.     if(argbuf[arg]!=NULL)
  434.     {
  435.         switch(flags[arg]&TYPEMASK)
  436.         {
  437.         case NORMAL:
  438.         case MULTIPLE:
  439.         case REST:
  440.         case SWITCH:
  441.             /* Simple arguments are just copied. */
  442.             array[arg]=(IPTR)argbuf[arg];
  443.             break;
  444.         case TOGGLE:
  445.             /* /T logically inverts the argument. */
  446.             array[arg]=!array[arg];
  447.             break;
  448.         case NUMERIC:
  449.             /* Convert /N argument. */
  450.             chars=StrToLong(argbuf[arg],&value);
  451.             if(chars<=0||argbuf[arg][chars])
  452.             /* Conversion failed. */
  453.             ERROR(ERROR_BAD_NUMBER);
  454.             /* Put the result where it belongs. */
  455.             if(flags[arg]&REQUIRED)
  456.             /* Required argument. Return number. */
  457.             array[arg]=value;
  458.             else
  459.             {
  460.             /* Abuse the argbuf buffer. It's not needed anymore. */
  461.             argbuf[arg]=(STRPTR)value;
  462.             array[arg]=(IPTR)&argbuf[arg];
  463.             }
  464.             break;
  465.         }
  466.     }
  467.     }
  468.  
  469.     /* All OK. */
  470.     error=0;
  471. end:
  472.     /* Cleanup and return. */
  473.     FreeVec(iline);
  474.     FreeVec(flags);
  475.     if(error)
  476.     {
  477.     /* ReadArgs() failed. Clean everything up. */
  478.     FreeVec(rda);
  479.     FreeVec(dalist);
  480.     FreeVec(argbuf);
  481.     FreeVec(strbuf);
  482.     FreeVec(multvec);
  483.     me->pr_Result2=error;
  484.     return NULL;
  485.     }else
  486.     {
  487.     /* All went well. Prepare result and return. */
  488.     rda->RDA_DAList=(IPTR)dalist;
  489.     dalist->ArgBuf=argbuf;
  490.     dalist->StrBuf=strbuf;
  491.     dalist->MultVec=multvec;
  492.     return rda;
  493.     }
  494.     AROS_LIBFUNC_EXIT
  495. } /* ReadArgs */
  496.