3 Copyright © 1995-2001, The AROS Development Team. All rights reserved.
9 #include <exec/memory.h>
10 #include <proto/exec.h>
11 #include <dos/rdargs.h>
12 #include <dos/dosextens.h>
14 #include "dos_intern.h"
16 #include <aros/debug.h>
20 # include <proto/dos.h>
23 # define AROS_LH3(t,fn,a1,a2,a3,bt,bn,o,lib) t fn (a1,a2,a3)
25 # define AROS_LHA(t,n,r) t n
26 # undef AROS_LIBFUNC_INIT
27 # define AROS_LIBFUNC_INIT
28 # undef AROS_LIBBASE_EXT_DECL
29 # define AROS_LIBBASE_EXT_DECL(bt,bn)
30 # undef AROS_LIBFUNC_EXIT
31 # define AROS_LIBFUNC_EXIT
34 /*****************************************************************************
37 #include <proto/dos.h>
39 AROS_LH3(struct RDArgs
*, ReadArgs
,
42 AROS_LHA(CONST_STRPTR
, template, D1
),
43 AROS_LHA(IPTR
*, array
, D2
),
44 AROS_LHA(struct RDArgs
*, rdargs
, D3
),
47 struct DosLibrary
*, DOSBase
, 133, Dos
)
50 Parses the commandline, a given string or Input() and fills
51 an argument array according to the options template given.
52 The array must be initialized to the wanted defaults before
53 each call to ReadArgs(). If the rdargs argument is NULL
54 ReadArgs() tries to parse the commandline and continues
55 on the input channel if it just consists of a single '?',
56 prompting the user for input.
59 template - Template string. The template string is given as
60 a number of options separated by ',' and modified
61 by '/' modifiers, e.g. 'NAME,WIDTH/N,HEIGHT/N'
62 means get a name string and two numbers (width and
63 height). The possible modifiers are:
64 /S Option is a switch. It may be either set or
66 /T Option is a boolean value. Requires an argument
67 which may be "ON", "YES" (setting the respective
68 argument to 1), "OFF" or "NO" (setting the
69 respective argument to 0).
70 /N Option is a number. Strings are not allowed.
71 If the option is optional, a pointer to the
72 actual number is returned. This is how you know
73 if it was really given.
74 /A Argument is required. If it is left out ReadArgs()
76 /K The keyword must be given when filling the option.
77 Normally it's skipped.
78 /M Multiple strings. The result is returned as a string
79 pointer array terminated with NULL. /M eats all strings
80 that don't fit into any other option. If there are
81 unfilled /A arguments after parsing they steal strings
82 from /M. This makes it possible to e.g. write a COPY
83 template like 'FROM/A/M,TO/A'. There may be only one
84 /M option in a template.
85 /F Eats the rest of the line even if there are option
87 array - Array to be filled with the result values. The array must
88 be intialized to the default values before calling
90 rdargs - An optional RDArgs structure determinating the type of
94 A handle for the memory allocated by ReadArgs(). Must be freed
95 with FreeArgs() later.
100 *****************************************************************************/
103 AROS_LIBBASE_EXT_DECL(struct DosLibrary
*, DOSBase
)
105 /* Allocated resources */
106 struct DAList
*dalist
= NULL
;
108 STRPTR strbuf
= NULL
, iline
= NULL
;
109 STRPTR
*multvec
= NULL
, *argbuf
= NULL
;
110 ULONG multnum
= 0, multmax
= 0;
114 STRPTR s1
, s2
, *newmult
;
115 ULONG arg
, numargs
, nextarg
;
116 LONG it
, item
, chars
, value
;
117 struct CSource lcs
, *cs
;
119 ASSERT_VALID_PTR(template);
120 ASSERT_VALID_PTR(array
);
121 ASSERT_VALID_PTR_OR_NULL(rdargs
);
123 /* Get pointer to process structure. */
124 struct Process
*me
= (struct Process
*) FindTask(NULL
);
126 /* Error recovery. C has no exceptions. This is a simple replacement. */
130 #define ERROR(a) { error=a; goto end; }
132 /* Template options */
133 #define REQUIRED 0x80 /* /A */
134 #define KEYWORD 0x40 /* /K */
135 #define MULTIPLE 0x20 /* /M */
136 #define TYPEMASK 0x07
137 #define NORMAL 0x00 /* No option */
138 #define SWITCH 0x01 /* /S, implies /K */
139 #define TOGGLE 0x02 /* /T, implies /K */
140 #define NUMERIC 0x03 /* /N */
141 #define REST 0x04 /* /F */
143 /* Flags for each possible character. */
144 static const UBYTE argflags
[] =
146 REQUIRED
, 0, 0, 0, 0, REST
, 0, 0, 0, 0, KEYWORD
, 0, MULTIPLE
,
147 NUMERIC
, 0, 0, 0, 0, SWITCH
| KEYWORD
, TOGGLE
| KEYWORD
, 0, 0,
151 /* Allocate readargs structure (and private internal one) */
154 rdargs
= (struct RDArgs
*) AllocVec(sizeof(struct RDArgs
),
155 MEMF_ANY
| MEMF_CLEAR
);
158 rdargs
->RDA_Flags
|= RDAF_ALLOCATED_BY_READARGS
;
162 dalist
= (struct DAList
*) AllocVec(sizeof(struct DAList
),
163 MEMF_ANY
| MEMF_CLEAR
);
165 if (rdargs
== NULL
|| dalist
== NULL
)
167 ERROR(ERROR_NO_FREE_STORE
);
170 /* Init character source. */
171 if (rdargs
->RDA_Source
.CS_Buffer
)
173 cs
= &rdargs
->RDA_Source
;
177 lcs
.CS_Buffer
= (me
->pr_Arguments
? me
->pr_Arguments
: (UBYTE
*) "");
185 lcs
.CS_Length
= (IPTR
) cs1
- (IPTR
) lcs
.CS_Buffer
- 1;
191 /* Check for optional reprompting */
192 if (!(rdargs
->RDA_Flags
& RDAF_NOPROMPT
))
194 /* Check commandline for a single '?' */
197 /* Skip leading whitespace */
198 while (*cs1
== ' ' || *cs1
== '\t')
206 /* Skip whitespace */
207 while (*cs1
== ' ' || *cs1
== '\t')
213 if (*cs1
== '\n' || !*cs1
)
215 /* Only a single '?' on the commandline. */
216 BPTR input
= Input();
217 BPTR output
= Output();
218 ULONG isize
= 0, ibuf
= 0;
221 /* Prompt for more input */
223 /* printf ("Only ? found\n");
224 printf ("rdargs=%p\n", rdargs);
226 printf ("rdargs->RDA_ExtHelp=%p\n", rdargs->RDA_ExtHelp); */
228 if (rdargs
->RDA_ExtHelp
!= NULL
)
230 if (FPuts(output
, rdargs
->RDA_ExtHelp
))
231 ERROR(me
->pr_Result2
);
234 if (FPuts(output
, template) || FPuts(output
, ": "))
236 ERROR(me
->pr_Result2
);
241 ERROR(me
->pr_Result2
);
244 /* Read a line in. */
249 /* Buffer too small. Get a new one. */
254 newiline
= (STRPTR
) AllocVec(ibuf
, MEMF_ANY
);
256 if (newiline
== NULL
)
258 ERROR(ERROR_NO_FREE_STORE
);
261 CopyMemQuick((ULONG
*) iline
,
262 (ULONG
*) newiline
, isize
);
272 /* Check and write it. */
273 if (c
== EOF
&& me
->pr_Result2
)
275 ERROR(me
->pr_Result2
);
278 if (c
== EOF
|| c
== '\n' || !c
)
280 /* stegerg: added this. Otherwise try "list ?" then enter only "l" + RETURN
281 * and you will get a broken wall in FreeMem reported. This happens in
282 * FreeArgs() during the FreeVec() of the StrBuf. Appending '\n' here fixes
283 * this, but maybe the real bug is somewhere else. */
285 iline
[isize
++] = '\n';
295 /* Prepare input source for new line. */
296 cs
->CS_Buffer
= iline
;
297 cs
->CS_Length
= isize
;
303 * Get enough space for string buffer.
304 * It's always smaller than the size of the input line+1.
307 strbuf
= (STRPTR
) AllocVec(cs
->CS_Length
+ 1, MEMF_ANY
);
311 ERROR(ERROR_NO_FREE_STORE
);
314 /* Count the number of items in the template (number of ','+1). */
326 /* Use this count to get space for temporary flag array and result
328 flags
= (UBYTE
*) AllocVec(numargs
+ 1, MEMF_CLEAR
);
330 argbuf
= (STRPTR
*) AllocVec((numargs
+ 1) * sizeof(STRPTR
), MEMF_CLEAR
);
332 if (flags
== NULL
|| argbuf
== NULL
)
334 ERROR(ERROR_NO_FREE_STORE
);
337 /* Fill the flag array. */
343 /* A ',' means: goto next item. */
349 /* In case of a '/' use the next character as option. */
352 *s2
|= argflags
[*cs1
- 'A'];
356 /* Add a dummy so that the whole line is processed. */
360 * Now process commandline for the first time:
361 * Go from left to right and fill all items that need filling.
362 * If an item is given as 'OPTION=VALUE' or 'OPTION VALUE' fill
367 for (arg
= 0; arg
<= numargs
; arg
= nextarg
)
371 /* Skip /K options and options that are already done. */
372 if (flags
[arg
] & KEYWORD
|| argbuf
[arg
] != NULL
)
377 #if 0 /* stegerg: if so a template of CLOSE/S,QUICK/S,COMMAND/F would
378 not work correctly if command line for example is
379 "CLOSE QUICK" it would all end up being eaten by COMMAND/F
382 /* If the current option is of type /F do not look for keywords */
383 if ((flags
[arg
] & TYPEMASK
) != REST
)
387 /* Get item. Quoted items are no keywords. */
388 it
= ReadItem(s1
, ~0ul / 2, cs
);
390 if (it
== ITEM_UNQUOTED
)
392 /* Not quoted. Check if it's a keyword. */
393 item
= FindArg(template, s1
);
395 if (item
>= 0 && item
< numargs
&& argbuf
[item
] == NULL
)
398 * It's a keyword. Fill it and retry the current option
404 /* /S /T may not be given as 'OPTION=VALUE'. */
405 if ((flags
[item
] & TYPEMASK
) != SWITCH
406 && (flags
[item
] & TYPEMASK
) != TOGGLE
)
409 it
= ReadItem(s1
, ~0ul / 2, cs
);
411 if (it
== ITEM_EQUAL
)
413 it
= ReadItem(s1
, ~0ul / 2, cs
);
419 /* Check returncode of ReadItem(). */
420 if (it
== ITEM_EQUAL
)
422 ERROR(ERROR_BAD_TEMPLATE
);
424 else if (it
== ITEM_ERROR
)
426 ERROR(me
->pr_Result2
);
428 else if (it
== ITEM_NOTHING
)
434 /* /F takes all the rest */
435 /* TODO: Take care of quoted strings(?) */
436 if ((flags
[arg
] & TYPEMASK
) == REST
)
439 /* Skip leading whitespace */
440 while (cs
->CS_CurChr
< cs
->CS_Length
441 && (cs
->CS_Buffer
[cs
->CS_CurChr
] == ' '
442 || cs
->CS_Buffer
[cs
->CS_CurChr
] == '\t'))
449 /* Copy part already read above by ReadItem() */
455 /* Find the last non-whitespace character */
458 while (cs
->CS_CurChr
< cs
->CS_Length
459 && cs
->CS_Buffer
[cs
->CS_CurChr
]
460 && cs
->CS_Buffer
[cs
->CS_CurChr
] != '\n')
462 if (cs
->CS_Buffer
[cs
->CS_CurChr
] != ' '
463 && cs
->CS_Buffer
[cs
->CS_CurChr
] != '\t')
468 /* Copy string by the way. */
469 *s1
++ = cs
->CS_Buffer
[cs
->CS_CurChr
++];
472 /* Add terminator (1 after the character found). */
478 if (flags
[arg
] & MULTIPLE
)
480 /* All /M arguments are stored in a buffer. */
481 if (multnum
>= multmax
)
483 /* Buffer too small. Get a new one. */
486 newmult
= (STRPTR
*) AllocVec(multmax
* sizeof(char *),
490 ERROR(ERROR_NO_FREE_STORE
);
493 CopyMemQuick((ULONG
*) multvec
, (ULONG
*) newmult
,
494 multnum
* sizeof(char *));
501 /* Put string into the buffer. */
502 multvec
[multnum
++] = s1
;
508 /* /M takes more than one argument, so retry. */
511 else if ((flags
[arg
] & TYPEMASK
) == SWITCH
512 || (flags
[arg
] & TYPEMASK
) == TOGGLE
)
514 /* /S or /T just set a flag */
515 argbuf
[arg
] = (char *) ~0;
517 else /* NORMAL || NUMERIC */
519 /* Put argument into argument buffer. */
528 /* Unfilled /A options steal Arguments from /M */
529 for (arg
= numargs
; arg
-- > 0;)
531 if (flags
[arg
] & REQUIRED
&& argbuf
[arg
] == NULL
532 && !(flags
[arg
] & MULTIPLE
))
534 if (flags
[arg
] & KEYWORD
)
536 /* /K/A argument, which inisits on keyword
537 * being used, cannot be satisfied */
539 ERROR(ERROR_TOO_MANY_ARGS
); /* yes, strange error number,
540 * but it translates to "wrong
541 * number of arguments" */
547 /* No arguments left? Oh dear! */
548 ERROR(ERROR_REQUIRED_ARG_MISSING
);
551 argbuf
[arg
] = multvec
[--multnum
];
555 /* Put the rest of /M where it belongs */
556 for (arg
= 0; arg
< numargs
; arg
++)
558 if (flags
[arg
] & MULTIPLE
)
560 if (flags
[arg
] & REQUIRED
&& !multnum
)
562 ERROR(ERROR_REQUIRED_ARG_MISSING
);
567 /* NULL terminate it. */
568 if (multnum
>= multmax
)
572 newmult
= (STRPTR
*) AllocVec(multmax
* sizeof(STRPTR
),
577 ERROR(ERROR_NO_FREE_STORE
);
580 CopyMemQuick((ULONG
*) multvec
, (ULONG
*) newmult
,
581 multnum
* sizeof(char *));
588 multvec
[multnum
++] = NULL
;
589 argbuf
[arg
] = (STRPTR
) multvec
;
593 /* Shouldn't be necessary, but some buggy software relies on this */
601 /* There are some arguments left? Return error. */
602 if (multnum
&& arg
== numargs
)
604 ERROR(ERROR_TOO_MANY_ARGS
);
608 * The commandline is processed now. Put the results in the result array.
609 * Convert /N arguments by the way.
611 for (arg
= 0; arg
< numargs
; arg
++)
613 /* Just for the arguments given. */
614 if (argbuf
[arg
] != NULL
)
616 if (flags
[arg
] & MULTIPLE
)
618 array
[arg
] = (IPTR
) argbuf
[arg
];
620 if ((flags
[arg
] & TYPEMASK
) == NUMERIC
)
625 if (multnum
* 2 > multmax
)
627 multmax
= multnum
* 2;
628 newmult
= (STRPTR
*) AllocVec(multmax
* sizeof(STRPTR
),
633 ERROR(ERROR_NO_FREE_STORE
);
636 CopyMemQuick((ULONG
*) multvec
, (ULONG
*) newmult
,
637 multnum
* sizeof(char *));
644 array
[arg
] = (IPTR
) multvec
;
646 q
= (LONG
*) (multvec
+ multnum
);
650 /* Convert /N argument. */
651 chars
= StrToLong(*p
, q
);
653 if (chars
<= 0 || (*p
)[chars
])
655 /* Conversion failed. */
656 ERROR(ERROR_BAD_NUMBER
);
659 /* Put the result where it belongs. */
668 switch (flags
[arg
] & TYPEMASK
)
673 /* Simple arguments are just copied. */
674 array
[arg
] = (IPTR
) argbuf
[arg
];
678 /* /T logically inverts the argument. */
679 array
[arg
] = array
[arg
] ? 0 : ~0;
683 /* Convert /N argument. */
684 chars
= StrToLong(argbuf
[arg
], &value
);
686 if (chars
<= 0 || argbuf
[arg
][chars
])
688 /* Conversion failed. */
689 ERROR(ERROR_BAD_NUMBER
);
692 /* Put the result where it belongs. */
694 if (flags
[arg
] & REQUIRED
)
695 /* Required argument. Return number. */
700 /* Abuse the argbuf buffer. It's not needed anymore. */
701 argbuf
[arg
] = (STRPTR
) value
;
702 array
[arg
] = (IPTR
) & argbuf
[arg
];
710 if (flags
[arg
] & MULTIPLE
)
712 /* Shouldn't be necessary, but some buggy software relies on this.
713 * IBrowse's URL field isn't set to zero.
715 array
[arg
] = (IPTR
)NULL
;
723 /* Cleanup and return. */
729 /* ReadArgs() failed. Clean everything up. */
732 if (rdargs
->RDA_Flags
& RDAF_ALLOCATED_BY_READARGS
)
743 me
->pr_Result2
= error
;
749 /* All went well. Prepare result and return. */
750 rdargs
->RDA_DAList
= (IPTR
) dalist
;
751 dalist
->ArgBuf
= argbuf
;
752 dalist
->StrBuf
= strbuf
;
753 dalist
->MultVec
= multvec
;
760 # include <dos/dos.h>
761 # include <dos/rdargs.h>
762 # include <utility/tagitem.h>
764 # include <proto/dos.h>
766 char cmlargs
[] = "TEST/A";
769 "This is exthelp for test\n"
775 LONG cmlvec
[CML_END
];
778 main(int argc
, char **argv
)
780 struct RDArgs
*rdargs
;
782 if ((rdargs
= AllocDosObject(DOS_RDARGS
, NULL
)))
784 rdargs
->RDA_ExtHelp
= usage
; /* FIX: why doesn't this work? */
786 if (!(ReadArgs(cmlargs
, cmlvec
, rdargs
)))
788 PrintFault(IoErr(), "AROS boot");
789 FreeDosObject(DOS_RDARGS
, rdargs
);
795 PrintFault(ERROR_NO_FREE_STORE
, "AROS boot");
800 FreeDosObject(DOS_RDARGS
, rdargs
);