| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364 |
- // SPDX-License-Identifier: Apache-2.0
- /*
- * Copyright (C) 2013 Intel Corporation
- *
- */
- #define _GNU_SOURCE
- #include <stdio.h>
- #include <ctype.h>
- #include <string.h>
- #include "if-main.h"
- #include "terminal.h"
- /* how many times tab was hit */
- static int tab_hit_count;
- typedef struct split_arg {
- struct split_arg *next; /* next argument in buffer */
- const char *origin; /* pointer to original argument */
- char ntcopy[1]; /* null terminated copy of argument */
- } split_arg_t;
- /* function returns method of given name or NULL if not found */
- const struct method *get_interface_method(const char *iname,
- const char *mname)
- {
- const struct interface *iface = get_interface(iname);
- if (iface == NULL)
- return NULL;
- return get_method(iface->methods, mname);
- }
- /* prints matching elements */
- static void print_matches(enum_func f, void *user, const char *prefix, int len)
- {
- int i;
- const char *enum_name;
- putchar('\n');
- for (i = 0; NULL != (enum_name = f(user, i)); ++i) {
- if (strncmp(enum_name, prefix, len) == 0)
- printf("%s\t", enum_name);
- }
- putchar('\n');
- terminal_draw_command_line();
- }
- /*
- * This function splits command line into linked list of arguments.
- * line_buffer - pointer to input command line
- * size - size of command line to parse
- * buf - output buffer to keep split arguments list
- * buf_size_in_bytes - size of buf
- */
- static int split_command(const char *line_buffer, int size, split_arg_t *buf,
- int buf_size_in_bytes)
- {
- split_arg_t *prev = NULL;
- split_arg_t *arg = buf;
- int argc = 0;
- const char *p = line_buffer;
- const char *e = p + (size > 0 ? size : (int) strlen(p));
- int len;
- do {
- while (p < e && isspace(*p))
- p++;
- arg->origin = p;
- arg->next = NULL;
- while (p < e && !isspace(*p))
- p++;
- len = p - arg->origin;
- if (&arg->ntcopy[0] + len + 1 >
- (const char *) buf + buf_size_in_bytes)
- break;
- strncpy(arg->ntcopy, arg->origin, len);
- arg->ntcopy[len] = 0;
- if (prev != NULL)
- prev->next = arg;
- prev = arg;
- arg += (2 * sizeof(*arg) + len) / sizeof(*arg);
- argc++;
- } while (p < e);
- return argc;
- }
- /* Function to enumerate method names */
- static const char *methods_name(void *v, int i)
- {
- const struct interface *iface = v;
- return iface->methods[i].name[0] ? iface->methods[i].name : NULL;
- }
- struct command_completion_args;
- typedef void (*short_help)(struct command_completion_args *args);
- struct command_completion_args {
- const split_arg_t *arg; /* list of arguments */
- const char *typed; /* last typed element */
- enum_func func; /* enumerating function */
- void *user; /* argument to enumerating function */
- short_help help; /* help function */
- const char *user_help; /* additional data (used by short_help) */
- };
- /* complete command line */
- static void tab_completion(struct command_completion_args *args)
- {
- const char *name = args->typed;
- const int len = strlen(name);
- int i;
- int j;
- char prefix[128] = {0};
- int prefix_len = 0;
- int count = 0;
- const char *enum_name;
- for (i = 0; NULL != (enum_name = args->func(args->user, i)); ++i) {
- /* prefix does not match */
- if (strncmp(enum_name, name, len) != 0)
- continue;
- /* prefix matches first time */
- if (count++ == 0) {
- strcpy(prefix, enum_name);
- prefix_len = strlen(prefix);
- continue;
- }
- /* Prefix matches next time reduce prefix to common part */
- for (j = 0; prefix[j] != 0
- && prefix[j] == enum_name[j];)
- ++j;
- prefix_len = j;
- prefix[j] = 0;
- }
- if (count == 0) {
- /* no matches */
- if (args->help != NULL)
- args->help(args);
- tab_hit_count = 0;
- return;
- }
- /* len == prefix_len => nothing new was added */
- if (len == prefix_len) {
- if (count != 1) {
- if (tab_hit_count == 1) {
- putchar('\a');
- } else if (tab_hit_count == 2 ||
- args->help == NULL) {
- print_matches(args->func,
- args->user, name, len);
- } else {
- args->help(args);
- tab_hit_count = 1;
- }
- } else if (count == 1) {
- /* nothing to add, exact match add space */
- terminal_insert_into_command_line(" ");
- }
- } else {
- /* new chars can be added from some interface name(s) */
- if (count == 1) {
- /* exact match, add space */
- prefix[prefix_len++] = ' ';
- prefix[prefix_len] = '\0';
- }
- terminal_insert_into_command_line(prefix + len);
- tab_hit_count = 0;
- }
- }
- /* interface completion */
- static void command_completion(split_arg_t *arg)
- {
- struct command_completion_args args = {
- .arg = arg,
- .typed = arg->ntcopy,
- .func = command_name
- };
- tab_completion(&args);
- }
- /* method completion */
- static void method_completion(const struct interface *iface, split_arg_t *arg)
- {
- struct command_completion_args args = {
- .arg = arg,
- .typed = arg->next->ntcopy,
- .func = methods_name,
- .user = (void *) iface
- };
- if (iface == NULL)
- return;
- tab_completion(&args);
- }
- static const char *bold = "\x1b[1m";
- static const char *normal = "\x1b[0m";
- static bool find_nth_argument(const char *str, int n, const char **s,
- const char **e)
- {
- const char *p = str;
- int argc = 0;
- *e = NULL;
- while (p != NULL && *p != 0) {
- while (isspace(*p))
- ++p;
- if (n == argc)
- *s = p;
- if (*p == '[') {
- p = strchr(p, ']');
- if (p != NULL)
- *e = ++p;
- } else if (*p == '<') {
- p = strchr(p, '>');
- if (p != NULL)
- *e = ++p;
- } else {
- *e = strchr(p, ' ');
- if (*e == NULL)
- *e = p + strlen(p);
- p = *e;
- }
- if (n == argc)
- break;
- argc++;
- *e = NULL;
- }
- return *e != NULL;
- }
- /* prints short help on method for interface */
- static void method_help(struct command_completion_args *args)
- {
- int argc;
- const split_arg_t *arg = args->arg;
- const char *sb = NULL;
- const char *eb = NULL;
- const char *arg1 = "";
- int arg1_size = 0; /* size of method field (for methods > 0) */
- if (args->user_help == NULL)
- return;
- for (argc = 0; arg != NULL; argc++)
- arg = arg->next;
- /* Check if this is method from interface */
- if (get_command(args->arg->ntcopy) == NULL) {
- /* if so help is missing interface and method name */
- arg1 = args->arg->next->ntcopy;
- arg1_size = strlen(arg1) + 1;
- }
- find_nth_argument(args->user_help, argc - (arg1_size ? 3 : 2),
- &sb, &eb);
- if (eb != NULL)
- haltest_info("%s %-*s%.*s%s%.*s%s%s\n", args->arg->ntcopy,
- arg1_size, arg1, (int) (sb - args->user_help),
- args->user_help, bold, (int) (eb - sb),
- sb, normal, eb);
- else
- haltest_info("%s %-*s%s\n", args->arg->ntcopy,
- arg1_size, arg1, args->user_help);
- }
- /* So we have empty enumeration */
- static const char *return_null(void *user, int i)
- {
- return NULL;
- }
- /*
- * parameter completion function
- * argc - number of elements in arg list
- * arg - list of arguments
- * method - method to get completion from (can be NULL)
- */
- static void param_completion(int argc, const split_arg_t *arg,
- const struct method *method, int hlpix)
- {
- int i;
- const char *argv[argc];
- const split_arg_t *tmp = arg;
- struct command_completion_args args = {
- .arg = arg,
- .func = return_null
- };
- /* prepare standard argv from arg */
- for (i = 0; i < argc; ++i) {
- argv[i] = tmp->ntcopy;
- tmp = tmp->next;
- }
- if (method != NULL && method->complete != NULL) {
- /* ask method for completion function */
- method->complete(argc, argv, &args.func, &args.user);
- }
- /* If method provided enumeration function call try to complete */
- if (args.func != NULL) {
- args.typed = argv[argc - 1];
- args.help = method_help;
- args.user_help = method ? method->help : NULL;
- tab_completion(&args);
- }
- }
- /*
- * This method gets called when user tapped tab key.
- * line - points to command line
- * len - size of line that should be used for completions. This should be
- * cursor position during tab hit.
- */
- void process_tab(const char *line, int len)
- {
- int argc;
- static split_arg_t buf[(LINE_BUF_MAX * 2) / sizeof(split_arg_t)];
- const struct method *method;
- argc = split_command(line, len, buf, sizeof(buf));
- tab_hit_count++;
- if (argc == 0)
- return;
- if (argc == 1) {
- command_completion(buf);
- return;
- }
- method = get_command(buf[0].ntcopy);
- if (method != NULL) {
- param_completion(argc, buf, method, 1);
- } else if (argc == 2) {
- method_completion(get_interface(buf[0].ntcopy), buf);
- } else {
- /* Find method for <interface, name> pair */
- method = get_interface_method(buf[0].ntcopy,
- buf[0].next->ntcopy);
- param_completion(argc, buf, method, 2);
- }
- }
|