]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/redboot/v2_0/src/parse.c
unified MX27, MX25, MX37 trees
[karo-tx-redboot.git] / packages / redboot / v2_0 / src / parse.c
1 //==========================================================================
2 //
3 //      parse.c
4 //
5 //      RedBoot command line parsing routine
6 //
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
12 // Copyright (C) 2004, 2005, 2006 eCosCentric Limited
13 //
14 // eCos is free software; you can redistribute it and/or modify it under
15 // the terms of the GNU General Public License as published by the Free
16 // Software Foundation; either version 2 or (at your option) any later version.
17 //
18 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
19 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
20 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
21 // for more details.
22 //
23 // You should have received a copy of the GNU General Public License along
24 // with eCos; if not, write to the Free Software Foundation, Inc.,
25 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
26 //
27 // As a special exception, if other files instantiate templates or use macros
28 // or inline functions from this file, or you compile this file and link it
29 // with other works to produce a work based on this file, this file does not
30 // by itself cause the resulting work to be covered by the GNU General Public
31 // License. However the source code for this file must still be made available
32 // in accordance with section (3) of the GNU General Public License.
33 //
34 // This exception does not invalidate any other reasons why a work based on
35 // this file might be covered by the GNU General Public License.
36 // -------------------------------------------
37 //####ECOSGPLCOPYRIGHTEND####
38 //==========================================================================
39 //#####DESCRIPTIONBEGIN####
40 //
41 // Author(s):    gthomas
42 // Contributors: gthomas, eCosCentric
43 // Date:         2000-07-14
44 // Purpose:      
45 // Description:  
46 //              
47 // This code is part of RedBoot (tm).
48 //
49 //####DESCRIPTIONEND####
50 //
51 //==========================================================================
52
53 #include <redboot.h>
54 #include <cyg/hal/hal_arch.h>
55 #include <cyg/hal/hal_intr.h>
56 #include <cyg/hal/hal_cache.h>
57 #include CYGHWR_MEMORY_LAYOUT_H
58 #include <cyg/hal/hal_tables.h>
59
60 // Define table boundaries
61 extern struct cmd __RedBoot_CMD_TAB__[], __RedBoot_CMD_TAB_END__;
62
63 //
64 // Scan through an input line and break it into "arguments".  These
65 // are space delimited strings.  Return a structure which points to
66 // the strings, similar to a Unix program. Multiple commands in the line
67 // are separated by ; similar to sh. If we find a semi we stop processing the 
68 // line, terminate the current command with a null and return the start 
69 // of the next command in *line. parse() can then be called again to 
70 // process the next command on the line.
71 // Note: original input is destroyed by replacing the delimiters with 
72 // null ('\0') characters for ease of use.
73 //
74 struct cmd *
75 parse(char **line, int *argc, char **argv)
76 {
77     char *cp = *line;
78     char *pp;
79     int indx = 0;
80     int semi = 0;
81
82     while (*cp) {
83         // Skip leading spaces
84         while (*cp && *cp == ' ') cp++;
85         if (!*cp) {
86             break;  // Line ended with a string of spaces
87         }
88         if (*cp == ';') {
89             *cp = '\0';
90             semi=1;
91             break;
92         }
93         if (indx < MAX_ARGV) {
94             argv[indx++] = cp;
95         } else {
96             diag_printf("Too many arguments - stopped at: '%s'\n", cp);
97         }
98         while (*cp) {
99             if (*cp == ' ') {
100                 *cp++ = '\0';
101                 break;
102             } else if (*cp == ';') {
103                 break;
104             } else if (*cp == '"') {
105                 // Swallow quote, scan till following one
106                 if (argv[indx-1] == cp) {
107                     argv[indx-1] = ++cp;
108                 }
109                 pp = cp;
110                 while (*cp && *cp != '"') {
111                     if (*cp == '\\') {
112                         // Skip over escape - allows for escaped '"'
113                         cp++;
114                     }
115                     // Move string to swallow escapes
116                     *pp++ = *cp++;
117                 }
118                 if (!*cp) {
119                     diag_printf("Unbalanced string!\n");
120                 } else {
121                     if (pp != cp) *pp = '\0';
122                     *cp++ = '\0';
123                     break;
124                 }
125             } else {
126                 cp++;
127             }
128         }
129     }
130     if (semi) {
131         *line = cp + 1;
132     } else {
133         *line = cp;
134     }
135     *argc = indx;
136     return cmd_search(__RedBoot_CMD_TAB__, &__RedBoot_CMD_TAB_END__, argv[0]);
137 }
138
139 //
140 // Search through a list of commands
141 //
142 struct cmd *
143 cmd_search(struct cmd *tab, struct cmd *tabend, char *arg)
144 {
145     int cmd_len;
146     struct cmd *cmd, *cmd2;
147     // Search command table
148     cmd_len = strlen(arg);
149     cmd = tab;
150     while (cmd != tabend) {
151         if (strncasecmp(arg, cmd->str, cmd_len) == 0) {
152             if (strlen(cmd->str) > cmd_len) {
153                 // Check for ambiguous commands here
154                 // Note: If there are commands which are not length-unique
155                 // then this check will be invalid.  E.g. "du" and "dump"
156                 bool first = true;
157                 cmd2 = tab;
158                 while (cmd2 != tabend) {
159                     if ((cmd != cmd2) && 
160                         (strncasecmp(arg, cmd2->str, cmd_len) == 0)) {
161                         if (first) {
162                             diag_printf("Ambiguous command '%s', choices are: %s", 
163                                         arg, cmd->str);
164                             first = false;
165                         }
166                         diag_printf(" %s", cmd2->str);
167                     }
168                     cmd2++;
169                 }
170                 if (!first) {
171                     // At least one ambiguity found - fail the lookup
172                     diag_printf("\n");
173                     return (struct cmd *)0;
174                 }
175             }
176             return cmd;
177         }
178         cmd++;
179     }
180     return (struct cmd *)0;
181 }
182
183 void
184 cmd_usage(struct cmd *tab, struct cmd *tabend, char *prefix)
185 {
186     struct cmd *cmd;
187
188     diag_printf("Usage:\n"); 
189     for (cmd = tab;  cmd != tabend;  cmd++) {
190         diag_printf("  %s%s %s\n", prefix, cmd->str, cmd->usage);
191     }
192 }
193
194 //
195 // Handle illegal memory accesses (and other abort conditions)
196 //
197 static hal_jmp_buf error_jmpbuf;
198 static cyg_bool redboot_exec_call = false;
199 #ifdef CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS
200 __externC void* volatile __mem_fault_handler;
201
202 static void error_handler(void)
203 {
204     hal_longjmp(error_jmpbuf, 1);
205 }
206 #endif
207
208 // Routine to allow code to invoke RedBoot commands. This is useful
209 // during initialization and in platform specific code.
210 //
211 // Call it like this:
212 //
213 // result = redboot_exec( "load", "-m", "file", "foo", 0 );
214 //
215 // Note the terminating zero. The result will be zero if the command
216 // succeeded, and <0 if something went wrong.
217
218 #define ARGV_MAX        20
219 int redboot_exec( char *command, ... )
220 {
221     int argc;
222     char *argv[ARGV_MAX+1];
223     va_list ap;
224     struct cmd *cmd;
225     int result = 0;
226     
227     va_start(ap, command);
228
229     argv[0] = command;
230     for( argc = 1; argc < ARGV_MAX; argc++ )
231     {
232         char *arg = va_arg(ap, char *);
233         if( arg == 0 )
234             break;
235         argv[argc] = arg;
236     }
237     argv[argc] = NULL;
238
239     if(( cmd = cmd_search(__RedBoot_CMD_TAB__, &__RedBoot_CMD_TAB_END__, command) ))
240     {
241         // Try to handle aborts - messy because of the stack unwinding...
242 #ifdef CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS
243         __mem_fault_handler = error_handler;
244 #endif
245         redboot_exec_call = true;
246         if (hal_setjmp(error_jmpbuf))
247             result = -1;
248         else
249             (cmd->fun)(argc, argv);
250
251         redboot_exec_call = false;
252 #ifdef CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS
253         __mem_fault_handler = 0;
254 #endif
255     }
256     else
257         result = -1;
258     
259     va_end(ap);
260
261     return result;
262 }
263
264 externC void err_printf(const char *fmt, ... )
265 {
266     va_list ap;
267     
268     va_start(ap, fmt);
269
270     diag_vprintf( fmt, ap );
271
272     va_end(ap);
273
274     // If we are not in redboot_exec() just return as usual. If we are
275     // inside a call to redboot_exec(), longjump out to terminate the command.
276     
277     if( redboot_exec_call )
278     {
279         diag_printf("err_printf: aborting command\n");
280         hal_longjmp(error_jmpbuf, 1);
281     }
282 }
283
284
285 // Option processing
286
287 // Initialize option table entry (required because these entries
288 // may have dynamic contents, thus cannot be statically initialized)
289 //
290 void
291 init_opts(struct option_info *opts, char flag, bool takes_arg, 
292           int arg_type, void *arg, bool *arg_set, char *name)
293 {
294     opts->flag = flag;
295     opts->takes_arg = takes_arg;
296     opts->arg_type = arg_type,
297     opts->arg = arg;
298     opts->arg_set = arg_set;
299     opts->name = name;
300 }
301
302 //
303 // Scan command line arguments (argc/argv), processing options, etc.
304 // returns:
305 //   0        on error
306 //   argc+1   if no default argument was specified,
307 //   (index of first non-option argument)+1 otherwise 
308 //
309 int
310 scan_opts(int argc, char *argv[], int first, 
311           struct option_info *opts, int num_opts, 
312           void *def_arg, int def_arg_type, char *def_descr)
313 {
314     int ok = true;
315     int ret = argc + 1;
316     bool flag_ok;
317     bool def_arg_set = false;
318     int i, j;
319     char c, *s;
320     struct option_info *opt;
321
322     if (def_arg && (def_arg_type == OPTION_ARG_TYPE_STR)) {
323         *(char **)def_arg = NULL;
324     }
325     opt = opts;
326     for (j = 0;  j < num_opts;  j++, opt++) {
327         if (opt->arg_set) {
328             *opt->arg_set = false;
329         }
330         if (!opt->takes_arg) {
331             switch (opt->arg_type) {
332             case OPTION_ARG_TYPE_NUM:
333                 *(int *)opt->arg = 0;
334                 break;
335             case OPTION_ARG_TYPE_FLG:
336                 *(bool *)opt->arg = false;
337                 break;
338             }
339         }
340     }
341     for (i = first;  i < argc;  i++) {
342         if (argv[i][0] == '-') {
343             c = argv[i][1];
344             flag_ok = false;
345             opt = opts;
346             for (j = 0;  j < num_opts;  j++, opt++) {
347                 if (c == opt->flag) {
348                     if (opt->arg_set && *opt->arg_set) {
349                             diag_printf("** Error: option '-%c' already specified\n", opt->flag);
350                         ok = false;
351                     }
352                     if (opt->takes_arg) {
353                         if (argv[i][2] == '=') {
354                             s = &argv[i][3];
355                         } else {
356                             i++;
357                             if (i < argc) {
358                                 s = argv[i];
359                             } else {
360                                 diag_printf("** Error: option '%c' [%s] requires an argument\n",
361                                             c, opt->name);
362                                 ok = false;
363                                 flag_ok = true;
364                                 break;
365                             }
366                         }
367                         switch (opt->arg_type) {
368                         case OPTION_ARG_TYPE_NUM:
369                             if (!parse_num(s, (unsigned long *)opt->arg, 0, 0)) {
370                                 diag_printf("** Error: invalid number '%s' for %s\n", 
371                                             s, opt->name);
372                                 ok = false;
373                             }
374                             break;
375                         case OPTION_ARG_TYPE_STR:
376                             *(char **)opt->arg = s;
377                             break;
378                         }
379                         *opt->arg_set = true;
380                     } else {
381                         switch (opt->arg_type) {
382                         case OPTION_ARG_TYPE_NUM:
383                             *(int *)opt->arg = *(int *)opt->arg + 1;
384                             break;
385                         case OPTION_ARG_TYPE_FLG:
386                             *(bool *)opt->arg = true;
387                             break;
388                         }
389                     }
390                     flag_ok = true;
391                     break;
392                 }
393             }
394             if (!flag_ok) {
395                 diag_printf("** Error: invalid flag '%c'\n", c);
396                 ok = false;
397             }
398         } else {
399             if (def_arg) {
400                 ret = i + 1;
401                 if (def_arg_set) {
402                     diag_printf("** Error: %s already specified\n", def_descr);
403                     ok = false;
404                 }
405                 switch (def_arg_type) {
406                 case OPTION_ARG_TYPE_NUM:
407                     if (!parse_num(argv[i], (unsigned long *)def_arg, 0, 0)) {
408                         diag_printf("** Error: invalid number '%s' for %s\n", 
409                                     argv[i], def_descr);
410                         ok = false;
411                     }
412                     break;
413                 case OPTION_ARG_TYPE_STR:
414                     *(char **)def_arg = argv[i];
415                     break;
416                 }
417                 def_arg_set = true;
418             } else {
419                 diag_printf("** Error: no default/non-flag arguments supported\n");
420                 ok = false;
421             }
422         }
423     }
424     return ok ? ret : 0;
425 }
426
427 //
428 // Parse (scan) a number
429 //
430 bool
431 parse_num(char *s, unsigned long *val, char **es, char *delim)
432 {
433     bool first = true;
434     int radix = 10;
435     char c;
436     unsigned long result = 0;
437     int digit;
438
439     while (*s == ' ') s++;
440     while (*s) {
441         if (first && (s[0] == '0') && (_tolower(s[1]) == 'x')) {
442             radix = 16;
443             s += 2;
444         }
445         first = false;
446         c = *s++;
447         if (_is_hex(c) && ((digit = _from_hex(c)) < radix)) {
448             // Valid digit
449 #ifdef CYGPKG_HAL_MIPS
450             // FIXME: tx49 compiler generates 0x2539018 for MUL which
451             // isn't any good.
452             if (16 == radix)
453                 result = result << 4;
454             else
455                 result = 10 * result;
456             result += digit;
457 #else
458             result = (result * radix) + digit;
459 #endif
460         } else {
461             if (delim != (char *)0) {
462                 // See if this character is one of the delimiters
463                 char *dp = delim;
464                 while (*dp && (c != *dp)) dp++;
465                 if (*dp) break;  // Found a good delimiter
466             }
467             return false;  // Malformatted number
468         }
469     }
470     *val = result;
471     if (es != (char **)0) {
472         *es = s;
473     }
474     return true;
475 }
476
477 bool
478 parse_bool(char *s, bool *val)
479 {
480     while (*s == ' ') s++;
481     if ((*s == 't') || (*s == 'T')) {
482         char *p = "rue";
483         char *P = "RUE";
484         // check for (partial) rest of the word and no extra including the
485         // terminating zero.  "tru" will match; "truef" will not.
486         while ( *++s ) {
487             if ( *p != *s && *P != *s ) return false;
488             p++; P++;
489         }
490         *val = true;
491     } else 
492     if ((*s == 'f') || (*s == 'F')) {
493         char *p = "alse";
494         char *P = "ALSE";
495         while ( *++s ) {
496             if ( *p != *s && *P != *s ) return false;
497             p++; P++;
498         }
499         *val = false;
500     } else {
501         return false;
502     }
503     return true;
504 }
505