uboot/common/command.c
<<
>>
Prefs
   1/*
   2 * (C) Copyright 2000-2009
   3 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
   4 *
   5 * See file CREDITS for list of people who contributed to this
   6 * project.
   7 *
   8 * This program is free software; you can redistribute it and/or
   9 * modify it under the terms of the GNU General Public License as
  10 * published by the Free Software Foundation; either version 2 of
  11 * the License, or (at your option) any later version.
  12 *
  13 * This program is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 * GNU General Public License for more details.
  17 *
  18 * You should have received a copy of the GNU General Public License
  19 * along with this program; if not, write to the Free Software
  20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  21 * MA 02111-1307 USA
  22 */
  23
  24/*
  25 *  Command Processor Table
  26 */
  27
  28#include <common.h>
  29#include <command.h>
  30
  31/*
  32 * Use puts() instead of printf() to avoid printf buffer overflow
  33 * for long help messages
  34 */
  35
  36int _do_help (cmd_tbl_t *cmd_start, int cmd_items, cmd_tbl_t * cmdtp, int
  37              flag, int argc, char * const argv[])
  38{
  39        int i;
  40        int rcode = 0;
  41
  42        if (argc == 1) {        /*show list of commands */
  43                cmd_tbl_t *cmd_array[cmd_items];
  44                int i, j, swaps;
  45
  46                /* Make array of commands from .uboot_cmd section */
  47                cmdtp = cmd_start;
  48                for (i = 0; i < cmd_items; i++) {
  49                        cmd_array[i] = cmdtp++;
  50                }
  51
  52                /* Sort command list (trivial bubble sort) */
  53                for (i = cmd_items - 1; i > 0; --i) {
  54                        swaps = 0;
  55                        for (j = 0; j < i; ++j) {
  56                                if (strcmp (cmd_array[j]->name,
  57                                            cmd_array[j + 1]->name) > 0) {
  58                                        cmd_tbl_t *tmp;
  59                                        tmp = cmd_array[j];
  60                                        cmd_array[j] = cmd_array[j + 1];
  61                                        cmd_array[j + 1] = tmp;
  62                                        ++swaps;
  63                                }
  64                        }
  65                        if (!swaps)
  66                                break;
  67                }
  68
  69                /* print short help (usage) */
  70                for (i = 0; i < cmd_items; i++) {
  71                        const char *usage = cmd_array[i]->usage;
  72
  73                        /* allow user abort */
  74                        if (ctrlc ())
  75                                return 1;
  76                        if (usage == NULL)
  77                                continue;
  78                        printf("%-*s- %s\n", CONFIG_SYS_HELP_CMD_WIDTH,
  79                               cmd_array[i]->name, usage);
  80                }
  81                return 0;
  82        }
  83        /*
  84         * command help (long version)
  85         */
  86        for (i = 1; i < argc; ++i) {
  87                if ((cmdtp = find_cmd_tbl (argv[i], cmd_start, cmd_items )) != NULL) {
  88                        rcode |= cmd_usage(cmdtp);
  89                } else {
  90                        printf ("Unknown command '%s' - try 'help'"
  91                                " without arguments for list of all"
  92                                " known commands\n\n", argv[i]
  93                                        );
  94                        rcode = 1;
  95                }
  96        }
  97        return rcode;
  98}
  99
 100/***************************************************************************
 101 * find command table entry for a command
 102 */
 103cmd_tbl_t *find_cmd_tbl (const char *cmd, cmd_tbl_t *table, int table_len)
 104{
 105        cmd_tbl_t *cmdtp;
 106        cmd_tbl_t *cmdtp_temp = table;  /*Init value */
 107        const char *p;
 108        int len;
 109        int n_found = 0;
 110
 111        if (!cmd)
 112                return NULL;
 113        /*
 114         * Some commands allow length modifiers (like "cp.b");
 115         * compare command name only until first dot.
 116         */
 117        len = ((p = strchr(cmd, '.')) == NULL) ? strlen (cmd) : (p - cmd);
 118
 119        for (cmdtp = table;
 120             cmdtp != table + table_len;
 121             cmdtp++) {
 122                if (strncmp (cmd, cmdtp->name, len) == 0) {
 123                        if (len == strlen (cmdtp->name))
 124                                return cmdtp;   /* full match */
 125
 126                        cmdtp_temp = cmdtp;     /* abbreviated command ? */
 127                        n_found++;
 128                }
 129        }
 130        if (n_found == 1) {                     /* exactly one match */
 131                return cmdtp_temp;
 132        }
 133
 134        return NULL;    /* not found or ambiguous command */
 135}
 136
 137cmd_tbl_t *find_cmd (const char *cmd)
 138{
 139        int len = &__u_boot_cmd_end - &__u_boot_cmd_start;
 140        return find_cmd_tbl(cmd, &__u_boot_cmd_start, len);
 141}
 142
 143int cmd_usage(cmd_tbl_t *cmdtp)
 144{
 145        printf("%s - %s\n\n", cmdtp->name, cmdtp->usage);
 146
 147#ifdef  CONFIG_SYS_LONGHELP
 148        printf("Usage:\n%s ", cmdtp->name);
 149
 150        if (!cmdtp->help) {
 151                puts ("- No additional help available.\n");
 152                return 1;
 153        }
 154
 155        puts (cmdtp->help);
 156        putc ('\n');
 157#endif  /* CONFIG_SYS_LONGHELP */
 158        return 1;
 159}
 160
 161#ifdef CONFIG_AUTO_COMPLETE
 162
 163int var_complete(int argc, char * const argv[], char last_char, int maxv, char *cmdv[])
 164{
 165        static char tmp_buf[512];
 166        int space;
 167
 168        space = last_char == '\0' || last_char == ' ' || last_char == '\t';
 169
 170        if (space && argc == 1)
 171                return env_complete("", maxv, cmdv, sizeof(tmp_buf), tmp_buf);
 172
 173        if (!space && argc == 2)
 174                return env_complete(argv[1], maxv, cmdv, sizeof(tmp_buf), tmp_buf);
 175
 176        return 0;
 177}
 178
 179/*************************************************************************************/
 180
 181static int complete_cmdv(int argc, char * const argv[], char last_char, int maxv, char *cmdv[])
 182{
 183        cmd_tbl_t *cmdtp;
 184        const char *p;
 185        int len, clen;
 186        int n_found = 0;
 187        const char *cmd;
 188
 189        /* sanity? */
 190        if (maxv < 2)
 191                return -2;
 192
 193        cmdv[0] = NULL;
 194
 195        if (argc == 0) {
 196                /* output full list of commands */
 197                for (cmdtp = &__u_boot_cmd_start; cmdtp != &__u_boot_cmd_end; cmdtp++) {
 198                        if (n_found >= maxv - 2) {
 199                                cmdv[n_found++] = "...";
 200                                break;
 201                        }
 202                        cmdv[n_found++] = cmdtp->name;
 203                }
 204                cmdv[n_found] = NULL;
 205                return n_found;
 206        }
 207
 208        /* more than one arg or one but the start of the next */
 209        if (argc > 1 || (last_char == '\0' || last_char == ' ' || last_char == '\t')) {
 210                cmdtp = find_cmd(argv[0]);
 211                if (cmdtp == NULL || cmdtp->complete == NULL) {
 212                        cmdv[0] = NULL;
 213                        return 0;
 214                }
 215                return (*cmdtp->complete)(argc, argv, last_char, maxv, cmdv);
 216        }
 217
 218        cmd = argv[0];
 219        /*
 220         * Some commands allow length modifiers (like "cp.b");
 221         * compare command name only until first dot.
 222         */
 223        p = strchr(cmd, '.');
 224        if (p == NULL)
 225                len = strlen(cmd);
 226        else
 227                len = p - cmd;
 228
 229        /* return the partial matches */
 230        for (cmdtp = &__u_boot_cmd_start; cmdtp != &__u_boot_cmd_end; cmdtp++) {
 231
 232                clen = strlen(cmdtp->name);
 233                if (clen < len)
 234                        continue;
 235
 236                if (memcmp(cmd, cmdtp->name, len) != 0)
 237                        continue;
 238
 239                /* too many! */
 240                if (n_found >= maxv - 2) {
 241                        cmdv[n_found++] = "...";
 242                        break;
 243                }
 244
 245                cmdv[n_found++] = cmdtp->name;
 246        }
 247
 248        cmdv[n_found] = NULL;
 249        return n_found;
 250}
 251
 252static int make_argv(char *s, int argvsz, char *argv[])
 253{
 254        int argc = 0;
 255
 256        /* split into argv */
 257        while (argc < argvsz - 1) {
 258
 259                /* skip any white space */
 260                while ((*s == ' ') || (*s == '\t'))
 261                        ++s;
 262
 263                if (*s == '\0') /* end of s, no more args       */
 264                        break;
 265
 266                argv[argc++] = s;       /* begin of argument string     */
 267
 268                /* find end of string */
 269                while (*s && (*s != ' ') && (*s != '\t'))
 270                        ++s;
 271
 272                if (*s == '\0')         /* end of s, no more args       */
 273                        break;
 274
 275                *s++ = '\0';            /* terminate current arg         */
 276        }
 277        argv[argc] = NULL;
 278
 279        return argc;
 280}
 281
 282static void print_argv(const char *banner, const char *leader, const char *sep, int linemax, char * const argv[])
 283{
 284        int ll = leader != NULL ? strlen(leader) : 0;
 285        int sl = sep != NULL ? strlen(sep) : 0;
 286        int len, i;
 287
 288        if (banner) {
 289                puts("\n");
 290                puts(banner);
 291        }
 292
 293        i = linemax;    /* force leader and newline */
 294        while (*argv != NULL) {
 295                len = strlen(*argv) + sl;
 296                if (i + len >= linemax) {
 297                        puts("\n");
 298                        if (leader)
 299                                puts(leader);
 300                        i = ll - sl;
 301                } else if (sep)
 302                        puts(sep);
 303                puts(*argv++);
 304                i += len;
 305        }
 306        printf("\n");
 307}
 308
 309static int find_common_prefix(char * const argv[])
 310{
 311        int i, len;
 312        char *anchor, *s, *t;
 313
 314        if (*argv == NULL)
 315                return 0;
 316
 317        /* begin with max */
 318        anchor = *argv++;
 319        len = strlen(anchor);
 320        while ((t = *argv++) != NULL) {
 321                s = anchor;
 322                for (i = 0; i < len; i++, t++, s++) {
 323                        if (*t != *s)
 324                                break;
 325                }
 326                len = s - anchor;
 327        }
 328        return len;
 329}
 330
 331static char tmp_buf[CONFIG_SYS_CBSIZE]; /* copy of console I/O buffer   */
 332
 333int cmd_auto_complete(const char *const prompt, char *buf, int *np, int *colp)
 334{
 335        int n = *np, col = *colp;
 336        char *argv[CONFIG_SYS_MAXARGS + 1];             /* NULL terminated      */
 337        char *cmdv[20];
 338        char *s, *t;
 339        const char *sep;
 340        int i, j, k, len, seplen, argc;
 341        int cnt;
 342        char last_char;
 343
 344        if (strcmp(prompt, CONFIG_SYS_PROMPT) != 0)
 345                return 0;       /* not in normal console */
 346
 347        cnt = strlen(buf);
 348        if (cnt >= 1)
 349                last_char = buf[cnt - 1];
 350        else
 351                last_char = '\0';
 352
 353        /* copy to secondary buffer which will be affected */
 354        strcpy(tmp_buf, buf);
 355
 356        /* separate into argv */
 357        argc = make_argv(tmp_buf, sizeof(argv)/sizeof(argv[0]), argv);
 358
 359        /* do the completion and return the possible completions */
 360        i = complete_cmdv(argc, argv, last_char, sizeof(cmdv)/sizeof(cmdv[0]), cmdv);
 361
 362        /* no match; bell and out */
 363        if (i == 0) {
 364                if (argc > 1)   /* allow tab for non command */
 365                        return 0;
 366                putc('\a');
 367                return 1;
 368        }
 369
 370        s = NULL;
 371        len = 0;
 372        sep = NULL;
 373        seplen = 0;
 374        if (i == 1) { /* one match; perfect */
 375                k = strlen(argv[argc - 1]);
 376                s = cmdv[0] + k;
 377                len = strlen(s);
 378                sep = " ";
 379                seplen = 1;
 380        } else if (i > 1 && (j = find_common_prefix(cmdv)) != 0) {      /* more */
 381                k = strlen(argv[argc - 1]);
 382                j -= k;
 383                if (j > 0) {
 384                        s = cmdv[0] + k;
 385                        len = j;
 386                }
 387        }
 388
 389        if (s != NULL) {
 390                k = len + seplen;
 391                /* make sure it fits */
 392                if (n + k >= CONFIG_SYS_CBSIZE - 2) {
 393                        putc('\a');
 394                        return 1;
 395                }
 396
 397                t = buf + cnt;
 398                for (i = 0; i < len; i++)
 399                        *t++ = *s++;
 400                if (sep != NULL)
 401                        for (i = 0; i < seplen; i++)
 402                                *t++ = sep[i];
 403                *t = '\0';
 404                n += k;
 405                col += k;
 406                puts(t - k);
 407                if (sep == NULL)
 408                        putc('\a');
 409                *np = n;
 410                *colp = col;
 411        } else {
 412                print_argv(NULL, "  ", " ", 78, cmdv);
 413
 414                puts(prompt);
 415                puts(buf);
 416        }
 417        return 1;
 418}
 419
 420#endif
 421
 422#ifdef CMD_DATA_SIZE
 423int cmd_get_data_size(char* arg, int default_size)
 424{
 425        /* Check for a size specification .b, .w or .l.
 426         */
 427        int len = strlen(arg);
 428        if (len > 2 && arg[len-2] == '.') {
 429                switch(arg[len-1]) {
 430                case 'b':
 431                        return 1;
 432                case 'w':
 433                        return 2;
 434                case 'l':
 435                        return 4;
 436                case 's':
 437                        return -2;
 438                default:
 439                        return -1;
 440                }
 441        }
 442        return default_size;
 443}
 444#endif
 445
 446#if defined(CONFIG_NEEDS_MANUAL_RELOC)
 447DECLARE_GLOBAL_DATA_PTR;
 448
 449void fixup_cmdtable(cmd_tbl_t *cmdtp, int size)
 450{
 451        int     i;
 452
 453        if (gd->reloc_off == 0)
 454                return;
 455
 456        for (i = 0; i < size; i++) {
 457                ulong addr;
 458
 459                addr = (ulong) (cmdtp->cmd) + gd->reloc_off;
 460#if DEBUG_COMMANDS
 461                printf("Command \"%s\": 0x%08lx => 0x%08lx\n",
 462                       cmdtp->name, (ulong) (cmdtp->cmd), addr);
 463#endif
 464                cmdtp->cmd =
 465                        (int (*)(struct cmd_tbl_s *, int, int, char * const []))addr;
 466                addr = (ulong)(cmdtp->name) + gd->reloc_off;
 467                cmdtp->name = (char *)addr;
 468                if (cmdtp->usage) {
 469                        addr = (ulong)(cmdtp->usage) + gd->reloc_off;
 470                        cmdtp->usage = (char *)addr;
 471                }
 472#ifdef  CONFIG_SYS_LONGHELP
 473                if (cmdtp->help) {
 474                        addr = (ulong)(cmdtp->help) + gd->reloc_off;
 475                        cmdtp->help = (char *)addr;
 476                }
 477#endif
 478                cmdtp++;
 479        }
 480}
 481#endif
 482