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