home *** CD-ROM | disk | FTP | other *** search
/ MACD 4 / MACD4.iso / Emulatory / AROS / dos / readargs.c < prev    next >
Encoding:
C/C++ Source or Header  |  1978-03-06  |  12.7 KB  |  474 lines

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