3 Copyright © 1995-2011, The AROS Development Team. All rights reserved.
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"
22 # include <proto/dos.h>
25 # define AROS_LH3(t,fn,a1,a2,a3,bt,bn,o,lib) t fn (a1,a2,a3)
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
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. The number is always of type
75 /A Argument is required. If it is left out ReadArgs()
77 /K The keyword must be given when filling the option.
78 Normally it's skipped.
79 /M Multiple strings or, when used in combination with /N,
80 numbers. The result is returned as an array of pointers
81 to strings or LONGs, and is terminated with NULL. /M
82 eats all strings that don't fit into any other option.
83 If there are unfilled /A arguments after parsing they
84 steal strings from /M. This makes it possible to, for
85 example, write a Copy command template like
86 'FROM/A/M,TO/A'. There may be only one /M option in a
88 /F Eats the rest of the line even if there are option
90 array - Array to be filled with the result values. The array must
91 be intialized to the default values before calling
93 rdargs - An optional RDArgs structure determinating the type of
97 A handle for the memory allocated by ReadArgs(). Must be freed
98 with FreeArgs() later.
103 *****************************************************************************/
107 /* Allocated resources */
108 struct DAList
*dalist
= NULL
;
110 STRPTR strbuf
= NULL
, iline
= NULL
;
111 STRPTR
*multvec
= NULL
, *argbuf
= NULL
;
113 ULONG multnum
= 0, multmax
= 0;
118 STRPTR s1
, s2
, *newmult
;
119 ULONG arg
, numargs
, nextarg
;
120 LONG it
, item
, chars
;
121 struct CSource lcs
, *cs
;
122 TEXT argbuff
[256]; /* Maximum BCPL string length + ASCIIZ */
124 ASSERT_VALID_PTR(template);
125 ASSERT_VALID_PTR(array
);
126 ASSERT_VALID_PTR_OR_NULL(rdargs
);
128 /* Get pointer to process structure. */
129 struct Process
*me
= (struct Process
*) FindTask(NULL
);
131 /* Error recovery. C has no exceptions. This is a simple replacement. */
135 #define ERROR(a) { error=a; goto end; }
137 /* Template options */
138 #define REQUIRED 0x80 /* /A */
139 #define KEYWORD 0x40 /* /K */
140 #define MULTIPLE 0x20 /* /M */
141 #define TYPEMASK 0x07
142 #define NORMAL 0x00 /* No option */
143 #define SWITCH 0x01 /* /S, implies /K */
144 #define TOGGLE 0x02 /* /T, implies /K */
145 #define NUMERIC 0x03 /* /N */
146 #define REST 0x04 /* /F */
148 /* Flags for each possible character. */
149 static const UBYTE argflags
[] =
151 REQUIRED
, 0, 0, 0, 0, REST
, 0, 0, 0, 0, KEYWORD
, 0, MULTIPLE
,
152 NUMERIC
, 0, 0, 0, 0, SWITCH
| KEYWORD
, TOGGLE
| KEYWORD
, 0, 0,
156 /* Allocate readargs structure (and private internal one) */
159 rdargs
= (struct RDArgs
*) AllocVec(sizeof(struct RDArgs
),
160 MEMF_ANY
| MEMF_CLEAR
);
163 rdargs
->RDA_Flags
|= RDAF_ALLOCATED_BY_READARGS
;
167 dalist
= (struct DAList
*) AllocVec(sizeof(struct DAList
),
168 MEMF_ANY
| MEMF_CLEAR
);
170 if (rdargs
== NULL
|| dalist
== NULL
)
172 ERROR(ERROR_NO_FREE_STORE
);
175 /* Init character source. */
176 if (rdargs
->RDA_Source
.CS_Buffer
)
178 cs
= &rdargs
->RDA_Source
;
182 BOOL notempty
= TRUE
;
183 BPTR input
= Input();
185 D(bug("[ReadArgs] Input: 0x%p\n", input
));
187 * Take arguments from input stream. They were injected there by either
188 * runcommand.c or createnewproc.c (see vbuf_inject() routine).
189 * This is described in Guru Book.
192 lcs
.CS_Buffer
= &argbuff
[0];
195 * Special kludge for interactive filehandles (i. e. CLI windows).
196 * Read data only if filehandle's buffer is not empty. Otherwise
197 * read will cause opening CLI window and waiting for user's input.
198 * As a consequence we still can use ReadArgs() on input redirected
199 * from a file, even if we are started from Workbench (hypothetical
201 * This prevents opening a CLI window if the program was started from
202 * Workbench and redirected its Input() and Output() to own window,
203 * but still called ReadArgs() after redirection for some reason.
204 * Streams redirection is widely used in AROS startup code.
206 if (IsInteractive(input
))
208 struct FileHandle
*fh
= BADDR(input
);
210 notempty
= (fh
->fh_Pos
!= fh
->fh_End
);
214 FGets(input
, lcs
.CS_Buffer
, sizeof(argbuff
));
216 D(bug("[ReadArgs] Line: %s\n", argbuff
));
220 for (; *cs1
!= '\0'; ++cs1
);
222 lcs
.CS_Length
= cs1
- lcs
.CS_Buffer
;
228 /* Check for optional reprompting */
229 if (!(rdargs
->RDA_Flags
& RDAF_NOPROMPT
))
231 /* Check commandline for a single '?' */
234 /* Skip leading whitespace */
235 while (*cs1
== ' ' || *cs1
== '\t')
243 /* Skip whitespace */
244 while (*cs1
== ' ' || *cs1
== '\t')
250 if (*cs1
== '\n' || !*cs1
)
252 /* Only a single '?' on the commandline. */
253 BPTR input
= Input();
254 BPTR output
= Output();
255 ULONG isize
= 0, ibuf
= 0;
257 ULONG helpdisplayed
= FALSE
;
259 /* Prompt for more input */
261 D(bug("[ReadArgs] Only ? found\n"));
262 D(bug("[ReadArgs] rdargs=0x%p\n", rdargs
));
263 D(if (rdargs
) bug ("[ReadArds] rdargs->RDA_ExtHelp=0x%p\n", rdargs
->RDA_ExtHelp
);)
265 if (FPuts(output
, template) || FPuts(output
, ": "))
267 ERROR(me
->pr_Result2
);
272 ERROR(me
->pr_Result2
);
276 /* Read a line in. */
277 for (c
= 0; c
!= '\n';)
281 iline
[isize
] = '\0'; /* end of string */
286 /* Buffer too small. Get a new one. */
291 newiline
= (STRPTR
) AllocVec(ibuf
, MEMF_ANY
);
293 if (newiline
== NULL
)
295 ERROR(ERROR_NO_FREE_STORE
);
299 CopyMemQuick(iline
, newiline
, isize
);
309 /* Check and write it. */
310 if (c
== EOF
&& me
->pr_Result2
)
312 ERROR(me
->pr_Result2
);
315 /* Fix short buffers to have a trailing '\n' */
316 if (c
== EOF
|| c
== '\0')
321 iline
[isize
] = '\0'; /* end of string */
323 D(iline
[isize
] = 0; bug("[ReadArgs] Size %d, line: '%s'\n", isize
, iline
));
325 /* if user entered single ? again or some string ending
326 with space and ? either display template again or
327 extended help if it's available */
328 if ((isize
== 1 || (isize
> 1 && iline
[isize
-2] == ' '))
329 && iline
[isize
-1] == '?' )
331 helpdisplayed
= TRUE
;
333 if(rdargs
->RDA_ExtHelp
!= NULL
)
335 if (FPuts(output
, rdargs
->RDA_ExtHelp
) || FPuts(output
, ": "))
336 ERROR(me
->pr_Result2
);
338 else if (FPuts(output
, template) || FPuts(output
, ": "))
340 ERROR(me
->pr_Result2
);
345 ERROR(me
->pr_Result2
);
349 helpdisplayed
= FALSE
;
351 while(helpdisplayed
);
353 /* Prepare input source for new line. */
354 cs
->CS_Buffer
= iline
;
355 cs
->CS_Length
= isize
;
361 * Get enough space for string buffer.
362 * It's always smaller than the size of the input line+1.
365 strbuflen
= cs
->CS_Length
+ 1;
366 strbuf
= (STRPTR
) AllocVec(strbuflen
, MEMF_ANY
);
370 ERROR(ERROR_NO_FREE_STORE
);
373 /* Count the number of items in the template (number of ','+1). */
385 /* Use this count to get space for temporary flag array and result
387 flags
= (UBYTE
*) AllocVec(numargs
+ 1, MEMF_CLEAR
);
389 argbuf
= (STRPTR
*) AllocVec((numargs
+ 1) * sizeof(STRPTR
), MEMF_CLEAR
);
391 if (flags
== NULL
|| argbuf
== NULL
)
393 ERROR(ERROR_NO_FREE_STORE
);
396 /* Fill the flag array. */
402 /* A ',' means: goto next item. */
408 /* In case of a '/' use the next character as option. */
411 UBYTE argc
= ToUpper(*cs1
);
412 if (argc
>= 'A' && argc
<= 'Z')
413 *s2
|= argflags
[argc
- 'A'];
417 /* Add a dummy so that the whole line is processed. */
421 * Now process commandline for the first time:
422 * Go from left to right and fill all items that need filling.
423 * If an item is given as 'OPTION=VALUE' or 'OPTION VALUE' fill
428 for (arg
= 0; arg
<= numargs
; arg
= nextarg
)
432 D(bug("[ReadArgs] s1=&strbuf[%d], %d left\n", s1
-strbuf
, strbuflen
));
434 /* Skip /K options and options that are already done. */
435 if (flags
[arg
] & KEYWORD
|| argbuf
[arg
] != NULL
)
440 #if 0 /* stegerg: if so a template of CLOSE/S,QUICK/S,COMMAND/F would
441 not work correctly if command line for example is
442 "CLOSE QUICK" it would all end up being eaten by COMMAND/F
445 /* If the current option is of type /F do not look for keywords */
446 if ((flags
[arg
] & TYPEMASK
) != REST
)
450 /* Get item. Quoted items are never keywords. */
451 it
= ReadItem(s1
, strbuflen
, cs
);
453 if (it
== ITEM_UNQUOTED
)
455 /* Not quoted. Check if it's a keyword. */
456 item
= FindArg(template, s1
);
458 if (item
>= 0 && item
< numargs
&& argbuf
[item
] == NULL
)
461 * It's a keyword. Fill it and retry the current option
467 /* /S /T may not be given as 'OPTION=VALUE'. */
468 if ((flags
[item
] & TYPEMASK
) != SWITCH
469 && (flags
[item
] & TYPEMASK
) != TOGGLE
)
472 it
= ReadItem(s1
, strbuflen
, cs
);
474 if (it
== ITEM_EQUAL
)
476 it
= ReadItem(s1
, strbuflen
, cs
);
482 /* Check returncode of ReadItem(). */
483 if (it
== ITEM_EQUAL
)
485 ERROR(ERROR_BAD_TEMPLATE
);
487 else if (it
== ITEM_ERROR
)
489 ERROR(me
->pr_Result2
);
491 else if (it
== ITEM_NOTHING
)
497 /* /F takes all the rest */
498 if ((flags
[arg
] & TYPEMASK
) == REST
)
503 /* Skip part already read above by ReadItem() */
513 it
= ReadItem(s1
, strbuflen
, cs
);
514 } while (it
== ITEM_QUOTED
|| it
== ITEM_UNQUOTED
);
521 if (flags
[arg
] & MULTIPLE
)
523 /* All /M arguments are stored in a buffer. */
524 if (multnum
>= multmax
)
526 /* Buffer too small. Get a new one. */
529 newmult
= (STRPTR
*) AllocVec(multmax
* sizeof(char *),
533 ERROR(ERROR_NO_FREE_STORE
);
536 CopyMemQuick((ULONG
*) multvec
, (ULONG
*) newmult
,
537 multnum
* sizeof(char *));
544 /* Put string into the buffer. */
545 multvec
[multnum
++] = s1
;
549 /* Account for the \000 at the end. */
552 /* /M takes more than one argument, so retry. */
555 else if ((flags
[arg
] & TYPEMASK
) == SWITCH
556 || (flags
[arg
] & TYPEMASK
) == TOGGLE
)
558 /* /S or /T just set a flag */
559 argbuf
[arg
] = (char *) ~0;
561 else /* NORMAL || NUMERIC */
563 /* Put argument into argument buffer. */
568 /* Account for the \000 at the end. */
572 if (cs
->CS_CurChr
>= cs
->CS_Length
)
573 break; /* end of input */
576 /* Unfilled /A options steal Arguments from /M */
577 for (arg
= numargs
; arg
-- > 0;)
579 if (flags
[arg
] & REQUIRED
&& argbuf
[arg
] == NULL
580 && !(flags
[arg
] & MULTIPLE
))
582 if (flags
[arg
] & KEYWORD
)
584 /* /K/A argument, which insists on keyword
585 * being used, cannot be satisfied */
587 ERROR(ERROR_TOO_MANY_ARGS
); /* yes, strange error number,
588 * but it translates to "wrong
589 * number of arguments" */
595 /* No arguments left? Oh dear! */
596 ERROR(ERROR_REQUIRED_ARG_MISSING
);
599 argbuf
[arg
] = multvec
[--multnum
];
603 /* Put the rest of /M where it belongs */
604 for (arg
= 0; arg
< numargs
; arg
++)
606 if (flags
[arg
] & MULTIPLE
)
608 if (flags
[arg
] & REQUIRED
&& multnum
== 0)
610 ERROR(ERROR_REQUIRED_ARG_MISSING
);
615 /* NULL terminate it. */
616 if (multnum
>= multmax
)
620 newmult
= (STRPTR
*) AllocVec(multmax
* sizeof(STRPTR
),
625 ERROR(ERROR_NO_FREE_STORE
);
628 CopyMemQuick((ULONG
*) multvec
, (ULONG
*) newmult
,
629 multnum
* sizeof(char *));
636 multvec
[multnum
++] = NULL
;
637 argbuf
[arg
] = (STRPTR
) multvec
;
641 /* Shouldn't be necessary, but some buggy software relies on this */
649 /* There are some arguments left? Return error. */
650 if (multnum
!= 0 && arg
== numargs
)
652 ERROR(ERROR_TOO_MANY_ARGS
);
656 * The commandline is processed now. Put the results in the result array.
657 * Convert /N arguments by the way.
659 for (arg
= 0; arg
< numargs
; arg
++)
661 /* Just for the arguments given. */
662 if (argbuf
[arg
] != NULL
)
664 if (flags
[arg
] & MULTIPLE
)
666 array
[arg
] = (IPTR
) argbuf
[arg
];
668 if ((flags
[arg
] & TYPEMASK
) == NUMERIC
)
673 if (multnum
* 2 > multmax
)
675 multmax
= multnum
* 2;
676 newmult
= (STRPTR
*) AllocVec(multmax
* sizeof(STRPTR
),
681 ERROR(ERROR_NO_FREE_STORE
);
684 CopyMemQuick((ULONG
*) multvec
, (ULONG
*) newmult
,
685 multnum
* sizeof(char *));
692 array
[arg
] = (IPTR
) multvec
;
694 q
= (LONG
*) (multvec
+ multnum
);
698 /* Convert /N argument. */
699 chars
= StrToLong(*p
, q
);
701 if (chars
<= 0 || (*p
)[chars
])
703 /* Conversion failed. */
704 ERROR(ERROR_BAD_NUMBER
);
707 /* Put the result where it belongs. */
710 q
+= sizeof(IPTR
) / sizeof(LONG
);
716 switch (flags
[arg
] & TYPEMASK
)
721 /* Simple arguments are just copied. */
722 array
[arg
] = (IPTR
) argbuf
[arg
];
726 /* /T logically inverts the argument. */
727 array
[arg
] = array
[arg
] ? 0 : ~0;
731 /* Convert /N argument. */
732 /* Abuse the argbuf buffer. It's not needed anymore. */
733 numstr
= (CONST_STRPTR
)argbuf
[arg
];
734 chars
= StrToLong(numstr
, (LONG
*)&argbuf
[arg
]);
736 if (chars
<= 0 || numstr
[chars
] != '\0')
738 /* Conversion failed. */
739 ERROR(ERROR_BAD_NUMBER
);
742 /* Put the result where it belongs. */
743 array
[arg
] = (IPTR
) &argbuf
[arg
];
750 if (flags
[arg
] & MULTIPLE
)
752 /* Shouldn't be necessary, but some buggy software relies on this.
753 * IBrowse's URL field isn't set to zero.
755 array
[arg
] = (IPTR
)NULL
;
763 /* Cleanup and return. */
769 /* ReadArgs() failed. Clean everything up. */
772 if (rdargs
->RDA_Flags
& RDAF_ALLOCATED_BY_READARGS
)
783 me
->pr_Result2
= error
;
789 /* All went well. Prepare result and return. */
790 rdargs
->RDA_DAList
= (IPTR
) dalist
;
791 dalist
->ArgBuf
= argbuf
;
792 dalist
->StrBuf
= strbuf
;
793 dalist
->MultVec
= multvec
;
800 # include <dos/dos.h>
801 # include <dos/rdargs.h>
802 # include <utility/tagitem.h>
804 # include <proto/dos.h>
806 char cmlargs
[] = "TEST/A";
809 "This is exthelp for test\n"
815 LONG cmlvec
[CML_END
];
818 main(int argc
, char **argv
)
820 struct RDArgs
*rdargs
;
822 if ((rdargs
= AllocDosObject(DOS_RDARGS
, NULL
)))
824 rdargs
->RDA_ExtHelp
= usage
; /* FIX: why doesn't this work? */
826 if (!(ReadArgs(cmlargs
, cmlvec
, rdargs
)))
828 PrintFault(IoErr(), "AROS boot");
829 FreeDosObject(DOS_RDARGS
, rdargs
);
835 PrintFault(ERROR_NO_FREE_STORE
, "AROS boot");
840 FreeDosObject(DOS_RDARGS
, rdargs
);