Detabbed
[AROS.git] / rom / dos / readargs.c
blob8d1156829e36e25c9fe6aaadf22f4e98f0c608a8
2 /*
3 Copyright © 1995-2011, 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));
301 cs1 = lcs.CS_Buffer;
303 for (; *cs1 != '\0'; ++cs1);
305 lcs.CS_Length = cs1 - lcs.CS_Buffer;
306 lcs.CS_CurChr = 0;
308 cs = &lcs;
311 /* Check for optional reprompting */
312 if (!(rdargs->RDA_Flags & RDAF_NOPROMPT))
314 if ((delthis = is_question(cs->CS_Buffer, cs->CS_Length)))
316 /* '?' was found on the commandline. */
317 BPTR input = Input();
318 BPTR output = Output();
319 ULONG isize = 0, ibuf = 0;
320 LONG c;
321 ULONG helpdisplayed = FALSE;
323 /* Prompt for more input */
325 D(bug("[ReadArgs] '?' found, %d chars to be removed\n", delthis));
326 D(bug("[ReadArgs] rdargs=0x%p\n", rdargs));
327 D(if (rdargs) bug ("[ReadArds] rdargs->RDA_ExtHelp=0x%p\n", rdargs->RDA_ExtHelp);)
329 if (FPuts(output, template) || FPuts(output, ": "))
331 ERROR(me->pr_Result2);
334 if (!Flush(output))
336 ERROR(me->pr_Result2);
339 cs->CS_Length -= delthis;
340 ULONG memsize = isize = ibuf = cs->CS_Length;
341 iline = (STRPTR) AllocVec(ibuf, MEMF_ANY);
342 CopyMem(cs->CS_Buffer, iline, isize);
346 /* Read a line in. */
347 c = -1;
348 for (;;)
350 if (c == '\n')
352 iline[isize] = '\0'; /* end of string */
353 break;
355 if (isize >= ibuf)
357 /* Buffer too small. Get a new one. */
358 STRPTR newiline;
360 ibuf += 256;
362 newiline = (STRPTR) AllocVec(ibuf, MEMF_ANY);
363 if (newiline == NULL)
365 ERROR(ERROR_NO_FREE_STORE);
368 if (iline != NULL)
369 CopyMem(iline, newiline, isize);
371 FreeVec(iline);
373 iline = newiline;
376 /* Read character */
377 if (is_file_not_buffer)
379 c = FGetC(input);
381 else
383 SetIoErr(0);
385 if (cs->CS_CurChr >= cs->CS_Length)
386 c = EOF;
387 else
388 c = cs->CS_Buffer[cs->CS_CurChr++];
391 /* Check and write it. */
392 if (c == EOF && me->pr_Result2)
394 ERROR(me->pr_Result2);
397 /* Fix short buffers to have a trailing '\n' */
398 if (c == EOF || c == '\0')
399 c = '\n';
401 iline[isize++] = c;
403 iline[isize] = '\0'; /* end of string */
405 D(iline[isize] = 0; bug("[ReadArgs] Size %d, line: '%s'\n", isize, iline));
407 /* if user entered single ? again or some string ending
408 with space and ? either display template again or
409 extended help if it's available */
410 if ( (delthis = is_question(iline, isize))
411 && (memsize <= (isize - delthis))
414 helpdisplayed = TRUE;
416 memsize = isize -= delthis;
418 if(rdargs->RDA_ExtHelp != NULL)
420 if (FPuts(output, rdargs->RDA_ExtHelp) || FPuts(output, ": "))
421 ERROR(me->pr_Result2);
423 else if (FPuts(output, template) || FPuts(output, ": "))
425 ERROR(me->pr_Result2);
428 if (!Flush(output))
430 ERROR(me->pr_Result2);
433 else
434 helpdisplayed = FALSE;
435 } while(helpdisplayed);
437 /* Prepare input source for new line. */
438 cs->CS_Buffer = iline;
439 cs->CS_Length = isize;
440 cs->CS_CurChr = 0;
445 * Get enough space for string buffer.
446 * It's always smaller than the size of the input line+1.
449 strbuflen = cs->CS_Length + 1;
450 strbuf = (STRPTR) AllocVec(strbuflen, MEMF_ANY);
452 if (strbuf == NULL)
454 ERROR(ERROR_NO_FREE_STORE);
457 /* Count the number of items in the template (number of ','+1). */
458 numargs = 1;
459 cs1 = template;
461 while (*cs1)
463 if (*cs1++ == ',')
465 numargs++;
469 /* Use this count to get space for temporary flag array and result
470 * buffer. */
471 flags = (UBYTE *) AllocVec(numargs + 1, MEMF_CLEAR);
473 argbuf = (STRPTR *) AllocVec((numargs + 1) * sizeof(STRPTR), MEMF_CLEAR);
475 if (flags == NULL || argbuf == NULL)
477 ERROR(ERROR_NO_FREE_STORE);
480 /* Fill the flag array. */
481 cs1 = template;
482 s2 = flags;
484 while (*cs1)
486 /* A ',' means: goto next item. */
487 if (*cs1 == ',')
489 s2++;
492 /* In case of a '/' use the next character as option. */
493 if (*cs1++ == '/')
495 UBYTE argc = ToUpper(*cs1);
496 if (argc >= 'A' && argc <= 'Z')
497 *s2 |= argflags[argc - 'A'];
501 /* Add a dummy so that the whole line is processed (see below). */
502 *++s2 = MULTIPLE;
505 * Now process commandline for the first time:
506 * Go from left to right and fill all items that need filling.
507 * If an item is given as 'OPTION=VALUE' or 'OPTION VALUE' fill
508 * it out of turn.
509 * NOTE: '<=' comparison is intentional here. When we allocated argbuf, we added one
510 * to the number of arguments. And right above we added fictional MULTIPLE flag.
511 * This is actually needed to make /S and /K working.
513 s1 = strbuf;
515 for (arg = 0; arg <= numargs ; arg = nextarg)
517 nextarg = arg + 1;
519 D(bug("[ReadArgs] Arg %d (0x%x) s1=&strbuf[%d], %d left\n", arg, flags[arg], s1-strbuf, strbuflen));
521 /* Out of buffer space?
522 * This should not have happened, some internal logic
523 * must have broken.
525 if (strbuflen == 0) {
526 D(bug("[ReadArgs] %d: INTERNAL ERROR: Ran out of buffer space.\n", arg));
527 break;
530 /* Skip /K options and options that are already done. */
531 if (flags[arg] & KEYWORD || argbuf[arg] != NULL)
533 continue;
536 #if 0 /* stegerg: if so a template of CLOSE/S,QUICK/S,COMMAND/F would
537 not work correctly if command line for example is
538 "CLOSE QUICK" it would all end up being eaten by COMMAND/F
539 argument */
541 /* If the current option is of type /F do not look for keywords */
542 if ((flags[arg] & TYPEMASK) != REST)
543 #endif
546 /* Get item. Quoted items are never keywords. */
547 it = READITEM(s1, strbuflen, cs);
548 D(bug("[ReadArgs] Item %s type %d\n", s1, it));
550 if (it == ITEM_UNQUOTED)
552 /* Not quoted. Check if it's a keyword. */
553 item = FindArg(template, s1);
555 if (item >= 0 && item < numargs && argbuf[item] == NULL)
557 D(bug("[ReadArgs] %d: Keyword \"%s\" (%d)\n", arg, s1, item));
559 * It's a keyword. Fill it and retry the current option
560 * at the next turn
562 nextarg = arg;
563 arg = item;
565 /* /S /T may not be given as 'OPTION=VALUE'. */
566 if ((flags[item] & TYPEMASK) != SWITCH
567 && (flags[item] & TYPEMASK) != TOGGLE)
569 /* Get value. */
570 it = READITEM(s1, strbuflen, cs);
572 if (it == ITEM_EQUAL)
574 it = READITEM(s1, strbuflen, cs);
575 } else if (it != ITEM_QUOTED && it != ITEM_UNQUOTED) {
576 ERROR(ERROR_KEY_NEEDS_ARG);
582 /* Check returncode of ReadItem(). */
583 if (it == ITEM_EQUAL)
585 ERROR(ERROR_BAD_TEMPLATE);
587 else if (it == ITEM_ERROR)
589 ERROR(me->pr_Result2);
591 else if (it == ITEM_NOTHING)
593 break;
597 /* /F takes all the rest, including extra spaces, =, and '"'
598 * NOTE: If the item was quoted, this will strip off the
599 * next '"' mark it sees.
601 if ((flags[arg] & TYPEMASK) == REST)
603 BOOL eat_quote = (it == ITEM_QUOTED) ? TRUE : FALSE;
604 argbuf[arg] = s1;
606 /* Skip past what ReadItem() just read.
608 while (*s1 && strbuflen > 0) {
609 s1++;
610 strbuflen--;
614 * Put the rest into the buffer, including the separator
615 * ReadItem() actually ungets '\n' terminator. So if CurChr points to it,
616 * we don't need to adjust it. Otherwise we duplicate last character of arguments line.
618 if (cs->CS_Buffer[cs->CS_CurChr] != '\n')
619 cs->CS_CurChr--;
620 s2 = &cs->CS_Buffer[cs->CS_CurChr];
622 while (cs->CS_CurChr < cs->CS_Length &&
623 strbuflen > 1 &&
624 *s2 &&
625 *s2 != '\n')
627 cs->CS_CurChr++;
629 if (eat_quote && *s2 == '"')
631 s2++;
632 eat_quote = FALSE;
633 continue;
636 *(s1++) = *(s2++);
637 strbuflen--;
640 *(s1++) = 0;
641 strbuflen--;
642 D(bug("[ReadArgs] /F copy: \"%s\" left=%d, CS_CurChr=%d, CS_Length=%d\n", argbuf[arg], strbuflen, cs->CS_CurChr, cs->CS_Length));
643 it = ITEM_NOTHING;
644 break;
647 if (flags[arg] & MULTIPLE)
649 /* All /M arguments are stored in a buffer. */
650 if (multnum >= multmax)
652 /* Buffer too small. Get a new one. */
653 multmax += 16;
655 newmult = (STRPTR *) AllocVec(multmax * sizeof(char *),
656 MEMF_ANY);
657 if (newmult == NULL)
659 ERROR(ERROR_NO_FREE_STORE);
662 CopyMemQuick((ULONG *) multvec, (ULONG *) newmult,
663 multnum * sizeof(char *));
665 FreeVec(multvec);
667 multvec = newmult;
670 /* Put string into the buffer. */
671 multvec[multnum++] = s1;
673 D(bug("[ReadArgs] %d: Multiple +\"%s\"\n", arg, s1));
674 while (*s1++)
675 --strbuflen;
676 /* Account for the \000 at the end. */
677 --strbuflen;
679 /* /M takes more than one argument, so retry. */
680 nextarg = arg;
682 else if ((flags[arg] & TYPEMASK) == SWITCH
683 || (flags[arg] & TYPEMASK) == TOGGLE)
685 /* /S or /T just set a flag */
686 argbuf[arg] = (char *) ~0;
687 D(bug("[ReadArgs] %d: Toggle\n", arg));
689 else /* NORMAL || NUMERIC */
691 /* Put argument into argument buffer. */
692 argbuf[arg] = s1;
693 D(bug("[ReadArgs] %d: Normal: \"%s\"\n", arg, s1));
695 while (*s1++)
696 --strbuflen;
697 /* Account for the \000 at the end. */
698 --strbuflen;
701 if (cs->CS_CurChr >= cs->CS_Length)
702 break; /* end of input */
705 /* Unfilled /A options steal Arguments from /M */
706 for (arg = numargs; arg-- > 0;)
708 if (flags[arg] & REQUIRED && argbuf[arg] == NULL
709 && !(flags[arg] & MULTIPLE))
711 if (flags[arg] & KEYWORD)
713 /* /K/A argument, which insists on keyword
714 * being used, cannot be satisfied */
716 ERROR(ERROR_TOO_MANY_ARGS); /* yes, strange error number,
717 * but it translates to "wrong
718 * number of arguments" */
722 if (multnum == 0)
724 /* No arguments left? Oh dear! */
725 ERROR(ERROR_REQUIRED_ARG_MISSING);
728 argbuf[arg] = multvec[--multnum];
732 /* Put the rest of /M where it belongs */
733 for (arg = 0; arg < numargs; arg++)
735 if (flags[arg] & MULTIPLE)
737 if (flags[arg] & REQUIRED && multnum == 0)
739 ERROR(ERROR_REQUIRED_ARG_MISSING);
742 if (multnum != 0)
744 /* NULL terminate it. */
745 if (multnum >= multmax)
747 multmax += 16;
749 newmult = (STRPTR *) AllocVec(multmax * sizeof(STRPTR),
750 MEMF_ANY);
752 if (newmult == NULL)
754 ERROR(ERROR_NO_FREE_STORE);
757 CopyMemQuick((ULONG *) multvec, (ULONG *) newmult,
758 multnum * sizeof(char *));
760 FreeVec(multvec);
762 multvec = newmult;
765 multvec[multnum++] = NULL;
766 argbuf[arg] = (STRPTR) multvec;
768 else
770 /* Shouldn't be necessary, but some buggy software relies on this */
771 argbuf[arg] = NULL;
774 break;
778 /* There are some arguments left? Return error. */
779 if (multnum != 0 && arg == numargs)
781 ERROR(ERROR_TOO_MANY_ARGS);
785 * The commandline is processed now. Put the results in the result array.
786 * Convert /N arguments by the way.
788 for (arg = 0; arg < numargs; arg++)
790 /* Just for the arguments given. */
791 if (argbuf[arg] != NULL)
793 if (flags[arg] & MULTIPLE)
795 array[arg] = (IPTR) argbuf[arg];
797 if ((flags[arg] & TYPEMASK) == NUMERIC)
799 STRPTR *p;
800 LONG *q;
802 if (multnum * 2 > multmax)
804 multmax = multnum * 2;
805 newmult = (STRPTR *) AllocVec(multmax * sizeof(STRPTR),
806 MEMF_ANY);
808 if (newmult == NULL)
810 ERROR(ERROR_NO_FREE_STORE);
813 CopyMemQuick((ULONG *) multvec, (ULONG *) newmult,
814 multnum * sizeof(char *));
816 FreeVec(multvec);
818 multvec = newmult;
821 array[arg] = (IPTR) multvec;
822 p = multvec;
823 q = (LONG *) (multvec + multnum);
825 while (*p)
827 /* Convert /N argument. */
828 chars = StrToLong(*p, q);
830 if (chars <= 0 || (*p)[chars])
832 /* Conversion failed. */
833 ERROR(ERROR_BAD_NUMBER);
836 /* Put the result where it belongs. */
837 *p = (STRPTR) q;
838 p++;
839 q += sizeof(IPTR) / sizeof(LONG);
843 else
845 switch (flags[arg] & TYPEMASK)
847 case NORMAL:
848 case REST:
849 case SWITCH:
850 /* Simple arguments are just copied. */
851 array[arg] = (IPTR) argbuf[arg];
852 break;
854 case TOGGLE:
855 /* /T logically inverts the argument. */
856 array[arg] = array[arg] ? 0 : ~0;
857 break;
859 case NUMERIC:
860 /* Convert /N argument. */
861 /* Abuse the argbuf buffer. It's not needed anymore. */
862 numstr = (CONST_STRPTR)argbuf[arg];
863 chars = StrToLong(numstr, (LONG *)&argbuf[arg]);
865 if (chars <= 0 || numstr[chars] != '\0')
867 /* Conversion failed. */
868 ERROR(ERROR_BAD_NUMBER);
871 /* Put the result where it belongs. */
872 array[arg] = (IPTR) &argbuf[arg];
873 break;
877 else
879 if (flags[arg] & MULTIPLE)
881 /* Shouldn't be necessary, but some buggy software relies on this.
882 * IBrowse's URL field isn't set to zero.
884 array[arg] = (IPTR)NULL;
889 /* All OK. */
890 error = 0;
891 end:
892 /* Cleanup and return. */
893 FreeVec(iline);
894 FreeVec(flags);
896 if (error)
898 /* ReadArgs() failed. Clean everything up. */
899 if (rdargs)
901 if (rdargs->RDA_Flags & RDAF_ALLOCATED_BY_READARGS)
903 FreeVec(rdargs);
907 FreeVec(dalist);
908 FreeVec(argbuf);
909 FreeVec(strbuf);
910 FreeVec(multvec);
912 me->pr_Result2 = error;
914 return NULL;
916 else
918 /* All went well. Prepare result and return. */
919 rdargs->RDA_DAList = (IPTR) dalist;
920 dalist->ArgBuf = argbuf;
921 dalist->StrBuf = strbuf;
922 dalist->MultVec = multvec;
923 return rdargs;
925 AROS_LIBFUNC_EXIT
926 } /* ReadArgs */
928 #ifdef TEST
929 # include <dos/dos.h>
930 # include <dos/rdargs.h>
931 # include <utility/tagitem.h>
933 # include <proto/dos.h>
935 char cmlargs[] = "TEST/A";
937 char usage[] =
938 "This is exthelp for test\n"
939 "Enter something";
941 #define CML_TEST 0
942 #define CML_END 1
944 LONG cmlvec[CML_END];
947 main(int argc, char **argv)
949 struct RDArgs *rdargs;
951 if ((rdargs = AllocDosObject(DOS_RDARGS, NULL)))
953 rdargs->RDA_ExtHelp = usage; /* FIX: why doesn't this work? */
955 if (!(ReadArgs(cmlargs, cmlvec, rdargs)))
957 PrintFault(IoErr(), "AROS boot");
958 FreeDosObject(DOS_RDARGS, rdargs);
959 exit(RETURN_FAIL);
962 else
964 PrintFault(ERROR_NO_FREE_STORE, "AROS boot");
965 exit(RETURN_FAIL);
968 FreeArgs(rdargs);
969 FreeDosObject(DOS_RDARGS, rdargs);
971 return 0;
972 } /* main */
974 #endif /* TEST */