source: bootcd/isolinux/syslinux-6.03/com32/hdt/hdt-cli.c

Last change on this file was e16e8f2, checked in by Edwin Eefting <edwin@datux.nl>, 3 years ago

bootstuff

  • Property mode set to 100644
File size: 32.2 KB
Line 
1/* ----------------------------------------------------------------------- *
2 *
3 *   Copyright 2009 Erwan Velu - All Rights Reserved
4 *
5 *   Permission is hereby granted, free of charge, to any person
6 *   obtaining a copy of this software and associated documentation
7 *   files (the "Software"), to deal in the Software without
8 *   restriction, including without limitation the rights to use,
9 *   copy, modify, merge, publish, distribute, sublicense, and/or
10 *   sell copies of the Software, and to permit persons to whom
11 *   the Software is furnished to do so, subject to the following
12 *   conditions:
13 *
14 *   The above copyright notice and this permission notice shall
15 *   be included in all copies or substantial portions of the Software.
16 *
17 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19 *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21 *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22 *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24 *   OTHER DEALINGS IN THE SOFTWARE.
25 *
26 * -----------------------------------------------------------------------
27 */
28
29#include <stdlib.h>
30#include <string.h>
31#include <syslinux/config.h>
32#include <getkey.h>
33#include <acpi/acpi.h>
34#include "hdt-cli.h"
35#include "hdt-common.h"
36
37struct cli_mode_descr *list_modes[] = {
38    &hdt_mode,
39    &dmi_mode,
40    &syslinux_mode,
41    &pxe_mode,
42    &kernel_mode,
43    &cpu_mode,
44    &pci_mode,
45    &vesa_mode,
46    &disk_mode,
47    &vpd_mode,
48    &memory_mode,
49    &acpi_mode,
50    NULL,
51};
52
53/*
54 * .aliases = {"q", "quit"} won't work since it is an array of pointers, not an
55 * array of variables. There is no easy way around it besides declaring the arrays of
56 * strings first.
57 */
58const char *exit_aliases[] = { "q", "quit" };
59const char *help_aliases[] = { "h", "?" };
60
61/* List of aliases */
62struct cli_alias hdt_aliases[] = {
63    {
64     .command = CLI_EXIT,
65     .nb_aliases = 2,
66     .aliases = exit_aliases,
67     },
68    {
69     .command = CLI_HELP,
70     .nb_aliases = 2,
71     .aliases = help_aliases,
72     },
73};
74
75struct cli_mode_descr *current_mode;
76int autocomplete_backlog;
77
78struct autocomplete_list {
79    char autocomplete_token[MAX_LINE_SIZE];
80    struct autocomplete_list *next;
81};
82struct autocomplete_list *autocomplete_head = NULL;
83struct autocomplete_list *autocomplete_tail = NULL;
84struct autocomplete_list *autocomplete_last_seen = NULL;
85
86static void autocomplete_add_token_to_list(const char *token)
87{
88    struct autocomplete_list *new = malloc(sizeof(struct autocomplete_list));
89
90    strlcpy(new->autocomplete_token, token, sizeof(new->autocomplete_token));
91    new->next = NULL;
92    autocomplete_backlog++;
93
94    if (autocomplete_tail != NULL)
95        autocomplete_tail->next = new;
96    if (autocomplete_head == NULL)
97        autocomplete_head = new;
98    autocomplete_tail = new;
99}
100
101static void autocomplete_destroy_list(void)
102{
103    struct autocomplete_list *tmp = NULL;
104
105    while (autocomplete_head != NULL) {
106        tmp = autocomplete_head->next;
107        free(autocomplete_head);
108        autocomplete_head = tmp;
109    }
110    autocomplete_backlog = 0;
111    autocomplete_tail = NULL;
112    autocomplete_last_seen = NULL;
113}
114
115/**
116 * set_mode - set the current mode of the cli
117 * @mode:       mode to set
118 *
119 * Unlike cli_set_mode, this function is not used by the cli directly.
120 **/
121void set_mode(cli_mode_t mode, struct s_hardware *hardware)
122{
123    int i = 0;
124
125    switch (mode) {
126    case EXIT_MODE:
127        hdt_cli.mode = mode;
128        break;
129    case HDT_MODE:
130        hdt_cli.mode = mode;
131        snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_HDT);
132        break;
133    case PXE_MODE:
134        if (hardware->sv->filesystem != SYSLINUX_FS_PXELINUX) {
135            more_printf("You are not currently using PXELINUX\n");
136            break;
137        }
138        hdt_cli.mode = mode;
139        snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_PXE);
140        break;
141    case KERNEL_MODE:
142        hdt_cli.mode = mode;
143        snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_KERNEL);
144        break;
145    case SYSLINUX_MODE:
146        hdt_cli.mode = mode;
147        snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_SYSLINUX);
148        break;
149    case VESA_MODE:
150        hdt_cli.mode = mode;
151        snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_VESA);
152        break;
153    case PCI_MODE:
154        hdt_cli.mode = mode;
155        snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_PCI);
156        break;
157    case CPU_MODE:
158        hdt_cli.mode = mode;
159        snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_CPU);
160        break;
161    case DMI_MODE:
162        if (!hardware->is_dmi_valid) {
163            more_printf("No valid DMI table found, exiting.\n");
164            break;
165        }
166        hdt_cli.mode = mode;
167        snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_DMI);
168        break;
169    case DISK_MODE:
170        hdt_cli.mode = mode;
171        snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_DISK);
172        break;
173    case VPD_MODE:
174        if (!hardware->is_vpd_valid) {
175            more_printf("No valid VPD table found, exiting.\n");
176            break;
177        }
178        hdt_cli.mode = mode;
179        snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_VPD);
180        break;
181    case MEMORY_MODE:
182        hdt_cli.mode = mode;
183        snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_MEMORY);
184        break;
185    case ACPI_MODE:
186        hdt_cli.mode = mode;
187        snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_ACPI);
188        break;
189    default:
190        /* Invalid mode */
191        more_printf("Unknown mode, please choose among:\n");
192        while (list_modes[i]) {
193            more_printf("\t%s\n", list_modes[i]->name);
194            i++;
195        }
196    }
197
198    find_cli_mode_descr(hdt_cli.mode, &current_mode);
199    /* There is not cli_mode_descr struct for the exit mode */
200    if (current_mode == NULL && hdt_cli.mode != EXIT_MODE) {
201        /* Shouldn't get here... */
202        more_printf("!!! BUG: Mode '%d' unknown.\n", hdt_cli.mode);
203    }
204}
205
206/**
207 * mode_s_to_mode_t - given a mode string, return the cli_mode_t representation
208 **/
209cli_mode_t mode_s_to_mode_t(char *name)
210{
211    int i = 0;
212
213    while (list_modes[i]) {
214        if (!strncmp(name, list_modes[i]->name, sizeof(list_modes[i]->name)))
215            break;
216        i++;
217    }
218
219    if (!list_modes[i])
220        return INVALID_MODE;
221    else
222        return list_modes[i]->mode;
223}
224
225/**
226 * find_cli_mode_descr - find the cli_mode_descr struct associated to a mode
227 * @mode:       mode to look for
228 * @mode_found: store the mode if found, NULL otherwise
229 *
230 * Given a mode name, return a pointer to the associated cli_mode_descr
231 * structure.
232 * Note: the current mode name is stored in hdt_cli.mode.
233 **/
234void find_cli_mode_descr(cli_mode_t mode, struct cli_mode_descr **mode_found)
235{
236    int i = 0;
237
238    while (list_modes[i] && list_modes[i]->mode != mode)
239        i++;
240
241    /* Shouldn't get here... */
242    if (!list_modes[i])
243        *mode_found = NULL;
244    else
245        *mode_found = list_modes[i];
246}
247
248/**
249 * expand_aliases - resolve aliases mapping
250 * @line:       command line to parse
251 * @command:    first token in the line
252 * @module:     second token in the line
253 * @argc:       number of arguments
254 * @argv:       array of arguments
255 *
256 * We maintain a small list of static alises to enhance user experience.
257 * Only commands can be aliased (first token). Otherwise it can become really hairy...
258 **/
259static void expand_aliases(char *line __unused, char **command, char **module,
260                           int *argc, char **argv)
261{
262    struct cli_mode_descr *mode;
263    int i, j;
264
265    find_cli_mode_descr(mode_s_to_mode_t(*command), &mode);
266    if (mode != NULL && *module == NULL) {
267        /*
268         * The user specified a mode instead of `set mode...', e.g.
269         * `dmi' instead of `set mode dmi'
270         */
271
272        /* *argv is NULL since *module is NULL */
273        *argc = 1;
274        *argv = malloc(*argc * sizeof(char *));
275        argv[0] = malloc((sizeof(*command) + 1) * sizeof(char));
276        strlcpy(argv[0], *command, sizeof(*command) + 1);
277        dprintf("CLI DEBUG: ALIAS %s ", *command);
278
279        strlcpy(*command, CLI_SET, sizeof(CLI_SET));    /* set */
280
281        *module = malloc(sizeof(CLI_MODE) * sizeof(char));
282        strlcpy(*module, CLI_MODE, sizeof(CLI_MODE));   /* mode */
283
284        dprintf("--> %s %s %s\n", *command, *module, argv[0]);
285        goto out;
286    }
287
288    /* Simple aliases mapping a single command to another one */
289    for (i = 0; i < MAX_ALIASES; i++) {
290        for (j = 0; j < hdt_aliases[i].nb_aliases; j++) {
291            if (!strncmp(*command, hdt_aliases[i].aliases[j],
292                         sizeof(hdt_aliases[i].aliases[j]))) {
293                dprintf("CLI DEBUG: ALIAS %s ", *command);
294                strlcpy(*command, hdt_aliases[i].command,
295                        sizeof(hdt_aliases[i].command) + 1);
296                dprintf("--> %s\n", *command);
297                goto out;       /* Don't allow chaining aliases */
298            }
299        }
300    }
301    return;
302
303out:
304    dprintf("CLI DEBUG: New parameters:\n");
305    dprintf("CLI DEBUG: command = %s\n", *command);
306    dprintf("CLI DEBUG: module  = %s\n", *module);
307    dprintf("CLI DEBUG: argc    = %d\n", *argc);
308    for (i = 0; i < *argc; i++)
309        dprintf("CLI DEBUG: argv[%d] = %s\n", i, argv[0]);
310    return;
311}
312
313/**
314 * parse_command_line - low level parser for the command line
315 * @line:       command line to parse
316 * @command:    first token in the line
317 * @module:     second token in the line
318 * @argc:       number of arguments
319 * @argv:       array of arguments
320 *
321 * The format of the command line is:
322 *      <main command> [<module on which to operate> [<args>]]
323 *      command is always malloc'ed (even for an empty line)
324 **/
325static void parse_command_line(char *line, char **command, char **module,
326                               int *argc, char **argv)
327{
328    int argc_iter = 0, args_pos = 0, token_found = 0, token_len = 0;
329    int args_len = 0;
330    char *pch = NULL, *pch_next = NULL, *tmp_pch_next = NULL;
331
332    *command = NULL;
333    *module = NULL;
334    *argc = 0;
335
336    pch = line;
337    while (pch != NULL) {
338        pch_next = strchr(pch + 1, ' ');
339        tmp_pch_next = pch_next;
340
341        /*
342         * Skip whitespaces if the user entered
343         * 'set   mode        foo' for 'set mode foo'
344         *  ^   ^
345         *  |___|___ pch
346         *      |___ pch_next <- wrong!
347         *
348         *  We still keep the position into tmp_pch_next to compute
349         *  the lenght of the current token.
350         */
351        while (pch_next != NULL && !strncmp(pch_next, CLI_SPACE, 1))
352            pch_next++;
353
354        /* End of line guaranteed to be zeroed */
355        if (pch_next == NULL) {
356            token_len = (int)(strchr(pch + 1, '\0') - pch);
357            args_len = token_len;
358        } else {
359            token_len = (int)(tmp_pch_next - pch);
360            args_len = (int)(pch_next - pch);
361        }
362
363        if (token_found == 0) {
364            /* Main command to execute */
365            *command = malloc((token_len + 1) * sizeof(char));
366            strlcpy(*command, pch, token_len);
367            (*command)[token_len] = '\0';
368            dprintf("CLI DEBUG parse: command = %s\n", *command);
369            args_pos += args_len;
370        } else if (token_found == 1) {
371            /* Module */
372            *module = malloc((token_len + 1) * sizeof(char));
373            strlcpy(*module, pch, token_len);
374            (*module)[token_len] = '\0';
375            dprintf("CLI DEBUG parse: module  = %s\n", *module);
376            args_pos += args_len;
377        } else
378            (*argc)++;
379
380        token_found++;
381        pch = pch_next;
382    }
383    dprintf("CLI DEBUG parse: argc    = %d\n", *argc);
384
385    /* Skip arguments handling if none is supplied */
386    if (!*argc)
387        return;
388
389    /* Transform the arguments string into an array */
390    *argv = malloc(*argc * sizeof(char *));
391    pch = strtok(line + args_pos, CLI_SPACE);
392    while (pch != NULL) {
393        dprintf("CLI DEBUG parse: argv[%d] = %s\n", argc_iter, pch);
394        argv[argc_iter] = malloc(strlen(pch) * sizeof(char));
395        strlcpy(argv[argc_iter], pch, strlen(pch));
396        argc_iter++;
397        pch = strtok(NULL, CLI_SPACE);
398        /*
399         * strtok(NULL, CLI_SPACE) over a stream of spaces
400         * will return an empty string
401         */
402        while (pch != NULL && !strncmp(pch, "", 1))
403            pch = strtok(NULL, CLI_SPACE);
404    }
405}
406
407/**
408 * find_cli_callback_descr - find a callback in a list of modules
409 * @module_name:        Name of the module to find
410 * @modules_list:       Lits of modules among which to find @module_name
411 * @module_found:       Pointer to the matched module, NULL if not found
412 *
413 * Given a module name and a list of possible modules, find the corresponding
414 * module structure that matches the module name and store it in @module_found.
415 **/
416void find_cli_callback_descr(const char *module_name,
417                             struct cli_module_descr *modules_list,
418                             struct cli_callback_descr **module_found)
419{
420    int modules_iter = 0;
421
422    if (modules_list == NULL)
423        goto not_found;
424
425    /* Find the callback to execute */
426    while (modules_list->modules[modules_iter].name &&
427           strcmp(module_name, modules_list->modules[modules_iter].name) != 0)
428        modules_iter++;
429
430    if (modules_list->modules[modules_iter].name) {
431        *module_found = &(modules_list->modules[modules_iter]);
432        dprintf("CLI DEBUG: module %s found\n", (*module_found)->name);
433        return;
434    }
435
436not_found:
437    *module_found = NULL;
438    return;
439}
440
441/**
442 * autocomplete_command - print matching commands
443 * @command:    Beginning of the command
444 *
445 * Given a string @command, print all availables commands starting with
446 * @command. Commands are found within the list of commands for the current
447 * mode and the hdt mode (if the current mode is not hdt).
448 **/
449static void autocomplete_command(char *command)
450{
451    int j = 0;
452    struct cli_callback_descr *associated_module = NULL;
453
454    /* First take care of the two special commands: 'show' and 'set' */
455    if (strncmp(CLI_SHOW, command, strlen(command)) == 0) {
456        printf("%s\n", CLI_SHOW);
457        autocomplete_add_token_to_list(CLI_SHOW);
458    }
459    if (strncmp(CLI_SET, command, strlen(command)) == 0) {
460        printf("%s\n", CLI_SET);
461        autocomplete_add_token_to_list(CLI_SET);
462    }
463
464    /*
465     * Then, go through the modes for the special case
466     *      '<mode>' -> 'set mode <mode>'
467     */
468    while (list_modes[j]) {
469        if (strncmp(list_modes[j]->name, command, strlen(command)) == 0) {
470            printf("%s\n", list_modes[j]->name);
471            autocomplete_add_token_to_list(list_modes[j]->name);
472        }
473        j++;
474    }
475
476    /*
477     * Let's go now through the list of default_modules for the current mode
478     * (single token commands for the current_mode)
479     */
480    j = 0;
481    if (current_mode->default_modules && current_mode->default_modules->modules) {
482        while (current_mode->default_modules->modules[j].name) {
483            if (strncmp(current_mode->default_modules->modules[j].name,
484                        command, strlen(command)) == 0) {
485                printf("%s\n", current_mode->default_modules->modules[j].name);
486                autocomplete_add_token_to_list(current_mode->default_modules->
487                                               modules[j].name);
488            }
489            j++;
490        }
491    }
492
493    /*
494     * Finally, if the current_mode is not hdt, list the available
495     * default_modules of hdt (these are always available from any mode).
496     */
497    if (current_mode->mode == HDT_MODE)
498        return;
499
500    if (!hdt_mode.default_modules || !hdt_mode.default_modules->modules)
501        return;
502
503    j = 0;
504    while (hdt_mode.default_modules &&
505           hdt_mode.default_modules->modules[j].name) {
506        /*
507         * Any default command that is present in hdt mode but
508         * not in the current mode is available. A default
509         * command can be redefined in the current mode though.
510         * This next call tests this use case: if it is
511         * overwritten, do not print it again.
512         */
513        find_cli_callback_descr(hdt_mode.default_modules->modules[j].name,
514                                current_mode->default_modules,
515                                &associated_module);
516        if (associated_module == NULL &&
517            strncmp(command,
518                    hdt_mode.default_modules->modules[j].name,
519                    strlen(command)) == 0) {
520            printf("%s\n", hdt_mode.default_modules->modules[j].name);
521            autocomplete_add_token_to_list(hdt_mode.default_modules->modules[j].
522                                           name);
523        }
524        j++;
525    }
526}
527
528/**
529 * autocomplete_module - print matching modules
530 * @command:    Command on the command line (not NULL)
531 * @module:     Beginning of the module
532 *
533 * Given a command @command and a string @module, print all availables modules
534 * starting with @module for command @command. Commands are found within the
535 * list of commands for the current mode and the hdt mode (if the current mode
536 * is not hdt).
537 **/
538static void autocomplete_module(char *command, char *module)
539{
540    int j = 0;
541    char autocomplete_full_line[MAX_LINE_SIZE];
542
543    if (strncmp(CLI_SHOW, command, strlen(command)) == 0) {
544        if (!current_mode->show_modules || !current_mode->show_modules->modules)
545            return;
546
547        while (current_mode->show_modules->modules[j].name) {
548            if (strncmp(current_mode->show_modules->modules[j].name,
549                        module, strlen(module)) == 0) {
550                printf("%s\n", current_mode->show_modules->modules[j].name);
551                sprintf(autocomplete_full_line, "%s %s",
552                        CLI_SHOW, current_mode->show_modules->modules[j].name);
553                autocomplete_add_token_to_list(autocomplete_full_line);
554            }
555            j++;
556        }
557    } else if (strncmp(CLI_SET, command, strlen(command)) == 0) {
558        j = 0;
559        if (!current_mode->set_modules || !current_mode->set_modules->modules)
560            return;
561
562        while (current_mode->set_modules->modules[j].name) {
563            if (strncmp(current_mode->set_modules->modules[j].name,
564                        module, strlen(module)) == 0) {
565                printf("%s\n", current_mode->set_modules->modules[j].name);
566                sprintf(autocomplete_full_line, "%s %s",
567                        CLI_SET, current_mode->set_modules->modules[j].name);
568                autocomplete_add_token_to_list(autocomplete_full_line);
569            }
570            j++;
571        }
572    }
573}
574
575/**
576 * autocomplete - find possible matches for a command line
577 * @line:       command line to parse
578 **/
579static void autocomplete(char *line)
580{
581    int i;
582    int argc = 0;
583    char *command = NULL, *module = NULL;
584    char **argv = NULL;
585
586    parse_command_line(line, &command, &module, &argc, argv);
587
588    dprintf("CLI DEBUG autocomplete: before checking args\n");
589    /* If the user specified arguments, there is nothing we can complete */
590    if (argc != 0)
591        goto out;
592
593    /* No argument, (the start of) a module has been specified */
594    if (module != NULL) {
595        autocomplete_module(command, module);
596        free(module);
597        goto out;
598    }
599
600    /* No argument, no module, (the start of) a command has been specified */
601    if (command != NULL) {
602        autocomplete_command(command);
603        free(command);
604        goto out;
605    }
606
607out:
608    /* Let's not forget to clean ourselves */
609    for (i = 0; i < argc; i++)
610        free(argv[i]);
611    if (argc > 0)
612        free(argv);
613    return;
614}
615
616/**
617 * exec_command - main logic to map the command line to callbacks
618 **/
619static void exec_command(char *line, struct s_hardware *hardware)
620{
621    int argc, i = 0;
622    char *command = NULL, *module = NULL;
623    char **argv = NULL;
624    struct cli_callback_descr *current_module = NULL;
625
626    /* This will allocate memory for command and module */
627    parse_command_line(line, &command, &module, &argc, argv);
628
629    dprintf("CLI DEBUG exec: Checking for aliases\n");
630    /*
631     * Expand shortcuts, if needed
632     * This will allocate memory for argc/argv
633     */
634    expand_aliases(line, &command, &module, &argc, argv);
635   
636    find_cli_callback_descr(command, current_mode->default_modules,
637                                &current_module);
638
639    if ((module == NULL) || (current_module->nomodule == true)) {
640        dprintf("CLI DEBUG exec : single command detected\n");
641        /*
642         * A single word was specified: look at the list of default
643         * commands in the current mode to see if there is a match.
644         * If not, it may be a generic function (exit, help, ...). These
645         * are stored in the list of default commands of the hdt mode.
646         */
647
648        /* First of all it the command doesn't need module, let's rework the arguments */
649        if ((current_module->nomodule == true) && ( module != NULL)) {
650                dprintf("CLI_DEBUG exec: Reworking arguments with argc=%d\n",argc);
651                char **new_argv=NULL;
652                new_argv=malloc((argc + 2)*sizeof(char *));
653                for (int argc_iter=0; argc_iter<argc; argc_iter++) {
654                        dprintf("CLI_DEBUG exec rework : copy %d to %d (%s)\n",argc_iter,argc_iter+1,argv[argc_iter]);
655                        new_argv[argc_iter+1] = malloc(strlen(argv[argc_iter]));
656                        strlcpy(new_argv[argc_iter+1], argv[argc_iter], strlen(argv[argc_iter]));
657                        free(argv[argc_iter]);
658                }
659                new_argv[0] = malloc(strlen(module)*sizeof(char));
660                strlcpy(new_argv[0], module, strlen(module));
661                argc++;
662                free(argv);
663                argv=new_argv;
664        }
665
666        if (current_module != NULL)
667            current_module->exec(argc, argv, hardware);
668        else if (!strncmp(command, CLI_SHOW, sizeof(CLI_SHOW) - 1) &&
669                 current_mode->show_modules != NULL &&
670                 current_mode->show_modules->default_callback != NULL)
671            current_mode->show_modules->default_callback(argc, argv, hardware);
672        else if (!strncmp(command, CLI_SET, sizeof(CLI_SET) - 1) &&
673                 current_mode->set_modules != NULL &&
674                 current_mode->set_modules->default_callback != NULL)
675            current_mode->set_modules->default_callback(argc, argv, hardware);
676        else {
677            find_cli_callback_descr(command, hdt_mode.default_modules,
678                                    &current_module);
679            if (current_module != NULL)
680                current_module->exec(argc, argv, hardware);
681            else
682                more_printf("unknown command: '%s'\n", command);
683        }
684    } else {
685        /*
686         * A module has been specified! We now need to find the type of command.
687         *
688         * The syntax of the cli is the following:
689         *    <type of command> <module on which to operate> <args>
690         * e.g.
691         *    dmi> show system
692         *    dmi> show bank 1
693         *    dmi> show memory 0 1
694         *    pci> show device 12
695         *    hdt> set mode dmi
696         */
697        if (!strncmp(command, CLI_SHOW, sizeof(CLI_SHOW) - 1)) {
698            dprintf("CLI DEBUG exec: %s command detected\n", CLI_SHOW);
699            /* Look first for a 'show' callback in the current mode */
700            find_cli_callback_descr(module, current_mode->show_modules,
701                                    &current_module);
702            /* Execute the callback, if found */
703            if (current_module != NULL)
704                current_module->exec(argc, argv, hardware);
705            else {
706                dprintf("CLI DEBUG exec: Looking for callback\n");
707                /* Look now for a 'show' callback in the hdt mode */
708                find_cli_callback_descr(module, hdt_mode.show_modules,
709                                        &current_module);
710                /* Execute the callback, if found */
711                if (current_module != NULL)
712                    current_module->exec(argc, argv, hardware);
713                else
714                    printf("unknown module: '%s'\n", module);
715            }
716        } else if (!strncmp(command, CLI_SET, sizeof(CLI_SET) - 1)) {
717            dprintf("CLI DEBUG exec : %s command detected\n", CLI_SET);
718            /* Look now for a 'set' callback in the hdt mode */
719            find_cli_callback_descr(module, current_mode->set_modules,
720                                    &current_module);
721            /* Execute the callback, if found */
722            if (current_module != NULL)
723                current_module->exec(argc, argv, hardware);
724            else {
725                /* Look now for a 'set' callback in the hdt mode */
726                find_cli_callback_descr(module, hdt_mode.set_modules,
727                                        &current_module);
728                /* Execute the callback, if found */
729                if (current_module != NULL)
730                    current_module->exec(argc, argv, hardware);
731                else
732                    printf("unknown module: '%s'\n", module);
733            }
734        }
735    }
736
737    /* Let's not forget to clean ourselves */
738    if (command != NULL)
739        free(command);
740    if (module != NULL)
741        free(module);
742    for (i = 0; i < argc; i++)
743        free(argv[i]);
744    if (argc > 0)
745        free(argv);
746}
747
748static void reset_prompt(void)
749{
750    /* No need to display the prompt if we exit */
751    if (hdt_cli.mode != EXIT_MODE) {
752        printf("%s", hdt_cli.prompt);
753        /* Reset the line */
754        hdt_cli.cursor_pos = 0;
755    }
756}
757
758void start_auto_mode(struct s_hardware *hardware)
759{
760    char *mypch;
761    int nb_commands = 0;
762    char *commands[MAX_NB_AUTO_COMMANDS];
763
764    more_printf("\nEntering Auto mode\n");
765
766    /* Protecting the auto_label from the strtok modifications */
767    char *temp = strdup(hardware->auto_label);
768
769    /* Searching & saving all commands */
770    mypch = strtok(temp, AUTO_SEPARATOR);
771    while (mypch != NULL) {
772        if ((strlen(remove_spaces(mypch)) > 0) &&
773            (remove_spaces(mypch)[0] != AUTO_SEPARATOR[0])) {
774            nb_commands++;
775            if ((commands[nb_commands] = malloc(AUTO_COMMAND_SIZE)) != NULL) {
776                sprintf(commands[nb_commands], "%s", remove_spaces(mypch));
777            } else
778                nb_commands--;
779        }
780        mypch = strtok(NULL, AUTO_SEPARATOR);
781    }
782
783    free(temp);
784
785    /* Executing found commands */
786    for (int i = 1; i <= nb_commands; i++) {
787        if (commands[i]) {
788            if (!quiet)
789                more_printf("%s%s\n", hdt_cli.prompt, commands[i]);
790            exec_command(commands[i], hardware);
791            free(commands[i]);
792        }
793    }
794
795    if (!quiet)
796        more_printf("\nExiting Auto mode\n");
797
798    more_printf("\n");
799}
800
801void print_history(int argc, char **argv, struct s_hardware * hardware)
802{
803    (void)argc;
804    (void)argv;
805    (void)hardware;
806
807    reset_more_printf();
808    for (int i = 1; i <= MAX_HISTORY_SIZE; i++) {
809        if (i == hdt_cli.history_pos) {
810            more_printf("*%d:'%s'\n", i, hdt_cli.history[i]);
811            continue;
812        }
813        if (strlen(hdt_cli.history[i]) == 0)
814            continue;
815        more_printf(" %d:'%s'\n", i, hdt_cli.history[i]);
816    }
817}
818
819/* Code that manages the cli mode */
820void start_cli_mode(struct s_hardware *hardware)
821{
822    int current_key = 0;
823    int future_history_pos = 1; /* position of the next position in the history */
824    int current_future_history_pos = 1; /* Temp variable */
825    bool display_history = true;        /* Temp Variable */
826    char temp_command[MAX_LINE_SIZE];
827
828    hdt_cli.cursor_pos = 0;
829    memset(hdt_cli.history, 0, sizeof(hdt_cli.history));
830    hdt_cli.history_pos = 1;
831    hdt_cli.max_history_pos = 1;
832
833    /* Find the mode selected */
834    set_mode(HDT_MODE, hardware);
835    find_cli_mode_descr(hdt_cli.mode, &current_mode);
836    if (current_mode == NULL) {
837        /* Shouldn't get here... */
838        more_printf("!!! BUG: Mode '%d' unknown.\n", hdt_cli.mode);
839        return;
840    }
841
842    /* Start the auto mode if the command line is set */
843    if (strlen(hardware->auto_label) > 0) {
844        start_auto_mode(hardware);
845    }
846
847    more_printf("Entering CLI mode\n");
848
849    reset_prompt();
850
851    while (hdt_cli.mode != EXIT_MODE) {
852
853        /* Display the cursor */
854        display_cursor(true);
855
856        /* Let's put the cursor blinking until we get an input */
857        set_cursor_blink(true);
858
859        /* We wait endlessly for a keyboard input */
860        current_key = get_key(stdin, 0);
861
862        /* We have to cancel the blinking mode to prevent
863         * input text to blink */
864        set_cursor_blink(false);
865
866        /* Reset autocomplete buffer unless TAB is pressed */
867        if (current_key != KEY_TAB)
868            autocomplete_destroy_list();
869
870        switch (current_key) {
871            /* clear until then end of line */
872        case KEY_CTRL('k'):
873            /* Clear the end of the line */
874            clear_end_of_line();
875            memset(&INPUT[hdt_cli.cursor_pos], 0,
876                   strlen(INPUT) - hdt_cli.cursor_pos);
877            break;
878
879        case KEY_CTRL('c'):
880            printf("\n");
881            reset_prompt();
882            break;
883
884        case KEY_LEFT:
885            if (hdt_cli.cursor_pos > 0) {
886                move_cursor_left(1);
887                hdt_cli.cursor_pos--;
888            }
889            break;
890
891        case KEY_RIGHT:
892            if (hdt_cli.cursor_pos < (int)strlen(INPUT)) {
893                move_cursor_right(1);
894                hdt_cli.cursor_pos++;
895            }
896            break;
897
898        case KEY_CTRL('e'):
899        case KEY_END:
900            /* Calling with a 0 value will make the cursor move */
901            /* So, let's move the cursor only if needed */
902            if ((strlen(INPUT) - hdt_cli.cursor_pos) > 0) {
903                /* Return to the begining of line */
904                move_cursor_right(strlen(INPUT) - hdt_cli.cursor_pos);
905                hdt_cli.cursor_pos = strlen(INPUT);
906            }
907            break;
908
909        case KEY_CTRL('a'):
910        case KEY_HOME:
911            /* Calling with a 0 value will make the cursor move */
912            /* So, let's move the cursor only if needed */
913            if (hdt_cli.cursor_pos > 0) {
914                /* Return to the begining of line */
915                move_cursor_left(hdt_cli.cursor_pos);
916                hdt_cli.cursor_pos = 0;
917            }
918            break;
919
920        case KEY_UP:
921
922            /* Saving future position */
923            current_future_history_pos = future_history_pos;
924
925            /* We have to compute the next position */
926            if (future_history_pos == 1) {
927                future_history_pos = MAX_HISTORY_SIZE;
928            } else {
929                future_history_pos--;
930            }
931
932            /* Does the next position is valid */
933            if (strlen(hdt_cli.history[future_history_pos]) == 0) {
934                /* Position is invalid, restoring position */
935                future_history_pos = current_future_history_pos;
936                break;
937            }
938
939            /* Let's make that future position the one we use */
940            memset(INPUT, 0, sizeof(INPUT));
941            strlcpy(INPUT, hdt_cli.history[future_history_pos], sizeof(INPUT));
942
943            /* Clear the line */
944            clear_line();
945
946            /* Move to the begining of line */
947            move_cursor_to_column(0);
948
949            reset_prompt();
950            printf("%s", INPUT);
951            hdt_cli.cursor_pos = strlen(INPUT);
952            break;
953
954        case KEY_DOWN:
955            display_history = true;
956
957            /* Saving future position */
958            current_future_history_pos = future_history_pos;
959
960            if (future_history_pos == MAX_HISTORY_SIZE) {
961                future_history_pos = 1;
962            } else {
963                future_history_pos++;
964            }
965
966            /* Does the next position is valid */
967            if (strlen(hdt_cli.history[future_history_pos]) == 0)
968                display_history = false;
969
970            /* An exception is made to reach the last empty line */
971            if (future_history_pos == hdt_cli.max_history_pos)
972                display_history = true;
973
974            if (display_history == false) {
975                /* Position is invalid, restoring position */
976                future_history_pos = current_future_history_pos;
977                break;
978            }
979
980            /* Let's make that future position the one we use */
981            memset(INPUT, 0, sizeof(INPUT));
982            strlcpy(INPUT, hdt_cli.history[future_history_pos], sizeof(INPUT));
983
984            /* Clear the line */
985            clear_line();
986
987            /* Move to the begining of line */
988            move_cursor_to_column(0);
989
990            reset_prompt();
991            printf("%s", INPUT);
992            hdt_cli.cursor_pos = strlen(INPUT);
993            break;
994
995        case KEY_TAB:
996            if (autocomplete_backlog) {
997                clear_line();
998                /* Move to the begining of line */
999                move_cursor_to_column(0);
1000                reset_prompt();
1001                printf("%s", autocomplete_last_seen->autocomplete_token);
1002                strlcpy(INPUT,
1003                        autocomplete_last_seen->autocomplete_token,
1004                        sizeof(INPUT));
1005                hdt_cli.cursor_pos = strlen(INPUT);
1006
1007                /* Cycle through the list */
1008                autocomplete_last_seen = autocomplete_last_seen->next;
1009                if (autocomplete_last_seen == NULL)
1010                    autocomplete_last_seen = autocomplete_head;
1011            } else {
1012                printf("\n");
1013                autocomplete(skip_spaces(INPUT));
1014                autocomplete_last_seen = autocomplete_head;
1015
1016                printf("%s%s", hdt_cli.prompt, INPUT);
1017            }
1018            break;
1019
1020        case KEY_ENTER:
1021            printf("\n");
1022            if (strlen(remove_spaces(INPUT)) < 1) {
1023                reset_prompt();
1024                break;
1025            }
1026            exec_command(remove_spaces(INPUT), hardware);
1027            hdt_cli.history_pos++;
1028
1029            /* Did we reach the end of the history ?*/
1030            if (hdt_cli.history_pos > MAX_HISTORY_SIZE) {
1031                /* Let's return at the beginning */   
1032                hdt_cli.history_pos = 1;
1033            }
1034
1035            /* Does the next position is already used ?
1036             * If yes, we are cycling in history */
1037            if (strlen(INPUT) > 0) {
1038                /* Let's clean that entry */
1039                memset(&INPUT,0,sizeof(INPUT));
1040            }
1041
1042            future_history_pos = hdt_cli.history_pos;
1043            if (hdt_cli.history_pos > hdt_cli.max_history_pos)
1044                hdt_cli.max_history_pos = hdt_cli.history_pos;
1045            reset_prompt();
1046            break;
1047
1048        case KEY_CTRL('d'):
1049        case KEY_DELETE:
1050            /* No need to delete when input is empty */
1051            if (strlen(INPUT) == 0)
1052                break;
1053            /* Don't delete when cursor is at the end of the line */
1054            if (hdt_cli.cursor_pos >= strlen(INPUT))
1055                break;
1056
1057            for (int c = hdt_cli.cursor_pos; c < (int)strlen(INPUT) - 1; c++)
1058                INPUT[c] = INPUT[c + 1];
1059            INPUT[strlen(INPUT) - 1] = '\0';
1060
1061            /* Clear the end of the line */
1062            clear_end_of_line();
1063
1064            /* Print the resulting buffer */
1065            printf("%s", INPUT + hdt_cli.cursor_pos);
1066
1067            /* Replace the cursor at the proper place */
1068            if (strlen(INPUT + hdt_cli.cursor_pos) > 0)
1069                move_cursor_left(strlen(INPUT + hdt_cli.cursor_pos));
1070            break;
1071
1072        case KEY_DEL:
1073        case KEY_BACKSPACE:
1074            /* Don't delete prompt */
1075            if (hdt_cli.cursor_pos == 0)
1076                break;
1077
1078            for (int c = hdt_cli.cursor_pos - 1;
1079                 c < (int)strlen(INPUT) - 1; c++)
1080                INPUT[c] = INPUT[c + 1];
1081            INPUT[strlen(INPUT) - 1] = '\0';
1082
1083            /* Get one char back */
1084            move_cursor_left(1);
1085
1086            /* Clear the end of the line */
1087            clear_end_of_line();
1088
1089            /* Print the resulting buffer */
1090            printf("%s", INPUT + hdt_cli.cursor_pos - 1);
1091
1092            /* Realing to a char before the place we were */
1093            hdt_cli.cursor_pos--;
1094            move_cursor_to_column(strlen(hdt_cli.prompt) + hdt_cli.cursor_pos +
1095                                  1);
1096
1097            break;
1098
1099        case KEY_F1:
1100            printf("\n");
1101            exec_command(CLI_HELP, hardware);
1102            reset_prompt();
1103            break;
1104
1105        default:
1106            if ((current_key < 0x20) || (current_key > 0x7e))
1107                break;
1108            /* Prevent overflow */
1109            if (hdt_cli.cursor_pos > MAX_LINE_SIZE - 2)
1110                break;
1111            /* If we aren't at the end of the input line, let's insert */
1112            if (hdt_cli.cursor_pos < (int)strlen(INPUT)) {
1113                char key[2];
1114                int trailing_chars = strlen(INPUT) - hdt_cli.cursor_pos;
1115                memset(temp_command, 0, sizeof(temp_command));
1116                strlcpy(temp_command, INPUT, hdt_cli.cursor_pos);
1117                sprintf(key, "%c", current_key);
1118                strncat(temp_command, key, 1);
1119                strncat(temp_command,
1120                        INPUT + hdt_cli.cursor_pos, trailing_chars);
1121                memset(INPUT, 0, sizeof(INPUT));
1122                snprintf(INPUT, sizeof(INPUT), "%s", temp_command);
1123
1124                /* Clear the end of the line */
1125                clear_end_of_line();
1126
1127                /* Print the resulting buffer */
1128                printf("%s", INPUT + hdt_cli.cursor_pos);
1129
1130                /* Return where we must put the new char */
1131                move_cursor_left(trailing_chars);
1132
1133            } else {
1134                putchar(current_key);
1135                INPUT[hdt_cli.cursor_pos] = current_key;
1136            }
1137            hdt_cli.cursor_pos++;
1138            break;
1139        }
1140    }
1141}
Note: See TracBrowser for help on using the repository browser.