menu_entry.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. /*
  2. * GRUB -- GRand Unified Bootloader
  3. * Copyright (C) 2009 Free Software Foundation, Inc.
  4. *
  5. * GRUB is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * GRUB is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include <grub/mm.h>
  19. #include <grub/env.h>
  20. #include <grub/lib.h>
  21. #include <grub/menu.h>
  22. #include <grub/misc.h>
  23. #include <grub/parser.h>
  24. #include <grub/command.h>
  25. #include <grub/menu_viewer.h>
  26. #include <grub/uitree.h>
  27. #include <grub/controller.h>
  28. GRUB_EXPORT(grub_menu_entry_add);
  29. GRUB_EXPORT(grub_menu_execute);
  30. GRUB_EXPORT(grub_menu_key2name);
  31. GRUB_EXPORT(grub_menu_name2key);
  32. static void
  33. free_menu (grub_menu_t menu)
  34. {
  35. grub_menu_entry_t entry;
  36. if (! menu)
  37. return;
  38. entry = menu->entry_list;
  39. while (entry)
  40. {
  41. grub_menu_entry_t next_entry = entry->next;
  42. grub_free ((void *) entry->title);
  43. grub_free ((void *) entry->sourcecode);
  44. grub_free ((void *) entry->group);
  45. entry = next_entry;
  46. }
  47. grub_free (menu);
  48. grub_env_unset_menu ();
  49. }
  50. static void
  51. free_menu_entry_classes (struct grub_menu_entry_class *head)
  52. {
  53. /* Free all the classes. */
  54. while (head)
  55. {
  56. struct grub_menu_entry_class *next;
  57. grub_free (head->name);
  58. next = head->next;
  59. grub_free (head);
  60. head = next;
  61. }
  62. }
  63. static struct
  64. {
  65. char *name;
  66. int key;
  67. } hotkey_aliases[] =
  68. {
  69. {"backspace", '\b'},
  70. {"tab", '\t'},
  71. {"delete", GRUB_TERM_DC}
  72. };
  73. /* Add a menu entry to the current menu context (as given by the environment
  74. variable data slot `menu'). As the configuration file is read, the script
  75. parser calls this when a menu entry is to be created. */
  76. grub_err_t
  77. grub_menu_entry_add (int argc, const char **args, const char *sourcecode)
  78. {
  79. const char *menutitle = 0;
  80. const char *menusourcecode;
  81. grub_menu_t menu;
  82. grub_menu_entry_t *last;
  83. int failed = 0;
  84. int i;
  85. struct grub_menu_entry_class *classes_head; /* Dummy head node for list. */
  86. struct grub_menu_entry_class *classes_tail;
  87. char *users = NULL;
  88. const char *group = NULL;
  89. int hotkey = 0;
  90. /* Allocate dummy head node for class list. */
  91. classes_head = grub_zalloc (sizeof (struct grub_menu_entry_class));
  92. if (! classes_head)
  93. return grub_errno;
  94. classes_tail = classes_head;
  95. menu = grub_env_get_menu ();
  96. if (! menu)
  97. return grub_error (GRUB_ERR_MENU, "no menu context");
  98. last = &menu->entry_list;
  99. menusourcecode = grub_strdup (sourcecode);
  100. if (! menusourcecode)
  101. return grub_errno;
  102. /* Parse menu arguments. */
  103. for (i = 0; i < argc; i++)
  104. {
  105. /* Capture arguments. */
  106. if (grub_strncmp ("--", args[i], 2) == 0)
  107. {
  108. const char *arg = &args[i][2];
  109. /* Handle menu class. */
  110. if (grub_strcmp(arg, "class") == 0)
  111. {
  112. char *class_name;
  113. struct grub_menu_entry_class *new_class;
  114. i++;
  115. class_name = grub_strdup (args[i]);
  116. if (! class_name)
  117. {
  118. failed = 1;
  119. break;
  120. }
  121. /* Create a new class and add it at the tail of the list. */
  122. new_class = grub_zalloc (sizeof (struct grub_menu_entry_class));
  123. if (! new_class)
  124. {
  125. grub_free (class_name);
  126. failed = 1;
  127. break;
  128. }
  129. /* Fill in the new class node. */
  130. new_class->name = class_name;
  131. /* Link the tail to it, and make it the new tail. */
  132. classes_tail->next = new_class;
  133. classes_tail = new_class;
  134. continue;
  135. }
  136. else if (grub_strcmp(arg, "users") == 0)
  137. {
  138. i++;
  139. users = grub_strdup (args[i]);
  140. if (! users)
  141. {
  142. failed = 1;
  143. break;
  144. }
  145. continue;
  146. }
  147. else if (grub_strcmp(arg, "hotkey") == 0)
  148. {
  149. unsigned j;
  150. i++;
  151. if (args[i][1] == 0)
  152. {
  153. hotkey = args[i][0];
  154. continue;
  155. }
  156. for (j = 0; j < ARRAY_SIZE (hotkey_aliases); j++)
  157. if (grub_strcmp (args[i], hotkey_aliases[j].name) == 0)
  158. {
  159. hotkey = hotkey_aliases[j].key;
  160. break;
  161. }
  162. if (j < ARRAY_SIZE (hotkey_aliases))
  163. continue;
  164. failed = 1;
  165. grub_error (GRUB_ERR_MENU,
  166. "Invalid hotkey: '%s'.", args[i]);
  167. break;
  168. }
  169. else if (grub_strcmp(arg, "group") == 0)
  170. {
  171. i++;
  172. grub_free ((void *) group);
  173. group = grub_strdup (args[i]);
  174. if (! group)
  175. {
  176. failed = 1;
  177. break;
  178. }
  179. continue;
  180. }
  181. else
  182. {
  183. /* Handle invalid argument. */
  184. failed = 1;
  185. grub_error (GRUB_ERR_MENU,
  186. "invalid argument for menuentry: %s", args[i]);
  187. break;
  188. }
  189. }
  190. /* Capture title. */
  191. if (! menutitle)
  192. {
  193. menutitle = grub_strdup (args[i]);
  194. }
  195. else
  196. {
  197. failed = 1;
  198. grub_error (GRUB_ERR_MENU,
  199. "too many titles for menuentry: %s", args[i]);
  200. break;
  201. }
  202. }
  203. /* Validate arguments. */
  204. if ((! failed) && (! menutitle))
  205. {
  206. grub_error (GRUB_ERR_MENU, "menuentry is missing title");
  207. failed = 1;
  208. }
  209. /* If argument parsing failed, free any allocated resources. */
  210. if (failed)
  211. {
  212. free_menu_entry_classes (classes_head);
  213. grub_free ((void *) menutitle);
  214. grub_free ((void *) menusourcecode);
  215. /* Here we assume that grub_error has been used to specify failure details. */
  216. return grub_errno;
  217. }
  218. /* Add the menu entry at the end of the list. */
  219. while (*last)
  220. last = &(*last)->next;
  221. *last = grub_zalloc (sizeof (**last));
  222. if (! *last)
  223. {
  224. free_menu_entry_classes (classes_head);
  225. grub_free ((void *) menutitle);
  226. grub_free ((void *) menusourcecode);
  227. return grub_errno;
  228. }
  229. (*last)->title = menutitle;
  230. (*last)->hotkey = hotkey;
  231. (*last)->classes = classes_head;
  232. if (users)
  233. (*last)->restricted = 1;
  234. (*last)->users = users;
  235. (*last)->sourcecode = menusourcecode;
  236. (*last)->group = group;
  237. menu->size++;
  238. return grub_errno;
  239. }
  240. struct read_config_file_closure
  241. {
  242. grub_file_t file;
  243. grub_parser_t old_parser;
  244. };
  245. static grub_err_t
  246. getline (char **line, int cont __attribute__ ((unused)), void *closure)
  247. {
  248. struct read_config_file_closure *c = closure;
  249. while (1)
  250. {
  251. char *buf;
  252. *line = buf = grub_getline (c->file);
  253. if (! buf)
  254. return grub_errno;
  255. if (buf[0] == '#')
  256. {
  257. if (buf[1] == '!')
  258. {
  259. grub_parser_t parser;
  260. grub_named_list_t list;
  261. buf += 2;
  262. while (grub_isspace (*buf))
  263. buf++;
  264. if (! c->old_parser)
  265. c->old_parser = grub_parser_get_current ();
  266. list = GRUB_AS_NAMED_LIST (grub_parser_class.handler_list);
  267. parser = grub_named_list_find (list, buf);
  268. if (parser)
  269. grub_parser_set_current (parser);
  270. else
  271. {
  272. char cmd_name[8 + grub_strlen (buf)];
  273. /* Perhaps it's not loaded yet, try the autoload
  274. command. */
  275. grub_strcpy (cmd_name, "parser.");
  276. grub_strcat (cmd_name, buf);
  277. grub_command_execute (cmd_name, 0, 0);
  278. }
  279. }
  280. grub_free (*line);
  281. }
  282. else
  283. break;
  284. }
  285. return GRUB_ERR_NONE;
  286. }
  287. static void
  288. read_config_file (const char *config)
  289. {
  290. grub_menu_t newmenu;
  291. struct read_config_file_closure c;
  292. c.old_parser = 0;
  293. newmenu = grub_env_get_menu ();
  294. if (! newmenu)
  295. {
  296. newmenu = grub_zalloc (sizeof (*newmenu));
  297. if (! newmenu)
  298. return;
  299. grub_env_set_menu (newmenu);
  300. }
  301. /* Try to open the config file. */
  302. c.file = grub_file_open (config);
  303. if (! c.file)
  304. return;
  305. while (1)
  306. {
  307. char *line;
  308. /* Print an error, if any. */
  309. grub_print_error ();
  310. grub_errno = GRUB_ERR_NONE;
  311. if ((getline (&line, 0, &c)) || (! line))
  312. break;
  313. grub_parser_get_current ()->parse_line (line, getline, &c);
  314. grub_free (line);
  315. }
  316. grub_file_close (c.file);
  317. if (c.old_parser)
  318. grub_parser_set_current (c.old_parser);
  319. }
  320. /* Read the config file CONFIG and execute the menu interface or
  321. the command line interface if BATCH is false. */
  322. void
  323. grub_menu_execute (const char *config, int nested, int batch)
  324. {
  325. grub_menu_t menu;
  326. int has_menu;
  327. if (config)
  328. {
  329. read_config_file (config);
  330. /* Ignore any error. */
  331. grub_errno = GRUB_ERR_NONE;
  332. }
  333. menu = grub_env_get_menu ();
  334. has_menu = (menu && menu->size);
  335. if (! batch)
  336. {
  337. if (has_menu)
  338. {
  339. if (grub_controller_show_menu (menu, nested))
  340. {
  341. grub_errno = 0;
  342. grub_command_execute ("controller.normal", 0, 0);
  343. grub_controller_show_menu (menu, nested);
  344. }
  345. free_menu (menu);
  346. }
  347. }
  348. if ((! has_menu) && (! nested))
  349. grub_controller_show_menu (0, 0);
  350. }
  351. static const char *key_list[] =
  352. {
  353. "\002left",
  354. "\006right",
  355. "\020up",
  356. "\016down",
  357. "\001home",
  358. "\005end",
  359. "\004delete",
  360. "\007page_up",
  361. "\003page_down",
  362. "\033esc",
  363. "\011tab",
  364. "\010backspace",
  365. "\renter",
  366. " space",
  367. 0
  368. };
  369. const char *
  370. grub_menu_key2name (int key)
  371. {
  372. static char keyname[sizeof ("ctrl-a")];
  373. const char **p;
  374. for (p = key_list; *p; p++)
  375. {
  376. if (key == p[0][0])
  377. return p[0] + 1;
  378. }
  379. keyname[0] = 0;
  380. if ((key > 32) && (key < 127))
  381. {
  382. keyname[0] = key;
  383. keyname[1] = 0;
  384. }
  385. else if ((key >= GRUB_TERM_CTRL_A) && (key <= GRUB_TERM_CTRL_Z))
  386. grub_snprintf (keyname, sizeof (keyname), "ctrl-%c",
  387. key - GRUB_TERM_CTRL_A + 'a');
  388. else if ((key >= GRUB_TERM_F1) && (key <= GRUB_TERM_F10))
  389. grub_snprintf (keyname, sizeof (keyname), "f%d", key - GRUB_TERM_F1 + 1);
  390. return (keyname[0]) ? keyname : 0;
  391. }
  392. int
  393. grub_menu_name2key (const char *name)
  394. {
  395. const char **p;
  396. for (p = key_list; *p; p++)
  397. {
  398. if (! grub_strcmp (name, p[0] + 1))
  399. return p[0][0];
  400. }
  401. if ((name[0] > 32) && (name[0] < 127) && (name[1] == 0))
  402. return name[0];
  403. else if ((! grub_memcmp (name, "ctrl-", 5)) &&
  404. (name[5] >= 'a') && (name[5] <= 'z'))
  405. return name[5] - 'a' + GRUB_TERM_CTRL_A;
  406. else if (name[0] == 'f')
  407. {
  408. int num;
  409. num = grub_strtol (name + 1, 0, 0);
  410. if ((num >= 1) && (num <= 10))
  411. return GRUB_TERM_F1 + num - 1;
  412. }
  413. return 0;
  414. }