//==========================================================================
|
//==========================================================================
|
//
|
//
|
// parse.c
|
// parse.c
|
//
|
//
|
// RedBoot command line parsing routine
|
// RedBoot command line parsing routine
|
//
|
//
|
//==========================================================================
|
//==========================================================================
|
//####ECOSGPLCOPYRIGHTBEGIN####
|
//####ECOSGPLCOPYRIGHTBEGIN####
|
// -------------------------------------------
|
// -------------------------------------------
|
// This file is part of eCos, the Embedded Configurable Operating System.
|
// This file is part of eCos, the Embedded Configurable Operating System.
|
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
|
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
|
//
|
//
|
// eCos is free software; you can redistribute it and/or modify it under
|
// eCos is free software; you can redistribute it and/or modify it under
|
// the terms of the GNU General Public License as published by the Free
|
// the terms of the GNU General Public License as published by the Free
|
// Software Foundation; either version 2 or (at your option) any later version.
|
// Software Foundation; either version 2 or (at your option) any later version.
|
//
|
//
|
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
|
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
// for more details.
|
// for more details.
|
//
|
//
|
// You should have received a copy of the GNU General Public License along
|
// You should have received a copy of the GNU General Public License along
|
// with eCos; if not, write to the Free Software Foundation, Inc.,
|
// with eCos; if not, write to the Free Software Foundation, Inc.,
|
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
//
|
//
|
// As a special exception, if other files instantiate templates or use macros
|
// As a special exception, if other files instantiate templates or use macros
|
// or inline functions from this file, or you compile this file and link it
|
// or inline functions from this file, or you compile this file and link it
|
// with other works to produce a work based on this file, this file does not
|
// with other works to produce a work based on this file, this file does not
|
// by itself cause the resulting work to be covered by the GNU General Public
|
// by itself cause the resulting work to be covered by the GNU General Public
|
// License. However the source code for this file must still be made available
|
// License. However the source code for this file must still be made available
|
// in accordance with section (3) of the GNU General Public License.
|
// in accordance with section (3) of the GNU General Public License.
|
//
|
//
|
// This exception does not invalidate any other reasons why a work based on
|
// This exception does not invalidate any other reasons why a work based on
|
// this file might be covered by the GNU General Public License.
|
// this file might be covered by the GNU General Public License.
|
//
|
//
|
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
|
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
|
// at http://sources.redhat.com/ecos/ecos-license/
|
// at http://sources.redhat.com/ecos/ecos-license/
|
// -------------------------------------------
|
// -------------------------------------------
|
//####ECOSGPLCOPYRIGHTEND####
|
//####ECOSGPLCOPYRIGHTEND####
|
//==========================================================================
|
//==========================================================================
|
//#####DESCRIPTIONBEGIN####
|
//#####DESCRIPTIONBEGIN####
|
//
|
//
|
// Author(s): gthomas
|
// Author(s): gthomas
|
// Contributors: gthomas
|
// Contributors: gthomas
|
// Date: 2000-07-14
|
// Date: 2000-07-14
|
// Purpose:
|
// Purpose:
|
// Description:
|
// Description:
|
//
|
//
|
// This code is part of RedBoot (tm).
|
// This code is part of RedBoot (tm).
|
//
|
//
|
//####DESCRIPTIONEND####
|
//####DESCRIPTIONEND####
|
//
|
//
|
//==========================================================================
|
//==========================================================================
|
|
|
#include <redboot.h>
|
#include <redboot.h>
|
#include <cyg/hal/hal_arch.h>
|
#include <cyg/hal/hal_arch.h>
|
#include <cyg/hal/hal_intr.h>
|
#include <cyg/hal/hal_intr.h>
|
#include <cyg/hal/hal_cache.h>
|
#include <cyg/hal/hal_cache.h>
|
#include CYGHWR_MEMORY_LAYOUT_H
|
#include CYGHWR_MEMORY_LAYOUT_H
|
#include <cyg/hal/hal_tables.h>
|
#include <cyg/hal/hal_tables.h>
|
|
|
// Define table boundaries
|
// Define table boundaries
|
extern struct cmd __RedBoot_CMD_TAB__[], __RedBoot_CMD_TAB_END__;
|
extern struct cmd __RedBoot_CMD_TAB__[], __RedBoot_CMD_TAB_END__;
|
|
|
//
|
//
|
// Scan through an input line and break it into "arguments". These
|
// Scan through an input line and break it into "arguments". These
|
// are space delimited strings. Return a structure which points to
|
// are space delimited strings. Return a structure which points to
|
// the strings, similar to a Unix program. Multiple commands in the line
|
// the strings, similar to a Unix program. Multiple commands in the line
|
// are separated by ; similar to sh. If we find a semi we stop processing the
|
// are separated by ; similar to sh. If we find a semi we stop processing the
|
// line, terminate the current command with a null and return the start
|
// line, terminate the current command with a null and return the start
|
// of the next command in *line. parse() can then be called again to
|
// of the next command in *line. parse() can then be called again to
|
// process the next command on the line.
|
// process the next command on the line.
|
// Note: original input is destroyed by replacing the delimiters with
|
// Note: original input is destroyed by replacing the delimiters with
|
// null ('\0') characters for ease of use.
|
// null ('\0') characters for ease of use.
|
//
|
//
|
struct cmd *
|
struct cmd *
|
parse(char **line, int *argc, char **argv)
|
parse(char **line, int *argc, char **argv)
|
{
|
{
|
char *cp = *line;
|
char *cp = *line;
|
char *pp;
|
char *pp;
|
int indx = 0;
|
int indx = 0;
|
int semi = 0;
|
int semi = 0;
|
|
|
while (*cp) {
|
while (*cp) {
|
// Skip leading spaces
|
// Skip leading spaces
|
while (*cp && *cp == ' ') cp++;
|
while (*cp && *cp == ' ') cp++;
|
if (!*cp) {
|
if (!*cp) {
|
break; // Line ended with a string of spaces
|
break; // Line ended with a string of spaces
|
}
|
}
|
if (*cp == ';') {
|
if (*cp == ';') {
|
*cp = '\0';
|
*cp = '\0';
|
semi=1;
|
semi=1;
|
break;
|
break;
|
}
|
}
|
if (indx < MAX_ARGV) {
|
if (indx < MAX_ARGV) {
|
argv[indx++] = cp;
|
argv[indx++] = cp;
|
} else {
|
} else {
|
diag_printf("Too many arguments - stopped at: '%s'\n", cp);
|
diag_printf("Too many arguments - stopped at: '%s'\n", cp);
|
}
|
}
|
while (*cp) {
|
while (*cp) {
|
if (*cp == ' ') {
|
if (*cp == ' ') {
|
*cp++ = '\0';
|
*cp++ = '\0';
|
break;
|
break;
|
} else if (*cp == ';') {
|
} else if (*cp == ';') {
|
break;
|
break;
|
} else if (*cp == '"') {
|
} else if (*cp == '"') {
|
// Swallow quote, scan till following one
|
// Swallow quote, scan till following one
|
if (argv[indx-1] == cp) {
|
if (argv[indx-1] == cp) {
|
argv[indx-1] = ++cp;
|
argv[indx-1] = ++cp;
|
}
|
}
|
pp = cp;
|
pp = cp;
|
while (*cp && *cp != '"') {
|
while (*cp && *cp != '"') {
|
if (*cp == '\\') {
|
if (*cp == '\\') {
|
// Skip over escape - allows for escaped '"'
|
// Skip over escape - allows for escaped '"'
|
cp++;
|
cp++;
|
}
|
}
|
// Move string to swallow escapes
|
// Move string to swallow escapes
|
*pp++ = *cp++;
|
*pp++ = *cp++;
|
}
|
}
|
if (!*cp) {
|
if (!*cp) {
|
diag_printf("Unbalanced string!\n");
|
diag_printf("Unbalanced string!\n");
|
} else {
|
} else {
|
if (pp != cp) *pp = '\0';
|
if (pp != cp) *pp = '\0';
|
*cp++ = '\0';
|
*cp++ = '\0';
|
break;
|
break;
|
}
|
}
|
} else {
|
} else {
|
cp++;
|
cp++;
|
}
|
}
|
}
|
}
|
}
|
}
|
if (semi) {
|
if (semi) {
|
*line = cp + 1;
|
*line = cp + 1;
|
} else {
|
} else {
|
*line = cp;
|
*line = cp;
|
}
|
}
|
*argc = indx;
|
*argc = indx;
|
return cmd_search(__RedBoot_CMD_TAB__, &__RedBoot_CMD_TAB_END__, argv[0]);
|
return cmd_search(__RedBoot_CMD_TAB__, &__RedBoot_CMD_TAB_END__, argv[0]);
|
}
|
}
|
|
|
//
|
//
|
// Search through a list of commands
|
// Search through a list of commands
|
//
|
//
|
struct cmd *
|
struct cmd *
|
cmd_search(struct cmd *tab, struct cmd *tabend, char *arg)
|
cmd_search(struct cmd *tab, struct cmd *tabend, char *arg)
|
{
|
{
|
int cmd_len;
|
int cmd_len;
|
struct cmd *cmd, *cmd2;
|
struct cmd *cmd, *cmd2;
|
// Search command table
|
// Search command table
|
cmd_len = strlen(arg);
|
cmd_len = strlen(arg);
|
cmd = tab;
|
cmd = tab;
|
while (cmd != tabend) {
|
while (cmd != tabend) {
|
if (strncasecmp(arg, cmd->str, cmd_len) == 0) {
|
if (strncasecmp(arg, cmd->str, cmd_len) == 0) {
|
if (strlen(cmd->str) > cmd_len) {
|
if (strlen(cmd->str) > cmd_len) {
|
// Check for ambiguous commands here
|
// Check for ambiguous commands here
|
// Note: If there are commands which are not length-unique
|
// Note: If there are commands which are not length-unique
|
// then this check will be invalid. E.g. "du" and "dump"
|
// then this check will be invalid. E.g. "du" and "dump"
|
bool first = true;
|
bool first = true;
|
cmd2 = tab;
|
cmd2 = tab;
|
while (cmd2 != tabend) {
|
while (cmd2 != tabend) {
|
if ((cmd != cmd2) &&
|
if ((cmd != cmd2) &&
|
(strncasecmp(arg, cmd2->str, cmd_len) == 0)) {
|
(strncasecmp(arg, cmd2->str, cmd_len) == 0)) {
|
if (first) {
|
if (first) {
|
diag_printf("Ambiguous command '%s', choices are: %s",
|
diag_printf("Ambiguous command '%s', choices are: %s",
|
arg, cmd->str);
|
arg, cmd->str);
|
first = false;
|
first = false;
|
}
|
}
|
diag_printf(" %s", cmd2->str);
|
diag_printf(" %s", cmd2->str);
|
}
|
}
|
cmd2++;
|
cmd2++;
|
}
|
}
|
if (!first) {
|
if (!first) {
|
// At least one ambiguity found - fail the lookup
|
// At least one ambiguity found - fail the lookup
|
diag_printf("\n");
|
diag_printf("\n");
|
return (struct cmd *)0;
|
return (struct cmd *)0;
|
}
|
}
|
}
|
}
|
return cmd;
|
return cmd;
|
}
|
}
|
cmd++;
|
cmd++;
|
}
|
}
|
return (struct cmd *)0;
|
return (struct cmd *)0;
|
}
|
}
|
|
|
void
|
void
|
cmd_usage(struct cmd *tab, struct cmd *tabend, char *prefix)
|
cmd_usage(struct cmd *tab, struct cmd *tabend, char *prefix)
|
{
|
{
|
struct cmd *cmd;
|
struct cmd *cmd;
|
|
|
diag_printf("Usage:\n");
|
diag_printf("Usage:\n");
|
for (cmd = tab; cmd != tabend; cmd++) {
|
for (cmd = tab; cmd != tabend; cmd++) {
|
diag_printf(" %s%s %s\n", prefix, cmd->str, cmd->usage);
|
diag_printf(" %s%s %s\n", prefix, cmd->str, cmd->usage);
|
}
|
}
|
}
|
}
|
|
|
// Option processing
|
// Option processing
|
|
|
// Initialize option table entry (required because these entries
|
// Initialize option table entry (required because these entries
|
// may have dynamic contents, thus cannot be statically initialized)
|
// may have dynamic contents, thus cannot be statically initialized)
|
//
|
//
|
void
|
void
|
init_opts(struct option_info *opts, char flag, bool takes_arg,
|
init_opts(struct option_info *opts, char flag, bool takes_arg,
|
int arg_type, void **arg, bool *arg_set, char *name)
|
int arg_type, void **arg, bool *arg_set, char *name)
|
{
|
{
|
opts->flag = flag;
|
opts->flag = flag;
|
opts->takes_arg = takes_arg;
|
opts->takes_arg = takes_arg;
|
opts->arg_type = arg_type,
|
opts->arg_type = arg_type,
|
opts->arg = arg;
|
opts->arg = arg;
|
opts->arg_set = arg_set;
|
opts->arg_set = arg_set;
|
opts->name = name;
|
opts->name = name;
|
}
|
}
|
|
|
//
|
//
|
// Scan command line arguments (argc/argv), processing options, etc.
|
// Scan command line arguments (argc/argv), processing options, etc.
|
//
|
//
|
bool
|
bool
|
scan_opts(int argc, char *argv[], int first,
|
scan_opts(int argc, char *argv[], int first,
|
struct option_info *opts, int num_opts,
|
struct option_info *opts, int num_opts,
|
void **def_arg, int def_arg_type, char *def_descr)
|
void **def_arg, int def_arg_type, char *def_descr)
|
{
|
{
|
bool ret = true;
|
bool ret = true;
|
bool flag_ok;
|
bool flag_ok;
|
bool def_arg_set = false;
|
bool def_arg_set = false;
|
int i, j;
|
int i, j;
|
char c, *s;
|
char c, *s;
|
struct option_info *opt;
|
struct option_info *opt;
|
|
|
if (def_arg && (def_arg_type == OPTION_ARG_TYPE_STR)) {
|
if (def_arg && (def_arg_type == OPTION_ARG_TYPE_STR)) {
|
*def_arg = (char *)0;
|
*def_arg = (char *)0;
|
}
|
}
|
opt = opts;
|
opt = opts;
|
for (j = 0; j < num_opts; j++, opt++) {
|
for (j = 0; j < num_opts; j++, opt++) {
|
if (opt->arg_set) {
|
if (opt->arg_set) {
|
*opt->arg_set = false;
|
*opt->arg_set = false;
|
}
|
}
|
if (!opt->takes_arg) {
|
if (!opt->takes_arg) {
|
switch (opt->arg_type) {
|
switch (opt->arg_type) {
|
case OPTION_ARG_TYPE_NUM:
|
case OPTION_ARG_TYPE_NUM:
|
*(int *)opt->arg = 0;
|
*(int *)opt->arg = 0;
|
break;
|
break;
|
case OPTION_ARG_TYPE_FLG:
|
case OPTION_ARG_TYPE_FLG:
|
*(bool *)opt->arg = false;
|
*(bool *)opt->arg = false;
|
break;
|
break;
|
}
|
}
|
}
|
}
|
}
|
}
|
for (i = first; i < argc; i++) {
|
for (i = first; i < argc; i++) {
|
if (argv[i][0] == '-') {
|
if (argv[i][0] == '-') {
|
c = argv[i][1];
|
c = argv[i][1];
|
flag_ok = false;
|
flag_ok = false;
|
opt = opts;
|
opt = opts;
|
for (j = 0; j < num_opts; j++, opt++) {
|
for (j = 0; j < num_opts; j++, opt++) {
|
if (c == opt->flag) {
|
if (c == opt->flag) {
|
if (opt->arg_set && *opt->arg_set) {
|
if (opt->arg_set && *opt->arg_set) {
|
diag_printf("** Error: %s already specified\n", opt->name);
|
diag_printf("** Error: %s already specified\n", opt->name);
|
ret = false;
|
ret = false;
|
}
|
}
|
if (opt->takes_arg) {
|
if (opt->takes_arg) {
|
if (argv[i][2] == '=') {
|
if (argv[i][2] == '=') {
|
s = &argv[i][3];
|
s = &argv[i][3];
|
} else {
|
} else {
|
s = argv[i+1];
|
s = argv[i+1];
|
i++;
|
i++;
|
}
|
}
|
switch (opt->arg_type) {
|
switch (opt->arg_type) {
|
case OPTION_ARG_TYPE_NUM:
|
case OPTION_ARG_TYPE_NUM:
|
if (!parse_num(s, (unsigned long *)opt->arg, 0, 0)) {
|
if (!parse_num(s, (unsigned long *)opt->arg, 0, 0)) {
|
diag_printf("** Error: invalid number '%s' for %s\n",
|
diag_printf("** Error: invalid number '%s' for %s\n",
|
s, opt->name);
|
s, opt->name);
|
ret = false;
|
ret = false;
|
}
|
}
|
break;
|
break;
|
case OPTION_ARG_TYPE_STR:
|
case OPTION_ARG_TYPE_STR:
|
*opt->arg = s;
|
*opt->arg = s;
|
break;
|
break;
|
}
|
}
|
*opt->arg_set = true;
|
*opt->arg_set = true;
|
} else {
|
} else {
|
switch (opt->arg_type) {
|
switch (opt->arg_type) {
|
case OPTION_ARG_TYPE_NUM:
|
case OPTION_ARG_TYPE_NUM:
|
*(int *)opt->arg = *(int *)opt->arg + 1;
|
*(int *)opt->arg = *(int *)opt->arg + 1;
|
break;
|
break;
|
case OPTION_ARG_TYPE_FLG:
|
case OPTION_ARG_TYPE_FLG:
|
*(bool *)opt->arg = true;
|
*(bool *)opt->arg = true;
|
break;
|
break;
|
}
|
}
|
}
|
}
|
flag_ok = true;
|
flag_ok = true;
|
break;
|
break;
|
}
|
}
|
}
|
}
|
if (!flag_ok) {
|
if (!flag_ok) {
|
diag_printf("** Error: invalid flag '%c'\n", c);
|
diag_printf("** Error: invalid flag '%c'\n", c);
|
ret = false;
|
ret = false;
|
}
|
}
|
} else {
|
} else {
|
if (def_arg) {
|
if (def_arg) {
|
if (def_arg_set) {
|
if (def_arg_set) {
|
diag_printf("** Error: %s already specified\n", def_descr);
|
diag_printf("** Error: %s already specified\n", def_descr);
|
ret = false;
|
ret = false;
|
}
|
}
|
switch (def_arg_type) {
|
switch (def_arg_type) {
|
case OPTION_ARG_TYPE_NUM:
|
case OPTION_ARG_TYPE_NUM:
|
if (!parse_num(argv[i], (unsigned long *)def_arg, 0, 0)) {
|
if (!parse_num(argv[i], (unsigned long *)def_arg, 0, 0)) {
|
diag_printf("** Error: invalid number '%s' for %s\n",
|
diag_printf("** Error: invalid number '%s' for %s\n",
|
argv[i], def_descr);
|
argv[i], def_descr);
|
ret = false;
|
ret = false;
|
}
|
}
|
break;
|
break;
|
case OPTION_ARG_TYPE_STR:
|
case OPTION_ARG_TYPE_STR:
|
*def_arg = argv[i];
|
*def_arg = argv[i];
|
break;
|
break;
|
}
|
}
|
def_arg_set = true;
|
def_arg_set = true;
|
} else {
|
} else {
|
diag_printf("** Error: no default/non-flag arguments supported\n");
|
diag_printf("** Error: no default/non-flag arguments supported\n");
|
ret = false;
|
ret = false;
|
}
|
}
|
}
|
}
|
}
|
}
|
return ret;
|
return ret;
|
}
|
}
|
|
|
//
|
//
|
// Parse (scan) a number
|
// Parse (scan) a number
|
//
|
//
|
bool
|
bool
|
parse_num(char *s, unsigned long *val, char **es, char *delim)
|
parse_num(char *s, unsigned long *val, char **es, char *delim)
|
{
|
{
|
bool first = true;
|
bool first = true;
|
int radix = 10;
|
int radix = 10;
|
char c;
|
char c;
|
unsigned long result = 0;
|
unsigned long result = 0;
|
int digit;
|
int digit;
|
|
|
while (*s == ' ') s++;
|
while (*s == ' ') s++;
|
while (*s) {
|
while (*s) {
|
if (first && (s[0] == '0') && (_tolower(s[1]) == 'x')) {
|
if (first && (s[0] == '0') && (_tolower(s[1]) == 'x')) {
|
radix = 16;
|
radix = 16;
|
s += 2;
|
s += 2;
|
}
|
}
|
first = false;
|
first = false;
|
c = *s++;
|
c = *s++;
|
if (_is_hex(c) && ((digit = _from_hex(c)) < radix)) {
|
if (_is_hex(c) && ((digit = _from_hex(c)) < radix)) {
|
// Valid digit
|
// Valid digit
|
#ifdef CYGPKG_HAL_MIPS
|
#ifdef CYGPKG_HAL_MIPS
|
// FIXME: tx49 compiler generates 0x2539018 for MUL which
|
// FIXME: tx49 compiler generates 0x2539018 for MUL which
|
// isn't any good.
|
// isn't any good.
|
if (16 == radix)
|
if (16 == radix)
|
result = result << 4;
|
result = result << 4;
|
else
|
else
|
result = 10 * result;
|
result = 10 * result;
|
result += digit;
|
result += digit;
|
#else
|
#else
|
result = (result * radix) + digit;
|
result = (result * radix) + digit;
|
#endif
|
#endif
|
} else {
|
} else {
|
if (delim != (char *)0) {
|
if (delim != (char *)0) {
|
// See if this character is one of the delimiters
|
// See if this character is one of the delimiters
|
char *dp = delim;
|
char *dp = delim;
|
while (*dp && (c != *dp)) dp++;
|
while (*dp && (c != *dp)) dp++;
|
if (*dp) break; // Found a good delimiter
|
if (*dp) break; // Found a good delimiter
|
}
|
}
|
return false; // Malformatted number
|
return false; // Malformatted number
|
}
|
}
|
}
|
}
|
*val = result;
|
*val = result;
|
if (es != (char **)0) {
|
if (es != (char **)0) {
|
*es = s;
|
*es = s;
|
}
|
}
|
return true;
|
return true;
|
}
|
}
|
|
|
bool
|
bool
|
parse_bool(char *s, bool *val)
|
parse_bool(char *s, bool *val)
|
{
|
{
|
while (*s == ' ') s++;
|
while (*s == ' ') s++;
|
if ((*s == 't') || (*s == 'T')) {
|
if ((*s == 't') || (*s == 'T')) {
|
char *p = "rue";
|
char *p = "rue";
|
char *P = "RUE";
|
char *P = "RUE";
|
// check for (partial) rest of the word and no extra including the
|
// check for (partial) rest of the word and no extra including the
|
// terminating zero. "tru" will match; "truef" will not.
|
// terminating zero. "tru" will match; "truef" will not.
|
while ( *++s ) {
|
while ( *++s ) {
|
if ( *p != *s && *P != *s ) return false;
|
if ( *p != *s && *P != *s ) return false;
|
p++; P++;
|
p++; P++;
|
}
|
}
|
*val = true;
|
*val = true;
|
} else
|
} else
|
if ((*s == 'f') || (*s == 'F')) {
|
if ((*s == 'f') || (*s == 'F')) {
|
char *p = "alse";
|
char *p = "alse";
|
char *P = "ALSE";
|
char *P = "ALSE";
|
while ( *++s ) {
|
while ( *++s ) {
|
if ( *p != *s && *P != *s ) return false;
|
if ( *p != *s && *P != *s ) return false;
|
p++; P++;
|
p++; P++;
|
}
|
}
|
*val = false;
|
*val = false;
|
} else {
|
} else {
|
return false;
|
return false;
|
}
|
}
|
return true;
|
return true;
|
}
|
}
|
|
|
|
|