| /* | |
| * Copyright (C) 2013 Intel Corporation | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| * | |
| */ | |
| #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); | |
| } | |
| } |