uboot/common/command.c
<<
>>
Prefs
   1/*
   2 * (C) Copyright 2000-2009
   3 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
   4 *
   5 * SPDX-License-Identifier:     GPL-2.0+
   6 */
   7
   8/*
   9 *  Command Processor Table
  10 */
  11
  12#include <common.h>
  13#include <command.h>
  14#include <linux/ctype.h>
  15
  16/*
  17 * Use puts() instead of printf() to avoid printf buffer overflow
  18 * for long help messages
  19 */
  20
  21int _do_help (cmd_tbl_t *cmd_start, int cmd_items, cmd_tbl_t * cmdtp, int
  22              flag, int argc, char * const argv[])
  23{
  24        int i;
  25        int rcode = 0;
  26
  27        if (argc == 1) {        /*show list of commands */
  28                cmd_tbl_t *cmd_array[cmd_items];
  29                int i, j, swaps;
  30
  31                /* Make array of commands from .uboot_cmd section */
  32                cmdtp = cmd_start;
  33                for (i = 0; i < cmd_items; i++) {
  34                        cmd_array[i] = cmdtp++;
  35                }
  36
  37                /* Sort command list (trivial bubble sort) */
  38                for (i = cmd_items - 1; i > 0; --i) {
  39                        swaps = 0;
  40                        for (j = 0; j < i; ++j) {
  41                                if (strcmp (cmd_array[j]->name,
  42                                            cmd_array[j + 1]->name) > 0) {
  43                                        cmd_tbl_t *tmp;
  44                                        tmp = cmd_array[j];
  45                                        cmd_array[j] = cmd_array[j + 1];
  46                                        cmd_array[j + 1] = tmp;
  47                                        ++swaps;
  48                                }
  49                        }
  50                        if (!swaps)
  51                                break;
  52                }
  53
  54                /* print short help (usage) */
  55                for (i = 0; i < cmd_items; i++) {
  56                        const char *usage = cmd_array[i]->usage;
  57
  58                        /* allow user abort */
  59                        if (ctrlc ())
  60                                return 1;
  61                        if (usage == NULL)
  62                                continue;
  63                        printf("%-*s- %s\n", CONFIG_SYS_HELP_CMD_WIDTH,
  64                               cmd_array[i]->name, usage);
  65                }
  66                return 0;
  67        }
  68        /*
  69         * command help (long version)
  70         */
  71        for (i = 1; i < argc; ++i) {
  72                if ((cmdtp = find_cmd_tbl (argv[i], cmd_start, cmd_items )) != NULL) {
  73                        rcode |= cmd_usage(cmdtp);
  74                } else {
  75                        printf ("Unknown command '%s' - try 'help'"
  76                                " without arguments for list of all"
  77                                " known commands\n\n", argv[i]
  78                                        );
  79                        rcode = 1;
  80                }
  81        }
  82        return rcode;
  83}
  84
  85/***************************************************************************
  86 * find command table entry for a command
  87 */
  88cmd_tbl_t *find_cmd_tbl (const char *cmd, cmd_tbl_t *table, int table_len)
  89{
  90        cmd_tbl_t *cmdtp;
  91        cmd_tbl_t *cmdtp_temp = table;  /*Init value */
  92        const char *p;
  93        int len;
  94        int n_found = 0;
  95
  96        if (!cmd)
  97                return NULL;
  98        /*
  99         * Some commands allow length modifiers (like "cp.b");
 100         * compare command name only until first dot.
 101         */
 102        len = ((p = strchr(cmd, '.')) == NULL) ? strlen (cmd) : (p - cmd);
 103
 104        for (cmdtp = table;
 105             cmdtp != table + table_len;
 106             cmdtp++) {
 107                if (strncmp (cmd, cmdtp->name, len) == 0) {
 108                        if (len == strlen (cmdtp->name))
 109                                return cmdtp;   /* full match */
 110
 111                        cmdtp_temp = cmdtp;     /* abbreviated command ? */
 112                        n_found++;
 113                }
 114        }
 115        if (n_found == 1) {                     /* exactly one match */
 116                return cmdtp_temp;
 117        }
 118
 119        return NULL;    /* not found or ambiguous command */
 120}
 121
 122cmd_tbl_t *find_cmd (const char *cmd)
 123{
 124        cmd_tbl_t *start = ll_entry_start(cmd_tbl_t, cmd);
 125        const int len = ll_entry_count(cmd_tbl_t, cmd);
 126        return find_cmd_tbl(cmd, start, len);
 127}
 128
 129int cmd_usage(const cmd_tbl_t *cmdtp)
 130{
 131        printf("%s - %s\n\n", cmdtp->name, cmdtp->usage);
 132
 133#ifdef  CONFIG_SYS_LONGHELP
 134        printf("Usage:\n%s ", cmdtp->name);
 135
 136        if (!cmdtp->help) {
 137                puts ("- No additional help available.\n");
 138                return 1;
 139        }
 140
 141        puts (cmdtp->help);
 142        putc ('\n');
 143#endif  /* CONFIG_SYS_LONGHELP */
 144        return 1;
 145}
 146
 147#ifdef CONFIG_AUTO_COMPLETE
 148
 149int var_complete(int argc, char * const argv[], char last_char, int maxv, char *cmdv[])
 150{
 151        static char tmp_buf[512];
 152        int space;
 153
 154        space = last_char == '\0' || isblank(last_char);
 155
 156        if (space && argc == 1)
 157                return env_complete("", maxv, cmdv, sizeof(tmp_buf), tmp_buf);
 158
 159        if (!space && argc == 2)
 160                return env_complete(argv[1], maxv, cmdv, sizeof(tmp_buf), tmp_buf);
 161
 162        return 0;
 163}
 164
 165/*************************************************************************************/
 166
 167static int complete_cmdv(int argc, char * const argv[], char last_char, int maxv, char *cmdv[])
 168{
 169        cmd_tbl_t *cmdtp = ll_entry_start(cmd_tbl_t, cmd);
 170        const int count = ll_entry_count(cmd_tbl_t, cmd);
 171        const cmd_tbl_t *cmdend = cmdtp + count;
 172        const char *p;
 173        int len, clen;
 174        int n_found = 0;
 175        const char *cmd;
 176
 177        /* sanity? */
 178        if (maxv < 2)
 179                return -2;
 180
 181        cmdv[0] = NULL;
 182
 183        if (argc == 0) {
 184                /* output full list of commands */
 185                for (; cmdtp != cmdend; cmdtp++) {
 186                        if (n_found >= maxv - 2) {
 187                                cmdv[n_found++] = "...";
 188                                break;
 189                        }
 190                        cmdv[n_found++] = cmdtp->name;
 191                }
 192                cmdv[n_found] = NULL;
 193                return n_found;
 194        }
 195
 196        /* more than one arg or one but the start of the next */
 197        if (argc > 1 || (last_char == '\0' || isblank(last_char))) {
 198                cmdtp = find_cmd(argv[0]);
 199                if (cmdtp == NULL || cmdtp->complete == NULL) {
 200                        cmdv[0] = NULL;
 201                        return 0;
 202                }
 203                return (*cmdtp->complete)(argc, argv, last_char, maxv, cmdv);
 204        }
 205
 206        cmd = argv[0];
 207        /*
 208         * Some commands allow length modifiers (like "cp.b");
 209         * compare command name only until first dot.
 210         */
 211        p = strchr(cmd, '.');
 212        if (p == NULL)
 213                len = strlen(cmd);
 214        else
 215                len = p - cmd;
 216
 217        /* return the partial matches */
 218        for (; cmdtp != cmdend; cmdtp++) {
 219
 220                clen = strlen(cmdtp->name);
 221                if (clen < len)
 222                        continue;
 223
 224                if (memcmp(cmd, cmdtp->name, len) != 0)
 225                        continue;
 226
 227                /* too many! */
 228                if (n_found >= maxv - 2) {
 229                        cmdv[n_found++] = "...";
 230                        break;
 231                }
 232
 233                cmdv[n_found++] = cmdtp->name;
 234        }
 235
 236        cmdv[n_found] = NULL;
 237        return n_found;
 238}
 239
 240static int make_argv(char *s, int argvsz, char *argv[])
 241{
 242        int argc = 0;
 243
 244        /* split into argv */
 245        while (argc < argvsz - 1) {
 246
 247                /* skip any white space */
 248                while (isblank(*s))
 249                        ++s;
 250
 251                if (*s == '\0') /* end of s, no more args       */
 252                        break;
 253
 254                argv[argc++] = s;       /* begin of argument string     */
 255
 256                /* find end of string */
 257                while (*s && !isblank(*s))
 258                        ++s;
 259
 260                if (*s == '\0')         /* end of s, no more args       */
 261                        break;
 262
 263                *s++ = '\0';            /* terminate current arg         */
 264        }
 265        argv[argc] = NULL;
 266
 267        return argc;
 268}
 269
 270static void print_argv(const char *banner, const char *leader, const char *sep, int linemax, char * const argv[])
 271{
 272        int ll = leader != NULL ? strlen(leader) : 0;
 273        int sl = sep != NULL ? strlen(sep) : 0;
 274        int len, i;
 275
 276        if (banner) {
 277                puts("\n");
 278                puts(banner);
 279        }
 280
 281        i = linemax;    /* force leader and newline */
 282        while (*argv != NULL) {
 283                len = strlen(*argv) + sl;
 284                if (i + len >= linemax) {
 285                        puts("\n");
 286                        if (leader)
 287                                puts(leader);
 288                        i = ll - sl;
 289                } else if (sep)
 290                        puts(sep);
 291                puts(*argv++);
 292                i += len;
 293        }
 294        printf("\n");
 295}
 296
 297static int find_common_prefix(char * const argv[])
 298{
 299        int i, len;
 300        char *anchor, *s, *t;
 301
 302        if (*argv == NULL)
 303                return 0;
 304
 305        /* begin with max */
 306        anchor = *argv++;
 307        len = strlen(anchor);
 308        while ((t = *argv++) != NULL) {
 309                s = anchor;
 310                for (i = 0; i < len; i++, t++, s++) {
 311                        if (*t != *s)
 312                                break;
 313                }
 314                len = s - anchor;
 315        }
 316        return len;
 317}
 318
 319static char tmp_buf[CONFIG_SYS_CBSIZE]; /* copy of console I/O buffer   */
 320
 321int cmd_auto_complete(const char *const prompt, char *buf, int *np, int *colp)
 322{
 323        int n = *np, col = *colp;
 324        char *argv[CONFIG_SYS_MAXARGS + 1];             /* NULL terminated      */
 325        char *cmdv[20];
 326        char *s, *t;
 327        const char *sep;
 328        int i, j, k, len, seplen, argc;
 329        int cnt;
 330        char last_char;
 331
 332        if (strcmp(prompt, CONFIG_SYS_PROMPT) != 0)
 333                return 0;       /* not in normal console */
 334
 335        cnt = strlen(buf);
 336        if (cnt >= 1)
 337                last_char = buf[cnt - 1];
 338        else
 339                last_char = '\0';
 340
 341        /* copy to secondary buffer which will be affected */
 342        strcpy(tmp_buf, buf);
 343
 344        /* separate into argv */
 345        argc = make_argv(tmp_buf, sizeof(argv)/sizeof(argv[0]), argv);
 346
 347        /* do the completion and return the possible completions */
 348        i = complete_cmdv(argc, argv, last_char, sizeof(cmdv)/sizeof(cmdv[0]), cmdv);
 349
 350        /* no match; bell and out */
 351        if (i == 0) {
 352                if (argc > 1)   /* allow tab for non command */
 353                        return 0;
 354                putc('\a');
 355                return 1;
 356        }
 357
 358        s = NULL;
 359        len = 0;
 360        sep = NULL;
 361        seplen = 0;
 362        if (i == 1) { /* one match; perfect */
 363                k = strlen(argv[argc - 1]);
 364                s = cmdv[0] + k;
 365                len = strlen(s);
 366                sep = " ";
 367                seplen = 1;
 368        } else if (i > 1 && (j = find_common_prefix(cmdv)) != 0) {      /* more */
 369                k = strlen(argv[argc - 1]);
 370                j -= k;
 371                if (j > 0) {
 372                        s = cmdv[0] + k;
 373                        len = j;
 374                }
 375        }
 376
 377        if (s != NULL) {
 378                k = len + seplen;
 379                /* make sure it fits */
 380                if (n + k >= CONFIG_SYS_CBSIZE - 2) {
 381                        putc('\a');
 382                        return 1;
 383                }
 384
 385                t = buf + cnt;
 386                for (i = 0; i < len; i++)
 387                        *t++ = *s++;
 388                if (sep != NULL)
 389                        for (i = 0; i < seplen; i++)
 390                                *t++ = sep[i];
 391                *t = '\0';
 392                n += k;
 393                col += k;
 394                puts(t - k);
 395                if (sep == NULL)
 396                        putc('\a');
 397                *np = n;
 398                *colp = col;
 399        } else {
 400                print_argv(NULL, "  ", " ", 78, cmdv);
 401
 402                puts(prompt);
 403                puts(buf);
 404        }
 405        return 1;
 406}
 407
 408#endif
 409
 410#ifdef CMD_DATA_SIZE
 411int cmd_get_data_size(char* arg, int default_size)
 412{
 413        /* Check for a size specification .b, .w or .l.
 414         */
 415        int len = strlen(arg);
 416        if (len > 2 && arg[len-2] == '.') {
 417                switch(arg[len-1]) {
 418                case 'b':
 419                        return 1;
 420                case 'w':
 421                        return 2;
 422                case 'l':
 423                        return 4;
 424#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA
 425                case 'q':
 426                        return 8;
 427#endif
 428                case 's':
 429                        return -2;
 430                default:
 431                        return -1;
 432                }
 433        }
 434        return default_size;
 435}
 436#endif
 437
 438#if defined(CONFIG_NEEDS_MANUAL_RELOC)
 439DECLARE_GLOBAL_DATA_PTR;
 440
 441void fixup_cmdtable(cmd_tbl_t *cmdtp, int size)
 442{
 443        int     i;
 444
 445        if (gd->reloc_off == 0)
 446                return;
 447
 448        for (i = 0; i < size; i++) {
 449                ulong addr;
 450
 451                addr = (ulong) (cmdtp->cmd) + gd->reloc_off;
 452#if DEBUG_COMMANDS
 453                printf("Command \"%s\": 0x%08lx => 0x%08lx\n",
 454                       cmdtp->name, (ulong) (cmdtp->cmd), addr);
 455#endif
 456                cmdtp->cmd =
 457                        (int (*)(struct cmd_tbl_s *, int, int, char * const []))addr;
 458                addr = (ulong)(cmdtp->name) + gd->reloc_off;
 459                cmdtp->name = (char *)addr;
 460                if (cmdtp->usage) {
 461                        addr = (ulong)(cmdtp->usage) + gd->reloc_off;
 462                        cmdtp->usage = (char *)addr;
 463                }
 464#ifdef  CONFIG_SYS_LONGHELP
 465                if (cmdtp->help) {
 466                        addr = (ulong)(cmdtp->help) + gd->reloc_off;
 467                        cmdtp->help = (char *)addr;
 468                }
 469#endif
 470#ifdef CONFIG_AUTO_COMPLETE
 471                if (cmdtp->complete) {
 472                        addr = (ulong)(cmdtp->complete) + gd->reloc_off;
 473                        cmdtp->complete =
 474                                (int (*)(int, char * const [], char, int, char * []))addr;
 475                }
 476#endif
 477                cmdtp++;
 478        }
 479}
 480#endif
 481
 482/**
 483 * Call a command function. This should be the only route in U-Boot to call
 484 * a command, so that we can track whether we are waiting for input or
 485 * executing a command.
 486 *
 487 * @param cmdtp         Pointer to the command to execute
 488 * @param flag          Some flags normally 0 (see CMD_FLAG_.. above)
 489 * @param argc          Number of arguments (arg 0 must be the command text)
 490 * @param argv          Arguments
 491 * @return 0 if command succeeded, else non-zero (CMD_RET_...)
 492 */
 493static int cmd_call(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 494{
 495        int result;
 496
 497        result = (cmdtp->cmd)(cmdtp, flag, argc, argv);
 498        if (result)
 499                debug("Command failed, result=%d", result);
 500        return result;
 501}
 502
 503enum command_ret_t cmd_process(int flag, int argc, char * const argv[],
 504                               int *repeatable, ulong *ticks)
 505{
 506        enum command_ret_t rc = CMD_RET_SUCCESS;
 507        cmd_tbl_t *cmdtp;
 508
 509        /* Look up command in command table */
 510        cmdtp = find_cmd(argv[0]);
 511        if (cmdtp == NULL) {
 512                printf("Unknown command '%s' - try 'help'\n", argv[0]);
 513                return 1;
 514        }
 515
 516        /* found - check max args */
 517        if (argc > cmdtp->maxargs)
 518                rc = CMD_RET_USAGE;
 519
 520#if defined(CONFIG_CMD_BOOTD)
 521        /* avoid "bootd" recursion */
 522        else if (cmdtp->cmd == do_bootd) {
 523                if (flag & CMD_FLAG_BOOTD) {
 524                        puts("'bootd' recursion detected\n");
 525                        rc = CMD_RET_FAILURE;
 526                } else {
 527                        flag |= CMD_FLAG_BOOTD;
 528                }
 529        }
 530#endif
 531
 532        /* If OK so far, then do the command */
 533        if (!rc) {
 534                if (ticks)
 535                        *ticks = get_timer(0);
 536                rc = cmd_call(cmdtp, flag, argc, argv);
 537                if (ticks)
 538                        *ticks = get_timer(*ticks);
 539                *repeatable &= cmdtp->repeatable;
 540        }
 541        if (rc == CMD_RET_USAGE)
 542                rc = cmd_usage(cmdtp);
 543        return rc;
 544}
 545
 546int cmd_process_error(cmd_tbl_t *cmdtp, int err)
 547{
 548        if (err) {
 549                printf("Command '%s' failed: Error %d\n", cmdtp->name, err);
 550                return 1;
 551        }
 552
 553        return 0;
 554}
 555