tabcompletion.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. // SPDX-License-Identifier: Apache-2.0
  2. /*
  3. * Copyright (C) 2013 Intel Corporation
  4. *
  5. */
  6. #define _GNU_SOURCE
  7. #include <stdio.h>
  8. #include <ctype.h>
  9. #include <string.h>
  10. #include "if-main.h"
  11. #include "terminal.h"
  12. /* how many times tab was hit */
  13. static int tab_hit_count;
  14. typedef struct split_arg {
  15. struct split_arg *next; /* next argument in buffer */
  16. const char *origin; /* pointer to original argument */
  17. char ntcopy[1]; /* null terminated copy of argument */
  18. } split_arg_t;
  19. /* function returns method of given name or NULL if not found */
  20. const struct method *get_interface_method(const char *iname,
  21. const char *mname)
  22. {
  23. const struct interface *iface = get_interface(iname);
  24. if (iface == NULL)
  25. return NULL;
  26. return get_method(iface->methods, mname);
  27. }
  28. /* prints matching elements */
  29. static void print_matches(enum_func f, void *user, const char *prefix, int len)
  30. {
  31. int i;
  32. const char *enum_name;
  33. putchar('\n');
  34. for (i = 0; NULL != (enum_name = f(user, i)); ++i) {
  35. if (strncmp(enum_name, prefix, len) == 0)
  36. printf("%s\t", enum_name);
  37. }
  38. putchar('\n');
  39. terminal_draw_command_line();
  40. }
  41. /*
  42. * This function splits command line into linked list of arguments.
  43. * line_buffer - pointer to input command line
  44. * size - size of command line to parse
  45. * buf - output buffer to keep split arguments list
  46. * buf_size_in_bytes - size of buf
  47. */
  48. static int split_command(const char *line_buffer, int size, split_arg_t *buf,
  49. int buf_size_in_bytes)
  50. {
  51. split_arg_t *prev = NULL;
  52. split_arg_t *arg = buf;
  53. int argc = 0;
  54. const char *p = line_buffer;
  55. const char *e = p + (size > 0 ? size : (int) strlen(p));
  56. int len;
  57. do {
  58. while (p < e && isspace(*p))
  59. p++;
  60. arg->origin = p;
  61. arg->next = NULL;
  62. while (p < e && !isspace(*p))
  63. p++;
  64. len = p - arg->origin;
  65. if (&arg->ntcopy[0] + len + 1 >
  66. (const char *) buf + buf_size_in_bytes)
  67. break;
  68. strncpy(arg->ntcopy, arg->origin, len);
  69. arg->ntcopy[len] = 0;
  70. if (prev != NULL)
  71. prev->next = arg;
  72. prev = arg;
  73. arg += (2 * sizeof(*arg) + len) / sizeof(*arg);
  74. argc++;
  75. } while (p < e);
  76. return argc;
  77. }
  78. /* Function to enumerate method names */
  79. static const char *methods_name(void *v, int i)
  80. {
  81. const struct interface *iface = v;
  82. return iface->methods[i].name[0] ? iface->methods[i].name : NULL;
  83. }
  84. struct command_completion_args;
  85. typedef void (*short_help)(struct command_completion_args *args);
  86. struct command_completion_args {
  87. const split_arg_t *arg; /* list of arguments */
  88. const char *typed; /* last typed element */
  89. enum_func func; /* enumerating function */
  90. void *user; /* argument to enumerating function */
  91. short_help help; /* help function */
  92. const char *user_help; /* additional data (used by short_help) */
  93. };
  94. /* complete command line */
  95. static void tab_completion(struct command_completion_args *args)
  96. {
  97. const char *name = args->typed;
  98. const int len = strlen(name);
  99. int i;
  100. int j;
  101. char prefix[128] = {0};
  102. int prefix_len = 0;
  103. int count = 0;
  104. const char *enum_name;
  105. for (i = 0; NULL != (enum_name = args->func(args->user, i)); ++i) {
  106. /* prefix does not match */
  107. if (strncmp(enum_name, name, len) != 0)
  108. continue;
  109. /* prefix matches first time */
  110. if (count++ == 0) {
  111. strcpy(prefix, enum_name);
  112. prefix_len = strlen(prefix);
  113. continue;
  114. }
  115. /* Prefix matches next time reduce prefix to common part */
  116. for (j = 0; prefix[j] != 0
  117. && prefix[j] == enum_name[j];)
  118. ++j;
  119. prefix_len = j;
  120. prefix[j] = 0;
  121. }
  122. if (count == 0) {
  123. /* no matches */
  124. if (args->help != NULL)
  125. args->help(args);
  126. tab_hit_count = 0;
  127. return;
  128. }
  129. /* len == prefix_len => nothing new was added */
  130. if (len == prefix_len) {
  131. if (count != 1) {
  132. if (tab_hit_count == 1) {
  133. putchar('\a');
  134. } else if (tab_hit_count == 2 ||
  135. args->help == NULL) {
  136. print_matches(args->func,
  137. args->user, name, len);
  138. } else {
  139. args->help(args);
  140. tab_hit_count = 1;
  141. }
  142. } else if (count == 1) {
  143. /* nothing to add, exact match add space */
  144. terminal_insert_into_command_line(" ");
  145. }
  146. } else {
  147. /* new chars can be added from some interface name(s) */
  148. if (count == 1) {
  149. /* exact match, add space */
  150. prefix[prefix_len++] = ' ';
  151. prefix[prefix_len] = '\0';
  152. }
  153. terminal_insert_into_command_line(prefix + len);
  154. tab_hit_count = 0;
  155. }
  156. }
  157. /* interface completion */
  158. static void command_completion(split_arg_t *arg)
  159. {
  160. struct command_completion_args args = {
  161. .arg = arg,
  162. .typed = arg->ntcopy,
  163. .func = command_name
  164. };
  165. tab_completion(&args);
  166. }
  167. /* method completion */
  168. static void method_completion(const struct interface *iface, split_arg_t *arg)
  169. {
  170. struct command_completion_args args = {
  171. .arg = arg,
  172. .typed = arg->next->ntcopy,
  173. .func = methods_name,
  174. .user = (void *) iface
  175. };
  176. if (iface == NULL)
  177. return;
  178. tab_completion(&args);
  179. }
  180. static const char *bold = "\x1b[1m";
  181. static const char *normal = "\x1b[0m";
  182. static bool find_nth_argument(const char *str, int n, const char **s,
  183. const char **e)
  184. {
  185. const char *p = str;
  186. int argc = 0;
  187. *e = NULL;
  188. while (p != NULL && *p != 0) {
  189. while (isspace(*p))
  190. ++p;
  191. if (n == argc)
  192. *s = p;
  193. if (*p == '[') {
  194. p = strchr(p, ']');
  195. if (p != NULL)
  196. *e = ++p;
  197. } else if (*p == '<') {
  198. p = strchr(p, '>');
  199. if (p != NULL)
  200. *e = ++p;
  201. } else {
  202. *e = strchr(p, ' ');
  203. if (*e == NULL)
  204. *e = p + strlen(p);
  205. p = *e;
  206. }
  207. if (n == argc)
  208. break;
  209. argc++;
  210. *e = NULL;
  211. }
  212. return *e != NULL;
  213. }
  214. /* prints short help on method for interface */
  215. static void method_help(struct command_completion_args *args)
  216. {
  217. int argc;
  218. const split_arg_t *arg = args->arg;
  219. const char *sb = NULL;
  220. const char *eb = NULL;
  221. const char *arg1 = "";
  222. int arg1_size = 0; /* size of method field (for methods > 0) */
  223. if (args->user_help == NULL)
  224. return;
  225. for (argc = 0; arg != NULL; argc++)
  226. arg = arg->next;
  227. /* Check if this is method from interface */
  228. if (get_command(args->arg->ntcopy) == NULL) {
  229. /* if so help is missing interface and method name */
  230. arg1 = args->arg->next->ntcopy;
  231. arg1_size = strlen(arg1) + 1;
  232. }
  233. find_nth_argument(args->user_help, argc - (arg1_size ? 3 : 2),
  234. &sb, &eb);
  235. if (eb != NULL)
  236. haltest_info("%s %-*s%.*s%s%.*s%s%s\n", args->arg->ntcopy,
  237. arg1_size, arg1, (int) (sb - args->user_help),
  238. args->user_help, bold, (int) (eb - sb),
  239. sb, normal, eb);
  240. else
  241. haltest_info("%s %-*s%s\n", args->arg->ntcopy,
  242. arg1_size, arg1, args->user_help);
  243. }
  244. /* So we have empty enumeration */
  245. static const char *return_null(void *user, int i)
  246. {
  247. return NULL;
  248. }
  249. /*
  250. * parameter completion function
  251. * argc - number of elements in arg list
  252. * arg - list of arguments
  253. * method - method to get completion from (can be NULL)
  254. */
  255. static void param_completion(int argc, const split_arg_t *arg,
  256. const struct method *method, int hlpix)
  257. {
  258. int i;
  259. const char *argv[argc];
  260. const split_arg_t *tmp = arg;
  261. struct command_completion_args args = {
  262. .arg = arg,
  263. .func = return_null
  264. };
  265. /* prepare standard argv from arg */
  266. for (i = 0; i < argc; ++i) {
  267. argv[i] = tmp->ntcopy;
  268. tmp = tmp->next;
  269. }
  270. if (method != NULL && method->complete != NULL) {
  271. /* ask method for completion function */
  272. method->complete(argc, argv, &args.func, &args.user);
  273. }
  274. /* If method provided enumeration function call try to complete */
  275. if (args.func != NULL) {
  276. args.typed = argv[argc - 1];
  277. args.help = method_help;
  278. args.user_help = method ? method->help : NULL;
  279. tab_completion(&args);
  280. }
  281. }
  282. /*
  283. * This method gets called when user tapped tab key.
  284. * line - points to command line
  285. * len - size of line that should be used for completions. This should be
  286. * cursor position during tab hit.
  287. */
  288. void process_tab(const char *line, int len)
  289. {
  290. int argc;
  291. static split_arg_t buf[(LINE_BUF_MAX * 2) / sizeof(split_arg_t)];
  292. const struct method *method;
  293. argc = split_command(line, len, buf, sizeof(buf));
  294. tab_hit_count++;
  295. if (argc == 0)
  296. return;
  297. if (argc == 1) {
  298. command_completion(buf);
  299. return;
  300. }
  301. method = get_command(buf[0].ntcopy);
  302. if (method != NULL) {
  303. param_completion(argc, buf, method, 1);
  304. } else if (argc == 2) {
  305. method_completion(get_interface(buf[0].ntcopy), buf);
  306. } else {
  307. /* Find method for <interface, name> pair */
  308. method = get_interface_method(buf[0].ntcopy,
  309. buf[0].next->ntcopy);
  310. param_completion(argc, buf, method, 2);
  311. }
  312. }