From: Erik M. <er...@us...> - 2001-12-17 00:07:39
|
Update of /cvsroot/blob/blob/doc In directory usw-pr-cvs1:/tmp/cvs-serv18358 Modified Files: Makefile.am Added Files: commandlist.txt Log Message: Document the command list. --- NEW FILE: commandlist.txt --- (emacs users can use -*- outline -*- mode for this file) The blob commandlist or How to add new commands to blob Copyright (C) 2001 Erik Mouw <J.A...@it...> $Id: commandlist.txt,v 1.1 2001/12/17 00:07:37 erikm Exp $ * Introduction ============== All interactive programs have to deal with the problem of parsing the command line and calling the correct function to execute the command. Most programs use a simple if-then-else ladder for command parsing, which works well for a small and/or fixed number of commands. When the number of commands becomes either large or isn't fixed the if-then-else ladder solution becomes either too long, or becomes cluttered up with ugly #ifdef/#endif statements to selectively compile the wanted commands. Another problem is that the actual implementation of the command is not in the same compilation unit as the command line parser, leaving room for discrepancies between the caller and the actual command. To overcome those difficulties, a modular command parser was introduced in blob-2.0.5. This file documents that command parser. Note that although this document talks about "blob", the same command parser is also used in the "diag" diagnostics utility. * Advantages ============ The modular command parser has the following advantages: - Modular (doh!) - Supports command abbreviation - Simple to use - Easy to extend the number of commands without changing the command parser itself - Integrated help functionality * How it works ============== ** The commandlist ------------------ The key concept in the command parser is the "commandlist", which is defined in src/lib/command.c as commandlist_t *commands. Early in the initialisation of blob this pointer is initialised with the contents of a special ELF section and transformed into a linked list of commands. The actual parser uses this linked list to compare the command on the command line with each of the individual commandlist entries. ** Creating command list entries -------------------------------- Command list entries are created with the __commandlist() macro as defined in include/blob/command.h: #define __commandlist(fn, nm, hlp) \ static commandlist_t __command_##fn __command = { \ magic: COMMAND_MAGIC, \ name: nm, \ help: hlp, \ callback: fn } In other words: it fills out a commandlist_t entry with the callback function, ASCII name, and help text. The __command attribute is again a macro: #define __command __attribute__((unused, __section__(".commandlist"))) This tells the compiler to put the commandlist_t entry in the .commandlist ELF section instead of the default .data section. The "unused" part tells the compiler not to discard the entry although it is unused in this compilation unit (remember the entry is declared as static to avoid name space clashes with other compilation units). ** Fun with linker magic ------------------------ Without a proper linker script the linker would happily discard all ELF sections it doesn't know about, so the carefully crafted command list entries would be lost. To avoid this, blob uses a linker script which (among other things) describes how to deal with the command list entries: . = ALIGN(4); .commandlist : { __commandlist_start = .; *(.commandlist) __commandlist_end = .; } This tells the linker to start an output section called .commandlist at a 4 byte aligned address, defines the __commandlist_start symbol with that address, groups all .commandlist input sections (containing all commandlist_t entries) into the output section, and defines the __commandlist_end symbol with the current address. In this way we have created a list of commands nicely bounded by __commandlist_start and __commandlist_end. During the initialisation of blob those two addresses are used to find the correct number of commands. The advantage is clear: the command parser has been made independent on the number of commands. ** Parsing and executing ------------------------ After all this linker magic, parsing the command list has been made very easy: take the command from the command line, and compare it with commandlist_t->name of all the commands in the command list. If the name matches, we have found our command and can start executing commandlist_t->callback(). * Usage ======= Using this all is very simple. First of all, all commands should be declared as static functions (to avoid name space pollution) and have this format: static int command(int argc, char *argv[]); This looks pretty much like the main() function in normal C programs, and the argc and argv parameters have exactly the same meaning: argc contains the number of arguments to the function, argv is an array with pointers to each argument. The first pointer in argv points to the name of the called command. The command should return 0 in case of success, or a negative error number otherwise (see include/blob/errno.h for error number definitions). To get the function registered into the command list, simply write a line with a brief help text (again static) about the command, and use the __commandlist() macro to register it: static char commandhelp[] = "help on command\n"; __commandlist(command, "command", commandhelp); That's all there is. It's good practice to both the help text and the __commandlist() definition just below the function that implements the command, so there is less chance for them to get discrepancies. * Example ========= Here is a longer example: static int example_command(int argc, char *argv[]) { int i; for(i = 0; i < argc; i++) { SerialOutputString("argv["); SerialOutputDec(i); SerialOutputString("] = \""); SerialOutputString(argv[i]); SerialOutputString("\"\n"); } return 0; } static char examplehelp[] = "example command\n"; __commandlist(example_command, "example", examplehelp); Index: Makefile.am =================================================================== RCS file: /cvsroot/blob/blob/doc/Makefile.am,v retrieving revision 1.1 retrieving revision 1.2 diff -u -d -r1.1 -r1.2 --- Makefile.am 2001/12/13 18:58:31 1.1 +++ Makefile.am 2001/12/17 00:07:37 1.2 @@ -6,4 +6,5 @@ CLEANFILES = *~ EXTRA_DIST = \ - porting.txt + porting.txt \ + commandlist.txt |