3 Copyright © 1995-2008, 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_LIBFUNC_EXIT
29 # define AROS_LIBFUNC_EXIT
32 /*****************************************************************************
35 #include <proto/dos.h>
37 AROS_LH3(struct RDArgs
*, ReadArgs
,
40 AROS_LHA(CONST_STRPTR
, template, D1
),
41 AROS_LHA(IPTR
*, array
, D2
),
42 AROS_LHA(struct RDArgs
*, rdargs
, D3
),
45 struct DosLibrary
*, DOSBase
, 133, Dos
)
48 Parses the commandline, a given string or Input() and fills
49 an argument array according to the options template given.
50 The array must be initialized to the wanted defaults before
51 each call to ReadArgs(). If the rdargs argument is NULL
52 ReadArgs() tries to parse the commandline and continues
53 on the input channel if it just consists of a single '?',
54 prompting the user for input.
57 template - Template string. The template string is given as
58 a number of options separated by ',' and modified
59 by '/' modifiers, e.g. 'NAME,WIDTH/N,HEIGHT/N'
60 means get a name string and two numbers (width and
61 height). The possible modifiers are:
62 /S Option is a switch. It may be either set or
64 /T Option is a boolean value. Requires an argument
65 which may be "ON", "YES" (setting the respective
66 argument to 1), "OFF" or "NO" (setting the
67 respective argument to 0).
68 /N Option is a number. Strings are not allowed.
69 If the option is optional, a pointer to the
70 actual number is returned. This is how you know
71 if it was really given. The number is always of type
73 /A Argument is required. If it is left out ReadArgs()
75 /K The keyword must be given when filling the option.
76 Normally it's skipped.
77 /M Multiple strings or, when used in combination with /N,
78 numbers. The result is returned as an array of pointers
79 to strings or LONGs, and is terminated with NULL. /M
80 eats all strings that don't fit into any other option.
81 If there are unfilled /A arguments after parsing they
82 steal strings from /M. This makes it possible to, for
83 example, write a Copy command template like
84 'FROM/A/M,TO/A'. There may be only one /M option in a
86 /F Eats the rest of the line even if there are option
88 array - Array to be filled with the result values. The array must
89 be intialized to the default values before calling
91 rdargs - An optional RDArgs structure determinating the type of
95 A handle for the memory allocated by ReadArgs(). Must be freed
96 with FreeArgs() later.
101 *****************************************************************************/
105 /* Allocated resources */
106 struct DAList
*dalist
= NULL
;
108 STRPTR strbuf
= NULL
, iline
= NULL
;
109 STRPTR
*multvec
= NULL
, *argbuf
= NULL
;
111 ULONG multnum
= 0, multmax
= 0;
115 STRPTR s1
, s2
, *newmult
;
116 ULONG arg
, numargs
, nextarg
;
117 LONG it
, item
, chars
;
118 struct CSource lcs
, *cs
;
120 ASSERT_VALID_PTR(template);
121 ASSERT_VALID_PTR(array
);
122 ASSERT_VALID_PTR_OR_NULL(rdargs
);
124 /* Get pointer to process structure. */
125 struct Process
*me
= (struct Process
*) FindTask(NULL
);
127 /* Error recovery. C has no exceptions. This is a simple replacement. */
131 #define ERROR(a) { error=a; goto end; }
133 /* Template options */
134 #define REQUIRED 0x80 /* /A */
135 #define KEYWORD 0x40 /* /K */
136 #define MULTIPLE 0x20 /* /M */
137 #define TYPEMASK 0x07
138 #define NORMAL 0x00 /* No option */
139 #define SWITCH 0x01 /* /S, implies /K */
140 #define TOGGLE 0x02 /* /T, implies /K */
141 #define NUMERIC 0x03 /* /N */
142 #define REST 0x04 /* /F */
144 /* Flags for each possible character. */
145 static const UBYTE argflags
[] =
147 REQUIRED
, 0, 0, 0, 0, REST
, 0, 0, 0, 0, KEYWORD
, 0, MULTIPLE
,
148 NUMERIC
, 0, 0, 0, 0, SWITCH
| KEYWORD
, TOGGLE
| KEYWORD
, 0, 0,
152 /* Allocate readargs structure (and private internal one) */
155 rdargs
= (struct RDArgs
*) AllocVec(sizeof(struct RDArgs
),
156 MEMF_ANY
| MEMF_CLEAR
);
159 rdargs
->RDA_Flags
|= RDAF_ALLOCATED_BY_READARGS
;
163 dalist
= (struct DAList
*) AllocVec(sizeof(struct DAList
),
164 MEMF_ANY
| MEMF_CLEAR
);
166 if (rdargs
== NULL
|| dalist
== NULL
)
168 ERROR(ERROR_NO_FREE_STORE
);
171 /* Init character source. */
172 if (rdargs
->RDA_Source
.CS_Buffer
)
174 cs
= &rdargs
->RDA_Source
;
178 lcs
.CS_Buffer
= (me
->pr_Arguments
? me
->pr_Arguments
: (UBYTE
*) "");
186 lcs
.CS_Length
= (IPTR
) cs1
- (IPTR
) lcs
.CS_Buffer
- 1;
192 /* Check for optional reprompting */
193 if (!(rdargs
->RDA_Flags
& RDAF_NOPROMPT
))
195 /* Check commandline for a single '?' */
198 /* Skip leading whitespace */
199 while (*cs1
== ' ' || *cs1
== '\t')
207 /* Skip whitespace */
208 while (*cs1
== ' ' || *cs1
== '\t')
214 if (*cs1
== '\n' || !*cs1
)
216 /* Only a single '?' on the commandline. */
217 BPTR input
= Input();
218 BPTR output
= Output();
219 ULONG isize
= 0, ibuf
= 0;
221 ULONG helpdisplayed
= FALSE
;
223 /* Prompt for more input */
225 /* printf ("Only ? found\n");
226 printf ("rdargs=%p\n", rdargs);
228 printf ("rdargs->RDA_ExtHelp=%p\n", rdargs->RDA_ExtHelp); */
230 if (FPuts(output
, template) || FPuts(output
, ": "))
232 ERROR(me
->pr_Result2
);
237 ERROR(me
->pr_Result2
);
241 /* Read a line in. */
246 /* Buffer too small. Get a new one. */
251 newiline
= (STRPTR
) AllocVec(ibuf
, MEMF_ANY
);
253 if (newiline
== NULL
)
255 ERROR(ERROR_NO_FREE_STORE
);
259 CopyMemQuick(iline
, newiline
, isize
);
269 /* Check and write it. */
270 if (c
== EOF
&& me
->pr_Result2
)
272 ERROR(me
->pr_Result2
);
275 if (c
== EOF
|| c
== '\n' || c
== '\0')
277 /* stegerg: added this. Otherwise try "list ?" then enter only "l" + RETURN
278 * and you will get a broken wall in FreeMem reported. This happens in
279 * FreeArgs() during the FreeVec() of the StrBuf. Appending '\n' here fixes
280 * this, but maybe the real bug is somewhere else. */
282 iline
[isize
++] = '\n';
292 /* if user entered single ? again or some string ending
293 with space and ? either display template again or
294 extended help if it's available */
295 if ((isize
== 2 || (isize
> 2 && iline
[isize
-3] == ' '))
296 && iline
[isize
-2] == '?' )
298 helpdisplayed
= TRUE
;
300 if(rdargs
->RDA_ExtHelp
!= NULL
)
302 if (FPuts(output
, rdargs
->RDA_ExtHelp
) || FPuts(output
, ": "))
303 ERROR(me
->pr_Result2
);
305 else if (FPuts(output
, template) || FPuts(output
, ": "))
307 ERROR(me
->pr_Result2
);
312 ERROR(me
->pr_Result2
);
316 helpdisplayed
= FALSE
;
318 while(helpdisplayed
);
320 /* Prepare input source for new line. */
321 cs
->CS_Buffer
= iline
;
322 cs
->CS_Length
= isize
;
328 * Get enough space for string buffer.
329 * It's always smaller than the size of the input line+1.
332 strbuf
= (STRPTR
) AllocVec(cs
->CS_Length
+ 1, MEMF_ANY
);
336 ERROR(ERROR_NO_FREE_STORE
);
339 /* Count the number of items in the template (number of ','+1). */
351 /* Use this count to get space for temporary flag array and result
353 flags
= (UBYTE
*) AllocVec(numargs
+ 1, MEMF_CLEAR
);
355 argbuf
= (STRPTR
*) AllocVec((numargs
+ 1) * sizeof(STRPTR
), MEMF_CLEAR
);
357 if (flags
== NULL
|| argbuf
== NULL
)
359 ERROR(ERROR_NO_FREE_STORE
);
362 /* Fill the flag array. */
368 /* A ',' means: goto next item. */
374 /* In case of a '/' use the next character as option. */
377 *s2
|= argflags
[*cs1
- 'A'];
381 /* Add a dummy so that the whole line is processed. */
385 * Now process commandline for the first time:
386 * Go from left to right and fill all items that need filling.
387 * If an item is given as 'OPTION=VALUE' or 'OPTION VALUE' fill
392 for (arg
= 0; arg
<= numargs
; arg
= nextarg
)
396 /* Skip /K options and options that are already done. */
397 if (flags
[arg
] & KEYWORD
|| argbuf
[arg
] != NULL
)
402 #if 0 /* stegerg: if so a template of CLOSE/S,QUICK/S,COMMAND/F would
403 not work correctly if command line for example is
404 "CLOSE QUICK" it would all end up being eaten by COMMAND/F
407 /* If the current option is of type /F do not look for keywords */
408 if ((flags
[arg
] & TYPEMASK
) != REST
)
412 /* Get item. Quoted items are never keywords. */
413 it
= ReadItem(s1
, ~0ul / 2, cs
);
415 if (it
== ITEM_UNQUOTED
)
417 /* Not quoted. Check if it's a keyword. */
418 item
= FindArg(template, s1
);
420 if (item
>= 0 && item
< numargs
&& argbuf
[item
] == NULL
)
423 * It's a keyword. Fill it and retry the current option
429 /* /S /T may not be given as 'OPTION=VALUE'. */
430 if ((flags
[item
] & TYPEMASK
) != SWITCH
431 && (flags
[item
] & TYPEMASK
) != TOGGLE
)
434 it
= ReadItem(s1
, ~0ul / 2, cs
);
436 if (it
== ITEM_EQUAL
)
438 it
= ReadItem(s1
, ~0ul / 2, cs
);
444 /* Check returncode of ReadItem(). */
445 if (it
== ITEM_EQUAL
)
447 ERROR(ERROR_BAD_TEMPLATE
);
449 else if (it
== ITEM_ERROR
)
451 ERROR(me
->pr_Result2
);
453 else if (it
== ITEM_NOTHING
)
459 /* /F takes all the rest */
460 /* TODO: Take care of quoted strings(?) */
461 if ((flags
[arg
] & TYPEMASK
) == REST
)
464 /* Skip leading whitespace */
465 while (cs
->CS_CurChr
< cs
->CS_Length
466 && (cs
->CS_Buffer
[cs
->CS_CurChr
] == ' '
467 || cs
->CS_Buffer
[cs
->CS_CurChr
] == '\t'))
474 /* Copy part already read above by ReadItem() */
480 /* Find the last non-whitespace character */
483 while (cs
->CS_CurChr
< cs
->CS_Length
484 && cs
->CS_Buffer
[cs
->CS_CurChr
]
485 && cs
->CS_Buffer
[cs
->CS_CurChr
] != '\n')
487 if (cs
->CS_Buffer
[cs
->CS_CurChr
] != ' '
488 && cs
->CS_Buffer
[cs
->CS_CurChr
] != '\t')
493 /* Copy string by the way. */
494 *s1
++ = cs
->CS_Buffer
[cs
->CS_CurChr
++];
497 /* Add terminator (1 after the character found). */
503 if (flags
[arg
] & MULTIPLE
)
505 /* All /M arguments are stored in a buffer. */
506 if (multnum
>= multmax
)
508 /* Buffer too small. Get a new one. */
511 newmult
= (STRPTR
*) AllocVec(multmax
* sizeof(char *),
515 ERROR(ERROR_NO_FREE_STORE
);
518 CopyMemQuick((ULONG
*) multvec
, (ULONG
*) newmult
,
519 multnum
* sizeof(char *));
526 /* Put string into the buffer. */
527 multvec
[multnum
++] = s1
;
533 /* /M takes more than one argument, so retry. */
536 else if ((flags
[arg
] & TYPEMASK
) == SWITCH
537 || (flags
[arg
] & TYPEMASK
) == TOGGLE
)
539 /* /S or /T just set a flag */
540 argbuf
[arg
] = (char *) ~0;
542 else /* NORMAL || NUMERIC */
544 /* Put argument into argument buffer. */
553 /* Unfilled /A options steal Arguments from /M */
554 for (arg
= numargs
; arg
-- > 0;)
556 if (flags
[arg
] & REQUIRED
&& argbuf
[arg
] == NULL
557 && !(flags
[arg
] & MULTIPLE
))
559 if (flags
[arg
] & KEYWORD
)
561 /* /K/A argument, which inisits on keyword
562 * being used, cannot be satisfied */
564 ERROR(ERROR_TOO_MANY_ARGS
); /* yes, strange error number,
565 * but it translates to "wrong
566 * number of arguments" */
572 /* No arguments left? Oh dear! */
573 ERROR(ERROR_REQUIRED_ARG_MISSING
);
576 argbuf
[arg
] = multvec
[--multnum
];
580 /* Put the rest of /M where it belongs */
581 for (arg
= 0; arg
< numargs
; arg
++)
583 if (flags
[arg
] & MULTIPLE
)
585 if (flags
[arg
] & REQUIRED
&& !multnum
)
587 ERROR(ERROR_REQUIRED_ARG_MISSING
);
592 /* NULL terminate it. */
593 if (multnum
>= multmax
)
597 newmult
= (STRPTR
*) AllocVec(multmax
* sizeof(STRPTR
),
602 ERROR(ERROR_NO_FREE_STORE
);
605 CopyMemQuick((ULONG
*) multvec
, (ULONG
*) newmult
,
606 multnum
* sizeof(char *));
613 multvec
[multnum
++] = NULL
;
614 argbuf
[arg
] = (STRPTR
) multvec
;
618 /* Shouldn't be necessary, but some buggy software relies on this */
626 /* There are some arguments left? Return error. */
627 if (multnum
&& arg
== numargs
)
629 ERROR(ERROR_TOO_MANY_ARGS
);
633 * The commandline is processed now. Put the results in the result array.
634 * Convert /N arguments by the way.
636 for (arg
= 0; arg
< numargs
; arg
++)
638 /* Just for the arguments given. */
639 if (argbuf
[arg
] != NULL
)
641 if (flags
[arg
] & MULTIPLE
)
643 array
[arg
] = (IPTR
) argbuf
[arg
];
645 if ((flags
[arg
] & TYPEMASK
) == NUMERIC
)
650 if (multnum
* 2 > multmax
)
652 multmax
= multnum
* 2;
653 newmult
= (STRPTR
*) AllocVec(multmax
* sizeof(STRPTR
),
658 ERROR(ERROR_NO_FREE_STORE
);
661 CopyMemQuick((ULONG
*) multvec
, (ULONG
*) newmult
,
662 multnum
* sizeof(char *));
669 array
[arg
] = (IPTR
) multvec
;
671 q
= (LONG
*) (multvec
+ multnum
);
675 /* Convert /N argument. */
676 chars
= StrToLong(*p
, q
);
678 if (chars
<= 0 || (*p
)[chars
])
680 /* Conversion failed. */
681 ERROR(ERROR_BAD_NUMBER
);
684 /* Put the result where it belongs. */
687 q
+= sizeof(IPTR
) / sizeof(LONG
);
693 switch (flags
[arg
] & TYPEMASK
)
698 /* Simple arguments are just copied. */
699 array
[arg
] = (IPTR
) argbuf
[arg
];
703 /* /T logically inverts the argument. */
704 array
[arg
] = array
[arg
] ? 0 : ~0;
708 /* Convert /N argument. */
709 /* Abuse the argbuf buffer. It's not needed anymore. */
710 numstr
= (CONST_STRPTR
)argbuf
[arg
];
711 chars
= StrToLong(numstr
, (LONG
*)&argbuf
[arg
]);
713 if (chars
<= 0 || numstr
[chars
] != '\0')
715 /* Conversion failed. */
716 ERROR(ERROR_BAD_NUMBER
);
719 /* Put the result where it belongs. */
720 array
[arg
] = (IPTR
) &argbuf
[arg
];
727 if (flags
[arg
] & MULTIPLE
)
729 /* Shouldn't be necessary, but some buggy software relies on this.
730 * IBrowse's URL field isn't set to zero.
732 array
[arg
] = (IPTR
)NULL
;
740 /* Cleanup and return. */
746 /* ReadArgs() failed. Clean everything up. */
749 if (rdargs
->RDA_Flags
& RDAF_ALLOCATED_BY_READARGS
)
760 me
->pr_Result2
= error
;
766 /* All went well. Prepare result and return. */
767 rdargs
->RDA_DAList
= (IPTR
) dalist
;
768 dalist
->ArgBuf
= argbuf
;
769 dalist
->StrBuf
= strbuf
;
770 dalist
->MultVec
= multvec
;
777 # include <dos/dos.h>
778 # include <dos/rdargs.h>
779 # include <utility/tagitem.h>
781 # include <proto/dos.h>
783 char cmlargs
[] = "TEST/A";
786 "This is exthelp for test\n"
792 LONG cmlvec
[CML_END
];
795 main(int argc
, char **argv
)
797 struct RDArgs
*rdargs
;
799 if ((rdargs
= AllocDosObject(DOS_RDARGS
, NULL
)))
801 rdargs
->RDA_ExtHelp
= usage
; /* FIX: why doesn't this work? */
803 if (!(ReadArgs(cmlargs
, cmlvec
, rdargs
)))
805 PrintFault(IoErr(), "AROS boot");
806 FreeDosObject(DOS_RDARGS
, rdargs
);
812 PrintFault(ERROR_NO_FREE_STORE
, "AROS boot");
817 FreeDosObject(DOS_RDARGS
, rdargs
);