ReadArgs, ReadItem and FindArg¶
ReadArgs() is the standard command line parser of AmigaDOS. It knows the Shell's quoting and escaping rules, matches arguments to a human-readable template, and gives every command the same built-in ? help. Because the parser lives in dos.library, a command that uses it needs no C startup code to split its arguments, which keeps small tools small. This article covers the template language, the RDArgs/CSource structures, the help protocol, resource lifetime, the lower-level ReadItem() and FindArg(), and the version-specific traps.
For the Shell side of quoting see The AmigaDOS Shell; for how the arguments reach a command see Shell Internals.
Where the command line comes from¶
When a Shell runs a command through RunCommand(), it copies the argument line into the buffer of the command's input filehandle (pr_CIS). ReadArgs(template, array, NULL) reads it from there. The line must end with a line feed (0x0a); anything after the first line feed is ignored.
A side effect: the first buffered read from Input() returns the command line, not data from the stream. To read real input after parsing, switch the handle to buffered mode and drop the buffer:
FGetC(Input()); /* make the stream buffered */
Flush(Input()); /* discard the command line parameters */
The template¶
A template is a comma-separated list of keywords. Each keyword may have an abbreviation after = and options after /:
FROM/A/M,TO/A,QUICK=Q/S,BUFFER=BUF/K/N,MODE/T
| Option | Array entry | Meaning |
|---|---|---|
| (none) | STRPTR or NULL |
Optional string, matched by keyword or by position |
/S |
LONG: 0 or non-zero |
Switch: set if the keyword appears at least once |
/T |
LONG: DOSTRUE/DOSFALSE |
Switch with an explicit value: On or Y sets it, Off or N clears it. The words are fixed and not localised. It does not toggle. |
/K |
per type | Filled only if the keyword appears on the command line |
/A |
per type | Required; missing → failure |
/N |
LONG * or NULL |
Decimal number; NULL tells you it was not given |
/M |
STRPTR * (NULL-terminated) or NULL |
Collects every argument not matched elsewhere; at most one per template |
/F |
STRPTR |
The whole rest of the line, keywords included (used by Alias) |
Note: the NDK 3.2 autodoc for ReadArgs() describes
/Tas a "toggle" that flips the value each time it appears.
Matching rules:
- On the command line a keyword is followed by its value after a space, tab or
=:FROM=organdFROM orgare the same. - Either the full keyword or its abbreviation is accepted (
QUICKorQforQUICK=Q/S). - Arguments without a keyword fill the non-
/K, non-/Sslots from left to right. - Unfilled
/Aslots are taken from the end of the/Mlist. This is howCopy FROM/A/M,TO/Atreats the last name as the destination. /Mexample: withFROM/M,TO, the linea b c TO dgivesTO="d"andFROM={"a","b","c",NULL}.
The NDK limits a template to MAX_TEMPLATE_ITEMS (100) keywords and a /M list to MAX_MULTIARGS (128) items; more /M items fail with ERROR_LINE_TOO_LONG (dos/rdargs.h).
Basic use¶
#include <exec/types.h>
#include <dos/dos.h>
#include <dos/rdargs.h>
#include <proto/dos.h>
#define TEMPLATE "FROM/A/M,TO/A,QUIET/S,BUFFER/K/N"
enum { ARG_FROM, ARG_TO, ARG_QUIET, ARG_BUFFER, ARG_COUNT };
int main(void)
{
LONG args[ARG_COUNT] = { 0 }; /* must start zeroed */
struct RDArgs *rda;
STRPTR *from;
LONG bufsize = 4096;
rda = ReadArgs(TEMPLATE, args, NULL);
if (rda == NULL) {
PrintFault(IoErr(), "MyCopy");
return RETURN_FAIL;
}
if (args[ARG_BUFFER])
bufsize = *(LONG *)args[ARG_BUFFER];
for (from = (STRPTR *)args[ARG_FROM]; *from; from++)
if (!args[ARG_QUIET])
Printf("%s -> %s (%ld)\n", *from, (STRPTR)args[ARG_TO], bufsize);
FreeArgs(rda); /* strings in args[] die here */
return RETURN_OK;
}
Everything args[] points to (strings, /M arrays, /N numbers) belongs to the RDArgs and is freed by FreeArgs(). Copy what you need to keep before freeing.
The RDArgs and CSource structures¶
Pass your own RDArgs to parse a string instead of the command line, to supply a buffer, to add extended help or to set flags. Always allocate it with AllocDosObject(DOS_RDARGS, NULL) (see DOS Objects Lifecycle).
struct CSource {
STRPTR CS_Buffer; /* text to parse */
LONG CS_Length; /* its length in characters */
LONG CS_CurChr; /* index of the next character to read */
};
struct RDArgs {
struct CSource RDA_Source; /* input source */
LONG RDA_DAList; /* PRIVATE, NULL from AllocDosObject() */
STRPTR RDA_Buffer; /* optional parsing buffer */
LONG RDA_BufSiz; /* its size in bytes */
STRPTR RDA_ExtHelp; /* optional extended help */
LONG RDA_Flags; /* RDAF_* */
};
- If
CS_Bufferis NULL, input comes from thepr_CISbuffer, exactly as with a NULLrdargs. There is no field for a filehandle source. CS_CurChris normally 0. To parse one long string in several passes, leave it where the previous call stopped.RDA_Bufferholds parsed strings,/Nnumbers and/Mpointer arrays. If it is NULL or too small,ReadArgs()allocates more, unlessRDAF_NOALLOCis set.RDA_DAListis private: never touch it.
| Flag | Value | Effect |
|---|---|---|
RDAF_STDIN 2.0+ |
1 | Defined but not used |
RDAF_NOALLOC 2.0+ |
2 | Never allocate or grow buffers; overflow fails with ERROR_NO_FREE_STORE |
RDAF_NOPROMPT 2.0+ |
4 | A lone ? is an ordinary argument; no help is shown |
Parsing a string¶
#define TEMPLATE "FILES/M,ALL/S"
LONG ParseLine(STRPTR line) /* line must end in '\n' */
{
LONG args[2] = { 0 };
LONG ok = FALSE;
struct RDArgs *rda = AllocDosObject(DOS_RDARGS, NULL);
if (rda == NULL)
return FALSE;
rda->RDA_Source.CS_Buffer = line;
rda->RDA_Source.CS_Length = strlen(line);
rda->RDA_Source.CS_CurChr = 0;
rda->RDA_Flags = RDAF_NOPROMPT;
if (ReadArgs(TEMPLATE, args, rda)) {
/* ... use args ... */
ok = TRUE;
FreeArgs(rda); /* frees the parse results only */
} else {
PrintFault(IoErr(), "parse");
}
FreeDosObject(DOS_RDARGS, rda); /* the structure is ours */
return ok;
}
The ? help protocol¶
Unless RDAF_NOPROMPT is set, a command line that is just ?:
- prints the template on standard output and reads a new line from standard input;
- if that line is again
?andRDA_ExtHelpis non-NULL, prints the extended help and reads a third time.
When the input is an extended (V47) Shell, ? input is read through the shell's SHELL_METH_FGETS, so TAB expansion of file names works at that prompt.
Note: the NDK 3.2
dos/rdargs.hcomment saysRDA_ExtHelpis "displayed instead of the template string".
Return value, errors and cleanup¶
On success ReadArgs() returns the RDArgs (yours, or one it allocated). Call FreeArgs() in both cases.
On failure it returns NULL and frees everything it allocated. A structure you passed in is not freed: dispose of it with FreeDosObject() (no FreeArgs() needed).
Error codes in IoErr() besides I/O errors:
| Code | Cause |
|---|---|
ERROR_NO_FREE_STORE 1.3+ |
Out of memory, or RDAF_NOALLOC buffer overflow |
ERROR_REQUIRED_ARG_MISSING 2.0+ |
A /A argument is missing |
ERROR_KEY_NEEDS_ARG 2.0+ |
A keyword was given without its value |
ERROR_TOO_MANY_ARGS 2.0+ |
More arguments than the template can take |
ERROR_BAD_NUMBER 2.0+ |
A /N value is not a valid number |
ERROR_BAD_TEMPLATE 2.0+ |
The template itself is malformed |
ERROR_UNMATCHED_QUOTES 2.0+ |
An opening quote has no closing quote |
ERROR_LINE_TOO_LONG 1.3+ |
Line too long, e.g. too many /M items |
The manual is not consistent about a missing /A argument: §10.2.9 assigns it ERROR_REQUIRED_ARG_MISSING, while §15.5.1 names ERROR_KEY_NEEDS_ARG. Treat both as "bad arguments".
FreeArgs¶
VOID FreeArgs(struct RDArgs *args); /* V36 */
Releases everything attached to the RDArgs, and the structure too if ReadArgs() allocated it. Afterwards the contents of array are invalid; zero it before reusing it.
From V39 FreeArgs() also clears RDA_Buffer, because it may point at memory being freed. This makes an AllocDosObject() structure safe to reuse, but a custom buffer you installed must be set again before the next ReadArgs().
Note: the NDK 3.2 autodoc for ReadArgs() says RDA_Buffer must be reset (cleared or set) before every call on a passed-in structure.
ReadItem — one argument at a time¶
LONG ReadItem(CONST_STRPTR name, LONG maxchars, struct CSource *cSource); /* V36 */
Reads one item into name (at most maxchars bytes) from cSource, or from the pr_CIS buffer if cSource is NULL.
- Items are separated by space, tab,
;or=outside quotes. Line feed and EOF end an item even inside quotes. - In a quoted item the quotes are removed and
*",*E,*Nand**are replaced. Outside quotes*is an ordinary character. - After the item, a line feed,
;or EOF is pushed back into the source; a space, tab or=is consumed. - No buffer is allocated: an overflow returns
ITEM_ERROR. ReadItem()never setsIoErr(), not even forITEM_ERROR.
| Result | Value | Meaning |
|---|---|---|
ITEM_EQUAL 2.0+ |
−2 | Ended by =: the item is probably a keyword |
ITEM_ERROR 2.0+ |
−1 | Buffer too small, or end of data/line before the closing quote |
ITEM_NOTHING 2.0+ |
0 | Nothing left: line feed, ; or EOF |
ITEM_UNQUOTED 2.0+ |
1 | Unquoted item read |
ITEM_QUOTED 2.0+ |
2 | Quoted item read |
/* Split a line into items, honouring Shell quoting. */
UBYTE item[256];
struct CSource cs;
LONG r;
cs.CS_Buffer = (STRPTR)"copy \"My File\" RAM:\n";
cs.CS_Length = strlen(cs.CS_Buffer);
cs.CS_CurChr = 0;
while ((r = ReadItem(item, sizeof(item), &cs)) > 0 || r == ITEM_EQUAL)
Printf("%s item: %s\n", r == ITEM_QUOTED ? "quoted" : "plain", item);
if (r == ITEM_ERROR)
PutStr("unterminated quote or item too long\n");
FindArg — look up a keyword¶
LONG FindArg(CONST_STRPTR arg_template, CONST_STRPTR keyword); /* V36: template in d1 */
Returns the zero-based position of keyword in the template (case-insensitive; abbreviations count), or −1. IoErr() is not changed.
FindArg("FROM/A/M,TO/A,QUIET=Q/S", "q"); /* 2 */
FindArg("FROM/A/M,TO/A,QUIET=Q/S", "ALL"); /* -1 */
Note: the NDK 3.2 SFD names the d1 parameter
keywordand the d2 parameterarg_template; the autodoc and the manual pass the template first (d1).
Interaction with the Shell and C compilers¶
- The Shell leaves
*N,*E,*"and**inside quotes untouched;ReadArgs()/ReadItem()replace them.Echo "a*Nb"prints two lines because Echo usesReadArgs(). A program that parsespr_Argumentsitself sees the raw sequences. - Parsers in some C compilers' startup code do not follow these rules exactly and give different results for quoted arguments.
=separates a keyword from its value, so a file name containing=must be quoted on the command line.ReadArgs()uses a fair amount of stack (dos/rdargs.hgives this as the reason for its limits); give commands a sensible stack.
Version notes¶
| Version | Change |
|---|---|
| V36 | /M/N returned strings; /T did not work; /K and /F interacted; a /K before a non-switch parameter forced that parameter's keyword (NDK autodoc BUGS). Fixed in V37. |
| V39 | FreeArgs() clears RDA_Buffer. |
| V47 | ? input goes through the shell's TAB expansion when available. |
See Also¶
- The AmigaDOS Shell
- Shell Internals
- Shell Variables
- DOS Objects Lifecycle
- AmigaDOS Error Codes
- dos.library Reference
Sources: Thomas Richter, Amiga ROM Kernel Reference Manual: AmigaDOS (2024, v1.27), §15.5 (pp. 341–346), §15.1.4 (pp. 315–316), §15.2.3–15.2.5 (pp. 328–330), §5.6 (p. 40), §10.2.9 (p. 155); NDK 3.2 R4 Include_H/dos/rdargs.h, dos/dos.h; Autodocs/dos.doc (ReadArgs, FindArg); SFD/dos_lib.sfd Updated: 2026-11-13