1 /* 2 * Copyright 2010-2011 Calxeda, Inc. 3 * 4 * This program is free software; you can redistribute it and/or modify it 5 * under the terms of the GNU General Public License as published by the Free 6 * Software Foundation; either version 2 of the License, or (at your option) 7 * any later version. 8 * 9 * This program is distributed in the hope it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 * more details. 13 * 14 * You should have received a copy of the GNU General Public License along with 15 * this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 #include <common.h> 19 #include <malloc.h> 20 #include <errno.h> 21 #include <linux/list.h> 22 23 #include "menu.h" 24 25 /* 26 * Internally, each item in a menu is represented by a struct menu_item. 27 * 28 * These items will be alloc'd and initialized by menu_item_add and destroyed 29 * by menu_item_destroy, and the consumer of the interface never sees that 30 * this struct is used at all. 31 */ 32 struct menu_item { 33 char *key; 34 void *data; 35 struct list_head list; 36 }; 37 38 /* 39 * The menu is composed of a list of items along with settings and callbacks 40 * provided by the user. An incomplete definition of this struct is available 41 * in menu.h, but the full definition is here to prevent consumers from 42 * relying on its contents. 43 */ 44 struct menu { 45 struct menu_item *default_item; 46 int timeout; 47 char *title; 48 int prompt; 49 void (*item_data_print)(void *); 50 struct list_head items; 51 }; 52 53 /* 54 * An iterator function for menu items. callback will be called for each item 55 * in m, with m, a pointer to the item, and extra being passed to callback. If 56 * callback returns a value other than NULL, iteration stops and the value 57 * return by callback is returned from menu_items_iter. This allows it to be 58 * used for search type operations. It is also safe for callback to remove the 59 * item from the list of items. 60 */ 61 static inline void *menu_items_iter(struct menu *m, 62 void *(*callback)(struct menu *, struct menu_item *, void *), 63 void *extra) 64 { 65 struct list_head *pos, *n; 66 struct menu_item *item; 67 void *ret; 68 69 list_for_each_safe(pos, n, &m->items) { 70 item = list_entry(pos, struct menu_item, list); 71 72 ret = callback(m, item, extra); 73 74 if (ret) 75 return ret; 76 } 77 78 return NULL; 79 } 80 81 /* 82 * Print a menu_item. If the consumer provided an item_data_print function 83 * when creating the menu, call it with a pointer to the item's private data. 84 * Otherwise, print the key of the item. 85 */ 86 static inline void *menu_item_print(struct menu *m, 87 struct menu_item *item, 88 void *extra) 89 { 90 if (!m->item_data_print) 91 printf("%s\n", item->key); 92 else 93 m->item_data_print(item->data); 94 95 return NULL; 96 } 97 98 /* 99 * Free the memory used by a menu item. This includes the memory used by its 100 * key. 101 */ 102 static inline void *menu_item_destroy(struct menu *m, 103 struct menu_item *item, 104 void *extra) 105 { 106 if (item->key) 107 free(item->key); 108 109 free(item); 110 111 return NULL; 112 } 113 114 /* 115 * Display a menu so the user can make a choice of an item. First display its 116 * title, if any, and then each item in the menu. 117 */ 118 static inline void menu_display(struct menu *m) 119 { 120 if (m->title) 121 printf("%s:\n", m->title); 122 123 menu_items_iter(m, menu_item_print, NULL); 124 } 125 126 /* 127 * Check if an item's key matches a provided string, pointed to by extra. If 128 * extra is NULL, an item with a NULL key will match. Otherwise, the item's 129 * key has to match according to strcmp. 130 * 131 * This is called via menu_items_iter, so it returns a pointer to the item if 132 * the key matches, and returns NULL otherwise. 133 */ 134 static inline void *menu_item_key_match(struct menu *m, 135 struct menu_item *item, void *extra) 136 { 137 char *item_key = extra; 138 139 if (!item_key || !item->key) { 140 if (item_key == item->key) 141 return item; 142 143 return NULL; 144 } 145 146 if (strcmp(item->key, item_key) == 0) 147 return item; 148 149 return NULL; 150 } 151 152 /* 153 * Find the first item with a key matching item_key, if any exists. 154 */ 155 static inline struct menu_item *menu_item_by_key(struct menu *m, 156 char *item_key) 157 { 158 return menu_items_iter(m, menu_item_key_match, item_key); 159 } 160 161 /* 162 * Wait for the user to hit a key according to the timeout set for the menu. 163 * Returns 1 if the user hit a key, or 0 if the timeout expired. 164 */ 165 static inline int menu_interrupted(struct menu *m) 166 { 167 if (!m->timeout) 168 return 0; 169 170 if (abortboot(m->timeout/10)) 171 return 1; 172 173 return 0; 174 } 175 176 /* 177 * Checks whether or not the default menu item should be used without 178 * prompting for a user choice. If the menu is set to always prompt, or the 179 * user hits a key during the timeout period, return 0. Otherwise, return 1 to 180 * indicate we should use the default menu item. 181 */ 182 static inline int menu_use_default(struct menu *m) 183 { 184 return !m->prompt && !menu_interrupted(m); 185 } 186 187 /* 188 * Set *choice to point to the default item's data, if any default item was 189 * set, and returns 1. If no default item was set, returns -ENOENT. 190 */ 191 static inline int menu_default_choice(struct menu *m, void **choice) 192 { 193 if (m->default_item) { 194 *choice = m->default_item->data; 195 return 1; 196 } 197 198 return -ENOENT; 199 } 200 201 /* 202 * Displays the menu and asks the user to choose an item. *choice will point 203 * to the private data of the item the user chooses. The user makes a choice 204 * by inputting a string matching the key of an item. Invalid choices will 205 * cause the user to be prompted again, repeatedly, until the user makes a 206 * valid choice. The user can exit the menu without making a choice via ^c. 207 * 208 * Returns 1 if the user made a choice, or -EINTR if they bail via ^c. 209 */ 210 static inline int menu_interactive_choice(struct menu *m, void **choice) 211 { 212 char cbuf[CONFIG_SYS_CBSIZE]; 213 struct menu_item *choice_item = NULL; 214 int readret; 215 216 while (!choice_item) { 217 cbuf[0] = '\0'; 218 219 menu_display(m); 220 221 readret = readline_into_buffer("Enter choice: ", cbuf); 222 223 if (readret >= 0) { 224 choice_item = menu_item_by_key(m, cbuf); 225 226 if (!choice_item) 227 printf("%s not found\n", cbuf); 228 } else { 229 printf("^C\n"); 230 return -EINTR; 231 } 232 } 233 234 *choice = choice_item->data; 235 236 return 1; 237 } 238 239 /* 240 * menu_default_set() - Sets the default choice for the menu. This is safe to 241 * call more than once on a menu. 242 * 243 * m - Points to a menu created by menu_create(). 244 * 245 * item_key - Points to a string that, when compared using strcmp, matches the 246 * key for an existing item in the menu. 247 * 248 * Returns 1 if successful, -EINVAL if m is NULL, or -ENOENT if no item with a 249 * key matching item_key is found. 250 */ 251 int menu_default_set(struct menu *m, char *item_key) 252 { 253 struct menu_item *item; 254 255 if (!m) 256 return -EINVAL; 257 258 item = menu_item_by_key(m, item_key); 259 260 if (!item) 261 return -ENOENT; 262 263 m->default_item = item; 264 265 return 1; 266 } 267 268 /* 269 * menu_get_choice() - Returns the user's selected menu entry, or the default 270 * if the menu is set to not prompt or the timeout expires. This is safe to 271 * call more than once. 272 * 273 * m - Points to a menu created by menu_create(). 274 * 275 * choice - Points to a location that will store a pointer to the selected 276 * menu item. If no item is selected or there is an error, no value will be 277 * written at the location it points to. 278 * 279 * Returns 1 if successful, -EINVAL if m or choice is NULL, -ENOENT if no 280 * default has been set and the menu is set to not prompt or the timeout 281 * expires, or -EINTR if the user exits the menu via ^c. 282 */ 283 int menu_get_choice(struct menu *m, void **choice) 284 { 285 if (!m || !choice) 286 return -EINVAL; 287 288 if (menu_use_default(m)) 289 return menu_default_choice(m, choice); 290 291 return menu_interactive_choice(m, choice); 292 } 293 294 /* 295 * menu_item_add() - Adds or replaces a menu item. Note that this replaces the 296 * data of an item if it already exists, but doesn't change the order of the 297 * item. 298 * 299 * m - Points to a menu created by menu_create(). 300 * 301 * item_key - Points to a string that will uniquely identify the item. The 302 * string will be copied to internal storage, and is safe to discard after 303 * passing to menu_item_add. 304 * 305 * item_data - An opaque pointer associated with an item. It is never 306 * dereferenced internally, but will be passed to the item_data_print, and 307 * will be returned from menu_get_choice if the menu item is selected. 308 * 309 * Returns 1 if successful, -EINVAL if m is NULL, or -ENOMEM if there is 310 * insufficient memory to add the menu item. 311 */ 312 int menu_item_add(struct menu *m, char *item_key, void *item_data) 313 { 314 struct menu_item *item; 315 316 if (!m) 317 return -EINVAL; 318 319 item = menu_item_by_key(m, item_key); 320 321 if (item) { 322 item->data = item_data; 323 return 1; 324 } 325 326 item = malloc(sizeof *item); 327 if (!item) 328 return -ENOMEM; 329 330 item->key = strdup(item_key); 331 332 if (!item->key) { 333 free(item); 334 return -ENOMEM; 335 } 336 337 item->data = item_data; 338 339 list_add_tail(&item->list, &m->items); 340 341 return 1; 342 } 343 344 /* 345 * menu_create() - Creates a menu handle with default settings 346 * 347 * title - If not NULL, points to a string that will be displayed before the 348 * list of menu items. It will be copied to internal storage, and is safe to 349 * discard after passing to menu_create(). 350 * 351 * timeout - A delay in seconds to wait for user input. If 0, timeout is 352 * disabled, and the default choice will be returned unless prompt is 1. 353 * 354 * prompt - If 0, don't ask for user input unless there is an interrupted 355 * timeout. If 1, the user will be prompted for input regardless of the value 356 * of timeout. 357 * 358 * item_data_print - If not NULL, will be called for each item when the menu 359 * is displayed, with the pointer to the item's data passed as the argument. 360 * If NULL, each item's key will be printed instead. Since an item's key is 361 * what must be entered to select an item, the item_data_print function should 362 * make it obvious what the key for each entry is. 363 * 364 * Returns a pointer to the menu if successful, or NULL if there is 365 * insufficient memory available to create the menu. 366 */ 367 struct menu *menu_create(char *title, int timeout, int prompt, 368 void (*item_data_print)(void *)) 369 { 370 struct menu *m; 371 372 m = malloc(sizeof *m); 373 374 if (!m) 375 return NULL; 376 377 m->default_item = NULL; 378 m->prompt = prompt; 379 m->timeout = timeout; 380 m->item_data_print = item_data_print; 381 382 if (title) { 383 m->title = strdup(title); 384 if (!m->title) { 385 free(m); 386 return NULL; 387 } 388 } else 389 m->title = NULL; 390 391 392 INIT_LIST_HEAD(&m->items); 393 394 return m; 395 } 396 397 /* 398 * menu_destroy() - frees the memory used by a menu and its items. 399 * 400 * m - Points to a menu created by menu_create(). 401 * 402 * Returns 1 if successful, or -EINVAL if m is NULL. 403 */ 404 int menu_destroy(struct menu *m) 405 { 406 if (!m) 407 return -EINVAL; 408 409 menu_items_iter(m, menu_item_destroy, NULL); 410 411 if (m->title) 412 free(m->title); 413 414 free(m); 415 416 return 1; 417 } 418