Experiment with the goal to make getting of a command template more
[AROS.git] / rom / dos / readargs.c
blobe7199a639d69b149430b828e6fd8c1b5e529326e
2 /*
3 Copyright © 1995-2012, The AROS Development Team. All rights reserved.
4 $Id$
6 Desc:
7 Lang: english
8 */
10 #define DEBUG 0
11 #include <aros/debug.h>
13 #include <exec/memory.h>
14 #include <proto/exec.h>
15 #include <dos/rdargs.h>
16 #include <dos/dosextens.h>
17 #include <proto/utility.h>
19 #include "dos_intern.h"
21 #ifdef TEST
22 # include <stdio.h>
23 # include <proto/dos.h>
24 # undef ReadArgs
25 # undef AROS_LH3
26 # define AROS_LH3(t,fn,a1,a2,a3,bt,bn,o,lib) t fn (a1,a2,a3)
27 # undef AROS_LHA
28 # define AROS_LHA(t,n,r) t n
29 # undef AROS_LIBFUNC_INIT
30 # define AROS_LIBFUNC_INIT
31 # undef AROS_LIBFUNC_EXIT
32 # define AROS_LIBFUNC_EXIT
33 #endif
35 /* Fix the end-of-buffer ReadItem() 'well known' bug
36 * where it "ungets" non-terminator (' ','=','\t','\n')
37 * characters at the end of a non-\n terminated buffer.
39 #define READITEM(buff, bufflen, cs) \
40 ({ LONG ret = ReadItem(buff, bufflen, cs); \
41 if (ret == ITEM_UNQUOTED && (cs->CS_CurChr+1) == cs->CS_Length) \
42 cs->CS_CurChr++; \
43 ret; \
46 /* Returns 0 if this is not a '?' line, otherwise
47 * returns the length between the '?' and the '\n'
50 static inline LONG is_question(BYTE * buff, LONG buffsize)
52 LONG i, j = 0;
53 BOOL escaped = FALSE,
54 quoted = FALSE,
55 seen_space = TRUE,
56 seen_question = FALSE;
58 /* Reach end of line */
59 for (i = 0; i < buffsize; i++)
61 /* Only spaces and return are allowed at the end of the line after the
62 * question mark for it to lead to reprompting. BTW, AmigaOS allowed
63 * only one space then... but do we need to be _that_ compatible?
66 switch (buff[i])
68 case '*':
69 if (quoted)
70 escaped = !escaped;
71 break;
72 case '"':
73 if (!escaped)
74 quoted = !quoted;
75 /* Fall through */
76 default:
77 escaped = FALSE;
80 switch (buff[i])
82 case ' ':
83 case '\t':
84 seen_space = TRUE;
85 break;
86 case '?':
87 if ((!quoted) && (seen_space))
89 seen_question = TRUE;
90 seen_space = FALSE;
91 j = i;
93 else
94 seen_question = FALSE;
95 break;
96 case EOF:
97 case '\n':
98 if (seen_question)
99 return (i + 1 - j);
100 else
101 return 0;
102 default:
103 seen_question = seen_space = FALSE;
106 return 0;
109 /*****************************************************************************
111 NAME */
112 #include <proto/dos.h>
114 AROS_LH3(struct RDArgs *, ReadArgs,
116 /* SYNOPSIS */
117 AROS_LHA(CONST_STRPTR, template, D1),
118 AROS_LHA(IPTR *, array, D2),
119 AROS_LHA(struct RDArgs *, rdargs, D3),
121 /* LOCATION */
122 struct DosLibrary *, DOSBase, 133, Dos)
124 /* FUNCTION
125 Parses the commandline, a given string or Input() and fills
126 an argument array according to the options template given.
127 The array must be initialized to the wanted defaults before
128 each call to ReadArgs(). If the rdargs argument is NULL
129 ReadArgs() tries to parse the commandline and continues
130 on the input channel if it just consists of a single '?',
131 prompting the user for input.
133 INPUTS
134 template - Template string. The template string is given as
135 a number of options separated by ',' and modified
136 by '/' modifiers, e.g. 'NAME,WIDTH/N,HEIGHT/N'
137 means get a name string and two numbers (width and
138 height). The possible modifiers are:
139 /S Option is a switch. It may be either set or
140 left out.
141 /T Option is a boolean value. Requires an argument
142 which may be "ON", "YES" (setting the respective
143 argument to 1), "OFF" or "NO" (setting the
144 respective argument to 0).
145 /N Option is a number. Strings are not allowed.
146 If the option is optional, a pointer to the
147 actual number is returned. This is how you know
148 if it was really given. The number is always of type
149 LONG.
150 /A Argument is required. If it is left out ReadArgs()
151 fails.
152 /K The keyword must be given when filling the option.
153 Normally it's skipped.
154 /M Multiple strings or, when used in combination with /N,
155 numbers. The result is returned as an array of pointers
156 to strings or LONGs, and is terminated with NULL. /M
157 eats all strings that don't fit into any other option.
158 If there are unfilled /A arguments after parsing they
159 steal strings from /M. This makes it possible to, for
160 example, write a Copy command template like
161 'FROM/A/M,TO/A'. There may be only one /M option in a
162 template.
163 /F Eats the rest of the line even if there are option
164 keywords in it.
165 array - Array to be filled with the result values. The array must
166 be intialized to the default values before calling
167 ReadArgs().
168 rdargs - An optional RDArgs structure determining the type of
169 input to process.
171 RESULT
172 A handle for the memory allocated by ReadArgs(). Must be freed
173 with FreeArgs() later.
175 SEE ALSO
176 FreeArgs(), Input()
178 *****************************************************************************/
180 AROS_LIBFUNC_INIT
182 /* Allocated resources */
183 struct DAList *dalist = NULL;
184 UBYTE *flags = NULL;
185 STRPTR strbuf = NULL, iline = NULL;
186 STRPTR *multvec = NULL, *argbuf = NULL;
187 CONST_STRPTR numstr;
188 ULONG multnum = 0, multmax = 0;
189 LONG strbuflen;
191 /* Some variables */
192 CONST_STRPTR cs1;
193 STRPTR s1, s2, *newmult;
194 ULONG arg, numargs, nextarg;
195 LONG it, item, chars, delthis;
196 struct CSource lcs, *cs;
197 BOOL is_file_not_buffer;
198 TEXT argbuff[256 + 1]; /* Maximum BCPL string length + injected \n + ASCIIZ */
200 ASSERT_VALID_PTR(template);
201 ASSERT_VALID_PTR(array);
202 ASSERT_VALID_PTR_OR_NULL(rdargs);
204 D(bug("[ReadArgs] Template: \"%s\"\n", template));
205 /* Get pointer to process structure. */
206 struct Process *me = (struct Process *) FindTask(NULL);
208 ASSERT_VALID_PROCESS(me);
210 /* Error recovery. C has no exceptions. This is a simple replacement. */
211 LONG error;
213 #undef ERROR
214 #define ERROR(a) { error=a; goto end; }
216 /* Template options */
217 #define REQUIRED 0x80 /* /A */
218 #define KEYWORD 0x40 /* /K */
219 #define MULTIPLE 0x20 /* /M */
220 #define TYPEMASK 0x07
221 #define NORMAL 0x00 /* No option */
222 #define SWITCH 0x01 /* /S, implies /K */
223 #define TOGGLE 0x02 /* /T, implies /K */
224 #define NUMERIC 0x03 /* /N */
225 #define REST 0x04 /* /F */
227 /* Flags for each possible character. */
228 static const UBYTE argflags[] =
230 REQUIRED, 0, 0, 0, 0, REST, 0, 0, 0, 0, KEYWORD, 0, MULTIPLE,
231 NUMERIC, 0, 0, 0, 0, SWITCH | KEYWORD, TOGGLE | KEYWORD, 0, 0,
232 0, 0, 0, 0
235 /* Allocate readargs structure (and private internal one) */
236 if (!rdargs)
238 rdargs = (struct RDArgs *) AllocVec(sizeof(struct RDArgs),
239 MEMF_ANY | MEMF_CLEAR);
240 if (rdargs)
242 rdargs->RDA_Flags |= RDAF_ALLOCATED_BY_READARGS;
246 dalist = (struct DAList *) AllocVec(sizeof(struct DAList),
247 MEMF_ANY | MEMF_CLEAR);
249 if (rdargs == NULL || dalist == NULL)
251 ERROR(ERROR_NO_FREE_STORE);
254 /* Init character source. */
255 if (rdargs->RDA_Source.CS_Buffer)
257 cs = &rdargs->RDA_Source;
258 D(bug("[ReadArgs] Buffer: \"%s\"\n", cs->CS_Buffer));
259 is_file_not_buffer = FALSE;
261 else
263 BOOL notempty = TRUE;
264 BPTR input = Input();
266 D(bug("[ReadArgs] Input: 0x%p\n", input));
267 is_file_not_buffer = TRUE;
270 * Take arguments from input stream. They were injected there by either
271 * runcommand.c or createnewproc.c (see vbuf_inject() routine).
272 * This is described in Guru Book.
274 argbuff[0] = 0;
275 lcs.CS_Buffer = &argbuff[0];
278 * Special kludge for interactive filehandles (i. e. CLI windows).
279 * Read data only if filehandle's buffer is not empty. Otherwise
280 * read will cause opening CLI window and waiting for user's input.
281 * As a consequence we still can use ReadArgs() on input redirected
282 * from a file, even if we are started from Workbench (hypothetical
283 * situation).
284 * This prevents opening a CLI window if the program was started from
285 * Workbench and redirected its Input() and Output() to own window,
286 * but still called ReadArgs() after redirection for some reason.
287 * Streams redirection is widely used in AROS startup code.
289 if (IsInteractive(input))
291 struct FileHandle *fh = BADDR(input);
293 notempty = (fh->fh_Pos != fh->fh_End);
296 if (notempty)
297 FGets(input, lcs.CS_Buffer, sizeof(argbuff));
299 D(bug("[ReadArgs] Line: %s\n", argbuff));
302 * If "!GETTEMPLATE!" is given as argument print the template and
303 * exit with an error code. This makes a "R" like tool more reliable.
307 !(rdargs->RDA_Flags & RDAF_NOPROMPT)
309 (strncmp(argbuff, "!GETTEMPLATE!", 13) == 0)
312 D(bug("[ReadArgs] !GETTEMPLATE! found\n"));
314 BPTR output = Output();
316 if (FPuts(output, template) || FPuts(output, ": "))
318 ERROR(me->pr_Result2);
321 if (!Flush(output))
323 ERROR(me->pr_Result2);
326 ERROR(ERROR_BAD_TEMPLATE);
329 cs1 = lcs.CS_Buffer;
331 for (; *cs1 != '\0'; ++cs1);
333 lcs.CS_Length = cs1 - lcs.CS_Buffer;
334 lcs.CS_CurChr = 0;
336 cs = &lcs;
339 /* Check for optional reprompting */
340 if (!(rdargs->RDA_Flags & RDAF_NOPROMPT))
342 if ((delthis = is_question(cs->CS_Buffer, cs->CS_Length)))
344 /* '?' was found on the commandline. */
345 BPTR input = Input();
346 BPTR output = Output();
347 ULONG isize = 0, ibuf = 0;
348 LONG c;
349 ULONG helpdisplayed = FALSE;
351 /* Prompt for more input */
353 D(bug("[ReadArgs] '?' found, %d chars to be removed\n", delthis));
354 D(bug("[ReadArgs] rdargs=0x%p\n", rdargs));
355 D(if (rdargs) bug ("[ReadArds] rdargs->RDA_ExtHelp=0x%p\n", rdargs->RDA_ExtHelp);)
357 if (FPuts(output, template) || FPuts(output, ": "))
359 ERROR(me->pr_Result2);
362 if (!Flush(output))
364 ERROR(me->pr_Result2);
367 cs->CS_Length -= delthis;
368 ULONG memsize = isize = ibuf = cs->CS_Length;
369 iline = (STRPTR) AllocVec(ibuf, MEMF_ANY);
370 CopyMem(cs->CS_Buffer, iline, isize);
374 /* Read a line in. */
375 c = -1;
376 for (;;)
378 if (c == '\n')
380 iline[isize] = '\0'; /* end of string */
381 break;
383 if (isize >= ibuf)
385 /* Buffer too small. Get a new one. */
386 STRPTR newiline;
388 ibuf += 256;
390 newiline = (STRPTR) AllocVec(ibuf, MEMF_ANY);
391 if (newiline == NULL)
393 ERROR(ERROR_NO_FREE_STORE);
396 if (iline != NULL)
397 CopyMem(iline, newiline, isize);
399 FreeVec(iline);
401 iline = newiline;
404 /* Read character */
405 if (is_file_not_buffer)
407 c = FGetC(input);
409 else
411 SetIoErr(0);
413 if (cs->CS_CurChr >= cs->CS_Length)
414 c = EOF;
415 else
416 c = cs->CS_Buffer[cs->CS_CurChr++];
419 /* Check and write it. */
420 if (c == EOF && me->pr_Result2)
422 ERROR(me->pr_Result2);
425 /* Fix short buffers to have a trailing '\n' */
426 if (c == EOF || c == '\0')
427 c = '\n';
429 iline[isize++] = c;
431 iline[isize] = '\0'; /* end of string */
433 D(iline[isize] = 0; bug("[ReadArgs] Size %d, line: '%s'\n", isize, iline));
435 /* if user entered single ? again or some string ending
436 with space and ? either display template again or
437 extended help if it's available */
438 if ( (delthis = is_question(iline, isize))
439 && (memsize <= (isize - delthis))
442 helpdisplayed = TRUE;
444 memsize = isize -= delthis;
446 if(rdargs->RDA_ExtHelp != NULL)
448 if (FPuts(output, rdargs->RDA_ExtHelp) || FPuts(output, ": "))
449 ERROR(me->pr_Result2);
451 else if (FPuts(output, template) || FPuts(output, ": "))
453 ERROR(me->pr_Result2);
456 if (!Flush(output))
458 ERROR(me->pr_Result2);
461 else
462 helpdisplayed = FALSE;
463 } while(helpdisplayed);
465 /* Prepare input source for new line. */
466 cs->CS_Buffer = iline;
467 cs->CS_Length = isize;
468 cs->CS_CurChr = 0;
473 * Get enough space for string buffer.
474 * It's always smaller than the size of the input line+1.
477 strbuflen = cs->CS_Length + 1;
478 strbuf = (STRPTR) AllocVec(strbuflen, MEMF_ANY);
480 if (strbuf == NULL)
482 ERROR(ERROR_NO_FREE_STORE);
485 /* Count the number of items in the template (number of ','+1). */
486 numargs = 1;
487 cs1 = template;
489 while (*cs1)
491 if (*cs1++ == ',')
493 numargs++;
497 /* Use this count to get space for temporary flag array and result
498 * buffer. */
499 flags = (UBYTE *) AllocVec(numargs + 1, MEMF_CLEAR);
501 argbuf = (STRPTR *) AllocVec((numargs + 1) * sizeof(STRPTR), MEMF_CLEAR);
503 if (flags == NULL || argbuf == NULL)
505 ERROR(ERROR_NO_FREE_STORE);
508 /* Fill the flag array. */
509 cs1 = template;
510 s2 = flags;
512 while (*cs1)
514 /* A ',' means: goto next item. */
515 if (*cs1 == ',')
517 s2++;
520 /* In case of a '/' use the next character as option. */
521 if (*cs1++ == '/')
523 UBYTE argc = ToUpper(*cs1);
524 if (argc >= 'A' && argc <= 'Z')
525 *s2 |= argflags[argc - 'A'];
529 /* Add a dummy so that the whole line is processed (see below). */
530 *++s2 = MULTIPLE;
533 * Now process commandline for the first time:
534 * Go from left to right and fill all items that need filling.
535 * If an item is given as 'OPTION=VALUE' or 'OPTION VALUE' fill
536 * it out of turn.
537 * NOTE: '<=' comparison is intentional here. When we allocated argbuf, we added one
538 * to the number of arguments. And right above we added fictional MULTIPLE flag.
539 * This is actually needed to make /S and /K working.
541 s1 = strbuf;
543 for (arg = 0; arg <= numargs ; arg = nextarg)
545 nextarg = arg + 1;
547 D(bug("[ReadArgs] Arg %d (0x%x) s1=&strbuf[%d], %d left\n", arg, flags[arg], s1-strbuf, strbuflen));
549 /* Out of buffer space?
550 * This should not have happened, some internal logic
551 * must have broken.
553 if (strbuflen == 0) {
554 D(bug("[ReadArgs] %d: INTERNAL ERROR: Ran out of buffer space.\n", arg));
555 break;
558 /* Skip /K options and options that are already done. */
559 if (flags[arg] & KEYWORD || argbuf[arg] != NULL)
561 continue;
564 #if 0 /* stegerg: if so a template of CLOSE/S,QUICK/S,COMMAND/F would
565 not work correctly if command line for example is
566 "CLOSE QUICK" it would all end up being eaten by COMMAND/F
567 argument */
569 /* If the current option is of type /F do not look for keywords */
570 if ((flags[arg] & TYPEMASK) != REST)
571 #endif
574 /* Get item. Quoted items are never keywords. */
575 it = READITEM(s1, strbuflen, cs);
576 D(bug("[ReadArgs] Item %s type %d\n", s1, it));
578 if (it == ITEM_UNQUOTED)
580 /* Not quoted. Check if it's a keyword. */
581 item = FindArg(template, s1);
583 if (item >= 0 && item < numargs && argbuf[item] == NULL)
585 D(bug("[ReadArgs] %d: Keyword \"%s\" (%d)\n", arg, s1, item));
587 * It's a keyword. Fill it and retry the current option
588 * at the next turn
590 nextarg = arg;
591 arg = item;
593 /* /S /T may not be given as 'OPTION=VALUE'. */
594 if ((flags[item] & TYPEMASK) != SWITCH
595 && (flags[item] & TYPEMASK) != TOGGLE)
597 /* Get value. */
598 it = READITEM(s1, strbuflen, cs);
600 if (it == ITEM_EQUAL)
602 it = READITEM(s1, strbuflen, cs);
603 } else if (it != ITEM_QUOTED && it != ITEM_UNQUOTED) {
604 ERROR(ERROR_KEY_NEEDS_ARG);
610 /* Check returncode of ReadItem(). */
611 if (it == ITEM_EQUAL)
613 ERROR(ERROR_BAD_TEMPLATE);
615 else if (it == ITEM_ERROR)
617 ERROR(me->pr_Result2);
619 else if (it == ITEM_NOTHING)
621 break;
625 /* /F takes all the rest, including extra spaces, =, and '"'
626 * NOTE: If the item was quoted, this will strip off the
627 * next '"' mark it sees.
629 if ((flags[arg] & TYPEMASK) == REST)
631 BOOL eat_quote = (it == ITEM_QUOTED) ? TRUE : FALSE;
632 argbuf[arg] = s1;
634 /* Skip past what ReadItem() just read.
636 while (*s1 && strbuflen > 0) {
637 s1++;
638 strbuflen--;
642 * Put the rest into the buffer, including the separator
643 * ReadItem() actually ungets '\n' terminator. So if CurChr points to it,
644 * we don't need to adjust it. Otherwise we duplicate last character of arguments line.
646 if (cs->CS_Buffer[cs->CS_CurChr] != '\n')
647 cs->CS_CurChr--;
648 s2 = &cs->CS_Buffer[cs->CS_CurChr];
650 while (cs->CS_CurChr < cs->CS_Length &&
651 strbuflen > 1 &&
652 *s2 &&
653 *s2 != '\n')
655 cs->CS_CurChr++;
657 if (eat_quote && *s2 == '"')
659 s2++;
660 eat_quote = FALSE;
661 continue;
664 *(s1++) = *(s2++);
665 strbuflen--;
668 *(s1++) = 0;
669 strbuflen--;
670 D(bug("[ReadArgs] /F copy: \"%s\" left=%d, CS_CurChr=%d, CS_Length=%d\n", argbuf[arg], strbuflen, cs->CS_CurChr, cs->CS_Length));
671 it = ITEM_NOTHING;
672 break;
675 if (flags[arg] & MULTIPLE)
677 /* All /M arguments are stored in a buffer. */
678 if (multnum >= multmax)
680 /* Buffer too small. Get a new one. */
681 multmax += 16;
683 newmult = (STRPTR *) AllocVec(multmax * sizeof(char *),
684 MEMF_ANY);
685 if (newmult == NULL)
687 ERROR(ERROR_NO_FREE_STORE);
690 CopyMemQuick((ULONG *) multvec, (ULONG *) newmult,
691 multnum * sizeof(char *));
693 FreeVec(multvec);
695 multvec = newmult;
698 /* Put string into the buffer. */
699 multvec[multnum++] = s1;
701 D(bug("[ReadArgs] %d: Multiple +\"%s\"\n", arg, s1));
702 while (*s1++)
703 --strbuflen;
704 /* Account for the \000 at the end. */
705 --strbuflen;
707 /* /M takes more than one argument, so retry. */
708 nextarg = arg;
710 else if ((flags[arg] & TYPEMASK) == SWITCH
711 || (flags[arg] & TYPEMASK) == TOGGLE)
713 /* /S or /T just set a flag */
714 argbuf[arg] = (char *) ~0;
715 D(bug("[ReadArgs] %d: Toggle\n", arg));
717 else /* NORMAL || NUMERIC */
719 /* Put argument into argument buffer. */
720 argbuf[arg] = s1;
721 D(bug("[ReadArgs] %d: Normal: \"%s\"\n", arg, s1));
723 while (*s1++)
724 --strbuflen;
725 /* Account for the \000 at the end. */
726 --strbuflen;
729 if (cs->CS_CurChr >= cs->CS_Length)
730 break; /* end of input */
733 /* Unfilled /A options steal Arguments from /M */
734 for (arg = numargs; arg-- > 0;)
736 if (flags[arg] & REQUIRED && argbuf[arg] == NULL
737 && !(flags[arg] & MULTIPLE))
739 if (flags[arg] & KEYWORD)
741 /* /K/A argument, which insists on keyword
742 * being used, cannot be satisfied */
744 ERROR(ERROR_TOO_MANY_ARGS); /* yes, strange error number,
745 * but it translates to "wrong
746 * number of arguments" */
750 if (multnum == 0)
752 /* No arguments left? Oh dear! */
753 ERROR(ERROR_REQUIRED_ARG_MISSING);
756 argbuf[arg] = multvec[--multnum];
760 /* Put the rest of /M where it belongs */
761 for (arg = 0; arg < numargs; arg++)
763 if (flags[arg] & MULTIPLE)
765 if (flags[arg] & REQUIRED && multnum == 0)
767 ERROR(ERROR_REQUIRED_ARG_MISSING);
770 if (multnum != 0)
772 /* NULL terminate it. */
773 if (multnum >= multmax)
775 multmax += 16;
777 newmult = (STRPTR *) AllocVec(multmax * sizeof(STRPTR),
778 MEMF_ANY);
780 if (newmult == NULL)
782 ERROR(ERROR_NO_FREE_STORE);
785 CopyMemQuick((ULONG *) multvec, (ULONG *) newmult,
786 multnum * sizeof(char *));
788 FreeVec(multvec);
790 multvec = newmult;
793 multvec[multnum++] = NULL;
794 argbuf[arg] = (STRPTR) multvec;
796 else
798 /* Shouldn't be necessary, but some buggy software relies on this */
799 argbuf[arg] = NULL;
802 break;
806 /* There are some arguments left? Return error. */
807 if (multnum != 0 && arg == numargs)
809 ERROR(ERROR_TOO_MANY_ARGS);
813 * The commandline is processed now. Put the results in the result array.
814 * Convert /N arguments by the way.
816 for (arg = 0; arg < numargs; arg++)
818 /* Just for the arguments given. */
819 if (argbuf[arg] != NULL)
821 if (flags[arg] & MULTIPLE)
823 array[arg] = (IPTR) argbuf[arg];
825 if ((flags[arg] & TYPEMASK) == NUMERIC)
827 STRPTR *p;
828 LONG *q;
830 if (multnum * 2 > multmax)
832 multmax = multnum * 2;
833 newmult = (STRPTR *) AllocVec(multmax * sizeof(STRPTR),
834 MEMF_ANY);
836 if (newmult == NULL)
838 ERROR(ERROR_NO_FREE_STORE);
841 CopyMemQuick((ULONG *) multvec, (ULONG *) newmult,
842 multnum * sizeof(char *));
844 FreeVec(multvec);
846 multvec = newmult;
849 array[arg] = (IPTR) multvec;
850 p = multvec;
851 q = (LONG *) (multvec + multnum);
853 while (*p)
855 /* Convert /N argument. */
856 chars = StrToLong(*p, q);
858 if (chars <= 0 || (*p)[chars])
860 /* Conversion failed. */
861 ERROR(ERROR_BAD_NUMBER);
864 /* Put the result where it belongs. */
865 *p = (STRPTR) q;
866 p++;
867 q += sizeof(IPTR) / sizeof(LONG);
871 else
873 switch (flags[arg] & TYPEMASK)
875 case NORMAL:
876 case REST:
877 case SWITCH:
878 /* Simple arguments are just copied. */
879 array[arg] = (IPTR) argbuf[arg];
880 break;
882 case TOGGLE:
883 /* /T logically inverts the argument. */
884 array[arg] = array[arg] ? 0 : ~0;
885 break;
887 case NUMERIC:
888 /* Convert /N argument. */
889 /* Abuse the argbuf buffer. It's not needed anymore. */
890 numstr = (CONST_STRPTR)argbuf[arg];
891 chars = StrToLong(numstr, (LONG *)&argbuf[arg]);
893 if (chars <= 0 || numstr[chars] != '\0')
895 /* Conversion failed. */
896 ERROR(ERROR_BAD_NUMBER);
899 /* Put the result where it belongs. */
900 array[arg] = (IPTR) &argbuf[arg];
901 break;
905 else
907 if (flags[arg] & MULTIPLE)
909 /* Shouldn't be necessary, but some buggy software relies on this.
910 * IBrowse's URL field isn't set to zero.
912 array[arg] = (IPTR)NULL;
917 /* All OK. */
918 error = 0;
919 end:
920 /* Cleanup and return. */
921 FreeVec(iline);
922 FreeVec(flags);
924 if (error)
926 /* ReadArgs() failed. Clean everything up. */
927 if (rdargs)
929 if (rdargs->RDA_Flags & RDAF_ALLOCATED_BY_READARGS)
931 FreeVec(rdargs);
935 FreeVec(dalist);
936 FreeVec(argbuf);
937 FreeVec(strbuf);
938 FreeVec(multvec);
940 me->pr_Result2 = error;
942 return NULL;
944 else
946 /* All went well. Prepare result and return. */
947 rdargs->RDA_DAList = (IPTR) dalist;
948 dalist->ArgBuf = argbuf;
949 dalist->StrBuf = strbuf;
950 dalist->MultVec = multvec;
951 return rdargs;
953 AROS_LIBFUNC_EXIT
954 } /* ReadArgs */
956 #ifdef TEST
957 # include <dos/dos.h>
958 # include <dos/rdargs.h>
959 # include <utility/tagitem.h>
961 # include <proto/dos.h>
963 char cmlargs[] = "TEST/A";
965 char usage[] =
966 "This is exthelp for test\n"
967 "Enter something";
969 #define CML_TEST 0
970 #define CML_END 1
972 LONG cmlvec[CML_END];
975 main(int argc, char **argv)
977 struct RDArgs *rdargs;
979 if ((rdargs = AllocDosObject(DOS_RDARGS, NULL)))
981 rdargs->RDA_ExtHelp = usage; /* FIX: why doesn't this work? */
983 if (!(ReadArgs(cmlargs, cmlvec, rdargs)))
985 PrintFault(IoErr(), "AROS boot");
986 FreeDosObject(DOS_RDARGS, rdargs);
987 exit(RETURN_FAIL);
990 else
992 PrintFault(ERROR_NO_FREE_STORE, "AROS boot");
993 exit(RETURN_FAIL);
996 FreeArgs(rdargs);
997 FreeDosObject(DOS_RDARGS, rdargs);
999 return 0;
1000 } /* main */
1002 #endif /* TEST */