bump version
[AROS.git] / rom / dos / readargs.c
blob0a4ee01d303a5e71cea62b8dc174dfab64b1f411
1 /*
2 Copyright © 1995-2017, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc:
6 Lang: english
7 */
9 #define DEBUG 0
10 #include <aros/debug.h>
12 #include <exec/memory.h>
13 #include <proto/exec.h>
14 #include <dos/rdargs.h>
15 #include <dos/dosextens.h>
16 #include <proto/utility.h>
18 #include "dos_intern.h"
20 #ifdef TEST
21 # include <stdio.h>
22 # include <proto/dos.h>
23 # undef ReadArgs
24 # undef AROS_LH3
25 # define AROS_LH3(t,fn,a1,a2,a3,bt,bn,o,lib) t fn (a1,a2,a3)
26 # undef AROS_LHA
27 # define AROS_LHA(t,n,r) t n
28 # undef AROS_LIBFUNC_INIT
29 # define AROS_LIBFUNC_INIT
30 # undef AROS_LIBFUNC_EXIT
31 # define AROS_LIBFUNC_EXIT
32 #endif
34 /* Fix the end-of-buffer ReadItem() 'well known' bug
35 * where it "ungets" non-terminator (' ','=','\t','\n')
36 * characters at the end of a non-\n terminated buffer.
38 #define READITEM(buff, bufflen, cs) \
39 ({ LONG ret = ReadItem(buff, bufflen, cs); \
40 if (ret == ITEM_UNQUOTED && (cs->CS_CurChr+1) == cs->CS_Length) \
41 cs->CS_CurChr++; \
42 ret; \
45 /* Returns 0 if this is not a '?' line, otherwise
46 * returns the length between the '?' and the '\n'
49 static inline LONG is_question(BYTE * buff, LONG buffsize)
51 LONG i, j = 0;
52 BOOL escaped = FALSE,
53 quoted = FALSE,
54 seen_space = TRUE,
55 seen_question = FALSE;
57 /* Reach end of line */
58 for (i = 0; i < buffsize; i++)
60 /* Only spaces and return are allowed at the end of the line after the
61 * question mark for it to lead to reprompting. BTW, AmigaOS allowed
62 * only one space then... but do we need to be _that_ compatible?
65 switch (buff[i])
67 case '*':
68 if (quoted)
69 escaped = !escaped;
70 break;
71 case '"':
72 if (!escaped)
73 quoted = !quoted;
74 /* Fall through */
75 default:
76 escaped = FALSE;
79 switch (buff[i])
81 case ' ':
82 case '\t':
83 seen_space = TRUE;
84 break;
85 case '?':
86 if ((!quoted) && (seen_space))
88 seen_question = TRUE;
89 seen_space = FALSE;
90 j = i;
92 else
93 seen_question = FALSE;
94 break;
95 case EOF:
96 case '\n':
97 if (seen_question)
98 return (i + 1 - j);
99 else
100 return 0;
101 default:
102 seen_question = seen_space = FALSE;
105 return 0;
108 /*****************************************************************************
110 NAME */
111 #include <proto/dos.h>
113 AROS_LH3(struct RDArgs *, ReadArgs,
115 /* SYNOPSIS */
116 AROS_LHA(CONST_STRPTR, template, D1),
117 AROS_LHA(SIPTR *, array, D2),
118 AROS_LHA(struct RDArgs *, rdargs, D3),
120 /* LOCATION */
121 struct DosLibrary *, DOSBase, 133, Dos)
123 /* FUNCTION
124 Parses the commandline, a given string or Input() and fills
125 an argument array according to the options template given.
126 The array must be initialized to the wanted defaults before
127 each call to ReadArgs(). If the rdargs argument is NULL
128 ReadArgs() tries to parse the commandline and continues
129 on the input channel if it just consists of a single '?',
130 prompting the user for input.
132 INPUTS
133 template - Template string. The template string is given as
134 a number of options separated by ',' and modified
135 by '/' modifiers, e.g. 'NAME,WIDTH/N,HEIGHT/N'
136 means get a name string and two numbers (width and
137 height). The possible modifiers are:
138 /S Option is a switch. It may be either set or
139 left out.
140 /T Option is a boolean value. Requires an argument
141 which may be "ON", "YES" (setting the respective
142 argument to 1), "OFF" or "NO" (setting the
143 respective argument to 0).
144 /N Option is a number. Strings are not allowed.
145 If the option is optional, a pointer to the
146 actual number is returned. This is how you know
147 if it was really given. The number is always of type
148 LONG.
149 /A Argument is required. If it is left out ReadArgs()
150 fails.
151 /K The keyword must be given when filling the option.
152 Normally it's skipped.
153 /M Multiple strings or, when used in combination with /N,
154 numbers. The result is returned as an array of pointers
155 to strings or LONGs, and is terminated with NULL. /M
156 eats all strings that don't fit into any other option.
157 If there are unfilled /A arguments after parsing they
158 steal strings from /M. This makes it possible to, for
159 example, write a Copy command template like
160 'FROM/A/M,TO/A'. There may be only one /M option in a
161 template.
162 /F Eats the rest of the line even if there are option
163 keywords in it.
164 array - Array to be filled with the result values. The array must
165 be intialized to the default values before calling
166 ReadArgs().
167 rdargs - An optional RDArgs structure determining the type of
168 input to process.
170 RESULT
171 A handle for the memory allocated by ReadArgs(). Must be freed
172 with FreeArgs() later.
174 SEE ALSO
175 FreeArgs(), Input()
177 *****************************************************************************/
179 AROS_LIBFUNC_INIT
181 /* Allocated resources */
182 struct DAList *dalist = NULL;
183 UBYTE *flags = NULL;
184 STRPTR strbuf = NULL, iline = NULL;
185 STRPTR *multvec = NULL, *argbuf = NULL;
186 CONST_STRPTR numstr;
187 ULONG multnum = 0, multmax = 0;
188 LONG strbuflen;
190 /* Some variables */
191 CONST_STRPTR cs1;
192 STRPTR s1, s2, *newmult;
193 ULONG arg, numargs, nextarg;
194 LONG it, item, chars, delthis;
195 struct CSource lcs, *cs;
196 BOOL is_file_not_buffer;
197 TEXT argbuff[256 + 1]; /* Maximum BCPL string length + injected \n + ASCIIZ */
199 ASSERT_VALID_PTR(template);
200 ASSERT_VALID_PTR(array);
201 ASSERT_VALID_PTR_OR_NULL(rdargs);
203 D(bug("[ReadArgs] Template: \"%s\"\n", template));
204 /* Get pointer to process structure. */
205 struct Process *me = (struct Process *) FindTask(NULL);
207 ASSERT_VALID_PROCESS(me);
209 /* Error recovery. C has no exceptions. This is a simple replacement. */
210 LONG error;
212 #undef ERROR
213 #define ERROR(a) { error=a; goto end; }
215 /* Template options */
216 #define REQUIRED 0x80 /* /A */
217 #define KEYWORD 0x40 /* /K */
218 #define MULTIPLE 0x20 /* /M */
219 #define TYPEMASK 0x07
220 #define NORMAL 0x00 /* No option */
221 #define SWITCH 0x01 /* /S, implies /K */
222 #define TOGGLE 0x02 /* /T, implies /K */
223 #define NUMERIC 0x03 /* /N */
224 #define REST 0x04 /* /F */
226 /* Flags for each possible character. */
227 static const UBYTE argflags[] =
229 REQUIRED, 0, 0, 0, 0, REST, 0, 0, 0, 0, KEYWORD, 0, MULTIPLE,
230 NUMERIC, 0, 0, 0, 0, SWITCH | KEYWORD, TOGGLE | KEYWORD, 0, 0,
231 0, 0, 0, 0
234 /* Allocate readargs structure (and private internal one) */
235 if (!rdargs)
237 rdargs = (struct RDArgs *) AllocVec(sizeof(struct RDArgs),
238 MEMF_ANY | MEMF_CLEAR);
239 if (rdargs)
241 rdargs->RDA_Flags |= RDAF_ALLOCATED_BY_READARGS;
245 dalist = (struct DAList *) AllocVec(sizeof(struct DAList),
246 MEMF_ANY | MEMF_CLEAR);
248 if (rdargs == NULL || dalist == NULL)
250 ERROR(ERROR_NO_FREE_STORE);
253 /* Init character source. */
254 if (rdargs->RDA_Source.CS_Buffer)
256 cs = &rdargs->RDA_Source;
257 D(bug("[ReadArgs] Buffer: \"%s\"\n", cs->CS_Buffer));
258 is_file_not_buffer = FALSE;
260 else
262 BOOL notempty = TRUE;
263 BPTR input = Input();
265 D(bug("[ReadArgs] Input: 0x%p\n", input));
266 is_file_not_buffer = TRUE;
269 * Take arguments from input stream. They were injected there by either
270 * runcommand.c or createnewproc.c (see vbuf_inject() routine).
271 * This is described in Guru Book.
273 argbuff[0] = 0;
274 lcs.CS_Buffer = &argbuff[0];
277 * Special kludge for interactive filehandles (i. e. CLI windows).
278 * Read data only if filehandle's buffer is not empty. Otherwise
279 * read will cause opening CLI window and waiting for user's input.
280 * As a consequence we still can use ReadArgs() on input redirected
281 * from a file, even if we are started from Workbench (hypothetical
282 * situation).
283 * This prevents opening a CLI window if the program was started from
284 * Workbench and redirected its Input() and Output() to own window,
285 * but still called ReadArgs() after redirection for some reason.
286 * Streams redirection is widely used in AROS startup code.
288 if (IsInteractive(input))
290 struct FileHandle *fh = BADDR(input);
292 notempty = (fh->fh_Pos != fh->fh_End);
295 if (notempty)
296 FGets(input, lcs.CS_Buffer, sizeof(argbuff));
298 D(bug("[ReadArgs] Line: %s\n", argbuff));
300 cs1 = lcs.CS_Buffer;
302 for (; *cs1 != '\0'; ++cs1);
304 lcs.CS_Length = cs1 - lcs.CS_Buffer;
305 lcs.CS_CurChr = 0;
307 cs = &lcs;
310 /* Check for optional reprompting */
311 if (!(rdargs->RDA_Flags & RDAF_NOPROMPT))
313 if ((delthis = is_question(cs->CS_Buffer, cs->CS_Length)))
315 /* '?' was found on the commandline. */
316 BPTR input = Input();
317 BPTR output = Output();
318 ULONG isize = 0, ibuf = 0;
319 LONG c;
320 ULONG helpdisplayed = FALSE;
322 /* Prompt for more input */
324 D(bug("[ReadArgs] '?' found, %d chars to be removed\n", delthis));
325 D(bug("[ReadArgs] rdargs=0x%p\n", rdargs));
326 D(if (rdargs) bug ("[ReadArds] rdargs->RDA_ExtHelp=0x%p\n", rdargs->RDA_ExtHelp);)
328 if (FPuts(output, template) || FPuts(output, ": "))
330 ERROR(me->pr_Result2);
333 if (!Flush(output))
335 ERROR(me->pr_Result2);
338 cs->CS_Length -= delthis;
339 ULONG memsize = isize = ibuf = cs->CS_Length;
340 iline = (STRPTR) AllocVec(ibuf, MEMF_ANY);
341 CopyMem(cs->CS_Buffer, iline, isize);
345 /* Read a line in. */
346 c = -1;
347 for (;;)
349 if (c == '\n')
351 iline[isize] = '\0'; /* end of string */
352 break;
354 if (isize >= ibuf)
356 /* Buffer too small. Get a new one. */
357 STRPTR newiline;
359 ibuf += 256;
361 newiline = (STRPTR) AllocVec(ibuf, MEMF_ANY);
362 if (newiline == NULL)
364 ERROR(ERROR_NO_FREE_STORE);
367 if (iline != NULL)
368 CopyMem(iline, newiline, isize);
370 FreeVec(iline);
372 iline = newiline;
375 /* Read character */
376 if (is_file_not_buffer)
378 c = FGetC(input);
380 else
382 SetIoErr(0);
384 if (cs->CS_CurChr >= cs->CS_Length)
385 c = EOF;
386 else
387 c = cs->CS_Buffer[cs->CS_CurChr++];
390 /* Check and write it. */
391 if (c == EOF && me->pr_Result2)
393 ERROR(me->pr_Result2);
396 /* Fix short buffers to have a trailing '\n' */
397 if (c == EOF || c == '\0')
398 c = '\n';
400 iline[isize++] = c;
402 iline[isize] = '\0'; /* end of string */
404 D(iline[isize] = 0; bug("[ReadArgs] Size %d, line: '%s'\n", isize, iline));
406 /* if user entered single ? again or some string ending
407 with space and ? either display template again or
408 extended help if it's available */
409 if ( (delthis = is_question(iline, isize))
410 && (memsize <= (isize - delthis))
413 helpdisplayed = TRUE;
415 memsize = isize -= delthis;
417 if(rdargs->RDA_ExtHelp != NULL)
419 if (FPuts(output, rdargs->RDA_ExtHelp) || FPuts(output, ": "))
420 ERROR(me->pr_Result2);
422 else if (FPuts(output, template) || FPuts(output, ": "))
424 ERROR(me->pr_Result2);
427 if (!Flush(output))
429 ERROR(me->pr_Result2);
432 else
433 helpdisplayed = FALSE;
434 } while(helpdisplayed);
436 /* Prepare input source for new line. */
437 cs->CS_Buffer = iline;
438 cs->CS_Length = isize;
439 cs->CS_CurChr = 0;
444 * Get enough space for string buffer.
445 * It's always smaller than the size of the input line+1.
448 strbuflen = cs->CS_Length + 1;
449 strbuf = (STRPTR) AllocVec(strbuflen, MEMF_ANY);
451 if (strbuf == NULL)
453 ERROR(ERROR_NO_FREE_STORE);
456 /* Count the number of items in the template (number of ','+1). */
457 numargs = 1;
458 cs1 = template;
460 while (*cs1)
462 if (*cs1++ == ',')
464 numargs++;
468 /* Use this count to get space for temporary flag array and result
469 * buffer. */
470 flags = (UBYTE *) AllocVec(numargs + 1, MEMF_CLEAR);
472 argbuf = (STRPTR *) AllocVec((numargs + 1) * sizeof(STRPTR), MEMF_CLEAR);
474 if (flags == NULL || argbuf == NULL)
476 ERROR(ERROR_NO_FREE_STORE);
479 /* Fill the flag array. */
480 cs1 = template;
481 s2 = flags;
483 while (*cs1)
485 /* A ',' means: goto next item. */
486 if (*cs1 == ',')
488 s2++;
491 /* In case of a '/' use the next character as option. */
492 if (*cs1++ == '/')
494 UBYTE argc = ToUpper(*cs1);
495 if (argc >= 'A' && argc <= 'Z')
496 *s2 |= argflags[argc - 'A'];
500 /* Add a dummy so that the whole line is processed (see below). */
501 *++s2 = MULTIPLE;
504 * Now process commandline for the first time:
505 * Go from left to right and fill all items that need filling.
506 * If an item is given as 'OPTION=VALUE' or 'OPTION VALUE' fill
507 * it out of turn.
508 * NOTE: '<=' comparison is intentional here. When we allocated argbuf, we added one
509 * to the number of arguments. And right above we added fictional MULTIPLE flag.
510 * This is actually needed to make /S and /K working.
512 s1 = strbuf;
514 for (arg = 0; arg <= numargs ; arg = nextarg)
516 nextarg = arg + 1;
518 D(bug("[ReadArgs] Arg %d (0x%x) s1=&strbuf[%d], %d left\n", arg, flags[arg], s1-strbuf, strbuflen));
520 /* Out of buffer space?
521 * This should not have happened, some internal logic
522 * must have broken.
524 if (strbuflen == 0) {
525 D(bug("[ReadArgs] %d: INTERNAL ERROR: Ran out of buffer space.\n", arg));
526 break;
529 /* Skip /K options and options that are already done. */
530 if (flags[arg] & KEYWORD || argbuf[arg] != NULL)
532 continue;
535 #if 0 /* stegerg: if so a template of CLOSE/S,QUICK/S,COMMAND/F would
536 not work correctly if command line for example is
537 "CLOSE QUICK" it would all end up being eaten by COMMAND/F
538 argument */
540 /* If the current option is of type /F do not look for keywords */
541 if ((flags[arg] & TYPEMASK) != REST)
542 #endif
545 /* Get item. Quoted items are never keywords. */
546 it = READITEM(s1, strbuflen, cs);
547 D(bug("[ReadArgs] Item %s type %d\n", s1, it));
549 if (it == ITEM_UNQUOTED)
551 /* Not quoted. Check if it's a keyword. */
552 item = FindArg(template, s1);
554 if (item >= 0 && item < numargs && argbuf[item] == NULL)
556 D(bug("[ReadArgs] %d: Keyword \"%s\" (%d)\n", arg, s1, item));
558 * It's a keyword. Fill it and retry the current option
559 * at the next turn
561 nextarg = arg;
562 arg = item;
564 /* /S /T may not be given as 'OPTION=VALUE'. */
565 if ((flags[item] & TYPEMASK) != SWITCH
566 && (flags[item] & TYPEMASK) != TOGGLE)
568 /* Get value. */
569 it = READITEM(s1, strbuflen, cs);
571 if (it == ITEM_EQUAL)
573 it = READITEM(s1, strbuflen, cs);
574 } else if (it != ITEM_QUOTED && it != ITEM_UNQUOTED) {
575 ERROR(ERROR_KEY_NEEDS_ARG);
581 /* Check returncode of ReadItem(). */
582 if (it == ITEM_EQUAL)
584 ERROR(ERROR_BAD_TEMPLATE);
586 else if (it == ITEM_ERROR)
588 /* ReadItem() never sets an error code, so we use the same
589 error code as AOS */
590 ERROR(ERROR_LINE_TOO_LONG);
592 else if (it == ITEM_NOTHING)
594 break;
598 /* /F takes all the rest, including extra spaces, =, and '"'
599 * NOTE: If the item was quoted, this will strip off the
600 * next '"' mark it sees.
602 if ((flags[arg] & TYPEMASK) == REST)
604 BOOL eat_quote = (it == ITEM_QUOTED) ? TRUE : FALSE;
605 argbuf[arg] = s1;
607 /* Skip past what ReadItem() just read.
609 while (*s1 && strbuflen > 0) {
610 s1++;
611 strbuflen--;
615 * Put the rest into the buffer, including the separator
616 * ReadItem() actually ungets '\n' terminator. So if CurChr points to it,
617 * we don't need to adjust it. Otherwise we duplicate last character of arguments line.
619 if (cs->CS_Buffer[cs->CS_CurChr] != '\n')
620 cs->CS_CurChr--;
621 s2 = &cs->CS_Buffer[cs->CS_CurChr];
623 while (cs->CS_CurChr < cs->CS_Length &&
624 strbuflen > 1 &&
625 *s2 &&
626 *s2 != '\n')
628 cs->CS_CurChr++;
630 if (eat_quote && *s2 == '"')
632 s2++;
633 eat_quote = FALSE;
634 continue;
637 *(s1++) = *(s2++);
638 strbuflen--;
641 *(s1++) = 0;
642 strbuflen--;
643 D(bug("[ReadArgs] /F copy: \"%s\" left=%d, CS_CurChr=%d, CS_Length=%d\n", argbuf[arg], strbuflen, cs->CS_CurChr, cs->CS_Length));
644 it = ITEM_NOTHING;
645 break;
648 if (flags[arg] & MULTIPLE)
650 /* All /M arguments are stored in a buffer. */
651 if (multnum >= multmax)
653 /* Buffer too small. Get a new one. */
654 multmax += 16;
656 newmult = (STRPTR *) AllocVec(multmax * sizeof(char *),
657 MEMF_ANY);
658 if (newmult == NULL)
660 ERROR(ERROR_NO_FREE_STORE);
663 CopyMemQuick((ULONG *) multvec, (ULONG *) newmult,
664 multnum * sizeof(char *));
666 FreeVec(multvec);
668 multvec = newmult;
671 /* Put string into the buffer. */
672 multvec[multnum++] = s1;
674 D(bug("[ReadArgs] %d: Multiple +\"%s\"\n", arg, s1));
675 while (*s1++)
676 --strbuflen;
677 /* Account for the \000 at the end. */
678 --strbuflen;
680 /* /M takes more than one argument, so retry. */
681 nextarg = arg;
683 else if ((flags[arg] & TYPEMASK) == SWITCH
684 || (flags[arg] & TYPEMASK) == TOGGLE)
686 /* /S or /T just set a flag */
687 argbuf[arg] = (char *) ~0;
688 D(bug("[ReadArgs] %d: Toggle\n", arg));
690 else /* NORMAL || NUMERIC */
692 /* Put argument into argument buffer. */
693 argbuf[arg] = s1;
694 D(bug("[ReadArgs] %d: Normal: \"%s\"\n", arg, s1));
696 while (*s1++)
697 --strbuflen;
698 /* Account for the \000 at the end. */
699 --strbuflen;
702 if (cs->CS_CurChr >= cs->CS_Length)
703 break; /* end of input */
706 /* Unfilled /A options steal Arguments from /M */
707 for (arg = numargs; arg-- > 0;)
709 if (flags[arg] & REQUIRED && argbuf[arg] == NULL
710 && !(flags[arg] & MULTIPLE))
712 if (flags[arg] & KEYWORD)
714 /* /K/A argument, which insists on keyword
715 * being used, cannot be satisfied */
717 ERROR(ERROR_TOO_MANY_ARGS); /* yes, strange error number,
718 * but it translates to "wrong
719 * number of arguments" */
723 if (multnum == 0)
725 /* No arguments left? Oh dear! */
726 ERROR(ERROR_REQUIRED_ARG_MISSING);
729 argbuf[arg] = multvec[--multnum];
733 /* Put the rest of /M where it belongs */
734 for (arg = 0; arg < numargs; arg++)
736 if (flags[arg] & MULTIPLE)
738 if (flags[arg] & REQUIRED && multnum == 0)
740 ERROR(ERROR_REQUIRED_ARG_MISSING);
743 if (multnum != 0)
745 /* NULL terminate it. */
746 if (multnum >= multmax)
748 multmax += 16;
750 newmult = (STRPTR *) AllocVec(multmax * sizeof(STRPTR),
751 MEMF_ANY);
753 if (newmult == NULL)
755 ERROR(ERROR_NO_FREE_STORE);
758 CopyMemQuick((ULONG *) multvec, (ULONG *) newmult,
759 multnum * sizeof(char *));
761 FreeVec(multvec);
763 multvec = newmult;
766 multvec[multnum++] = NULL;
767 argbuf[arg] = (STRPTR) multvec;
769 else
771 /* Shouldn't be necessary, but some buggy software relies on this */
772 argbuf[arg] = NULL;
775 break;
779 /* There are some arguments left? Return error. */
780 if (multnum != 0 && arg == numargs)
782 ERROR(ERROR_TOO_MANY_ARGS);
786 * The commandline is processed now. Put the results in the result array.
787 * Convert /N arguments by the way.
789 for (arg = 0; arg < numargs; arg++)
791 /* Just for the arguments given. */
792 if (argbuf[arg] != NULL)
794 if (flags[arg] & MULTIPLE)
796 array[arg] = (IPTR) argbuf[arg];
798 if ((flags[arg] & TYPEMASK) == NUMERIC)
800 STRPTR *p;
801 LONG *q;
803 if (multnum * 2 > multmax)
805 multmax = multnum * 2;
806 newmult = (STRPTR *) AllocVec(multmax * sizeof(STRPTR),
807 MEMF_ANY);
809 if (newmult == NULL)
811 ERROR(ERROR_NO_FREE_STORE);
814 CopyMemQuick((ULONG *) multvec, (ULONG *) newmult,
815 multnum * sizeof(char *));
817 FreeVec(multvec);
819 multvec = newmult;
822 array[arg] = (IPTR) multvec;
823 p = multvec;
824 q = (LONG *) (multvec + multnum);
826 while (*p)
828 /* Convert /N argument. */
829 chars = StrToLong(*p, q);
831 if (chars <= 0 || (*p)[chars])
833 /* Conversion failed. */
834 ERROR(ERROR_BAD_NUMBER);
837 /* Put the result where it belongs. */
838 *p = (STRPTR) q;
839 p++;
840 q += sizeof(IPTR) / sizeof(LONG);
844 else
846 switch (flags[arg] & TYPEMASK)
848 case NORMAL:
849 case REST:
850 case SWITCH:
851 /* Simple arguments are just copied. */
852 array[arg] = (IPTR) argbuf[arg];
853 break;
855 case TOGGLE:
856 /* /T logically inverts the argument. */
857 array[arg] = array[arg] ? 0 : ~0;
858 break;
860 case NUMERIC:
861 /* Convert /N argument. */
862 /* Abuse the argbuf buffer. It's not needed anymore. */
863 numstr = (CONST_STRPTR)argbuf[arg];
864 chars = StrToLong(numstr, (LONG *)&argbuf[arg]);
866 if (chars <= 0 || numstr[chars] != '\0')
868 /* Conversion failed. */
869 ERROR(ERROR_BAD_NUMBER);
872 /* Put the result where it belongs. */
873 array[arg] = (IPTR) &argbuf[arg];
874 break;
878 else
880 if (flags[arg] & MULTIPLE)
882 /* Shouldn't be necessary, but some buggy software relies on this.
883 * IBrowse's URL field isn't set to zero.
885 array[arg] = (IPTR)NULL;
890 /* All OK. */
891 error = 0;
892 end:
893 /* Cleanup and return. */
894 FreeVec(iline);
895 FreeVec(flags);
897 if (error)
899 /* ReadArgs() failed. Clean everything up. */
900 if (rdargs)
902 if (rdargs->RDA_Flags & RDAF_ALLOCATED_BY_READARGS)
904 FreeVec(rdargs);
908 FreeVec(dalist);
909 FreeVec(argbuf);
910 FreeVec(strbuf);
911 FreeVec(multvec);
913 me->pr_Result2 = error;
915 return NULL;
917 else
919 /* All went well. Prepare result and return. */
920 rdargs->RDA_DAList = (IPTR) dalist;
921 dalist->ArgBuf = argbuf;
922 dalist->StrBuf = strbuf;
923 dalist->MultVec = multvec;
924 return rdargs;
926 AROS_LIBFUNC_EXIT
927 } /* ReadArgs */
929 #ifdef TEST
930 # include <dos/dos.h>
931 # include <dos/rdargs.h>
932 # include <utility/tagitem.h>
934 # include <proto/dos.h>
936 char cmlargs[] = "TEST/A";
938 char usage[] =
939 "This is exthelp for test\n"
940 "Enter something";
942 #define CML_TEST 0
943 #define CML_END 1
945 LONG cmlvec[CML_END];
948 main(int argc, char **argv)
950 struct RDArgs *rdargs;
952 if ((rdargs = AllocDosObject(DOS_RDARGS, NULL)))
954 rdargs->RDA_ExtHelp = usage; /* FIX: why doesn't this work? */
956 if (!(ReadArgs(cmlargs, cmlvec, rdargs)))
958 PrintFault(IoErr(), "AROS boot");
959 FreeDosObject(DOS_RDARGS, rdargs);
960 exit(RETURN_FAIL);
963 else
965 PrintFault(ERROR_NO_FREE_STORE, "AROS boot");
966 exit(RETURN_FAIL);
969 FreeArgs(rdargs);
970 FreeDosObject(DOS_RDARGS, rdargs);
972 return 0;
973 } /* main */
975 #endif /* TEST */