1753a6d45SSherry Moore /*
2753a6d45SSherry Moore  * CDDL HEADER START
3753a6d45SSherry Moore  *
4753a6d45SSherry Moore  * The contents of this file are subject to the terms of the
5753a6d45SSherry Moore  * Common Development and Distribution License (the "License").
6753a6d45SSherry Moore  * You may not use this file except in compliance with the License.
7753a6d45SSherry Moore  *
8753a6d45SSherry Moore  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9753a6d45SSherry Moore  * or http://www.opensolaris.org/os/licensing.
10753a6d45SSherry Moore  * See the License for the specific language governing permissions
11753a6d45SSherry Moore  * and limitations under the License.
12753a6d45SSherry Moore  *
13753a6d45SSherry Moore  * When distributing Covered Code, include this CDDL HEADER in each
14753a6d45SSherry Moore  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15753a6d45SSherry Moore  * If applicable, add the following below this CDDL HEADER, with the
16753a6d45SSherry Moore  * fields enclosed by brackets "[]" replaced with your own identifying
17753a6d45SSherry Moore  * information: Portions Copyright [yyyy] [name of copyright owner]
18753a6d45SSherry Moore  *
19753a6d45SSherry Moore  * CDDL HEADER END
20753a6d45SSherry Moore  */
21753a6d45SSherry Moore /*
22753a6d45SSherry Moore  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23753a6d45SSherry Moore  * Use is subject to license terms.
24753a6d45SSherry Moore  */
25753a6d45SSherry Moore 
26753a6d45SSherry Moore /*
27753a6d45SSherry Moore  * This file contains functions for manipulating the GRUB menu.
28753a6d45SSherry Moore  */
29753a6d45SSherry Moore #include <stdio.h>
30753a6d45SSherry Moore #include <errno.h>
31753a6d45SSherry Moore #include <stdlib.h>
32753a6d45SSherry Moore #include <string.h>
33753a6d45SSherry Moore #include <unistd.h>
34753a6d45SSherry Moore #include <sys/types.h>
35753a6d45SSherry Moore #include <sys/mount.h>
36753a6d45SSherry Moore #include <stdarg.h>
37753a6d45SSherry Moore #include <assert.h>
38753a6d45SSherry Moore #include <ctype.h>
39753a6d45SSherry Moore 
40753a6d45SSherry Moore #include "libgrub_impl.h"
41753a6d45SSherry Moore 
42753a6d45SSherry Moore static const grub_cmd_desc_t grub_cmd_descs[GRBM_CMD_NUM] = {
43753a6d45SSherry Moore #define	menu_cmd(cmd, num, flag, parsef)	{cmd, num, flag},
44753a6d45SSherry Moore #include "libgrub_cmd.def"
45753a6d45SSherry Moore };
46753a6d45SSherry Moore 
47753a6d45SSherry Moore static void
append_line(grub_menu_t * mp,grub_line_t * lp)48753a6d45SSherry Moore append_line(grub_menu_t *mp, grub_line_t *lp)
49753a6d45SSherry Moore {
50753a6d45SSherry Moore 	if (mp->gm_start == NULL) {
51753a6d45SSherry Moore 		mp->gm_start = lp;
52753a6d45SSherry Moore 	} else {
53753a6d45SSherry Moore 		mp->gm_end->gl_next = lp;
54753a6d45SSherry Moore 		lp->gl_prev = mp->gm_end;
55753a6d45SSherry Moore 	}
56753a6d45SSherry Moore 	mp->gm_end = lp;
57753a6d45SSherry Moore 	lp->gl_line_num = ++mp->gm_line_num;
58753a6d45SSherry Moore 	lp->gl_entry_num = GRUB_ENTRY_DEFAULT;
59753a6d45SSherry Moore }
60753a6d45SSherry Moore 
61753a6d45SSherry Moore static void
process_line(grub_menu_t * mp)62753a6d45SSherry Moore process_line(grub_menu_t *mp)
63753a6d45SSherry Moore {
64753a6d45SSherry Moore 	int	n;
65753a6d45SSherry Moore 	grub_line_t	*lp;
66753a6d45SSherry Moore 
67753a6d45SSherry Moore 	lp = mp->gm_end;
68753a6d45SSherry Moore 	n = sizeof (grub_cmd_descs) / sizeof (grub_cmd_descs[0]);
69753a6d45SSherry Moore 
70753a6d45SSherry Moore 	/* search through the table of known commands */
71753a6d45SSherry Moore 	while (n-- != 0 && strcmp(lp->gl_cmd, grub_cmd_descs[n].gcd_cmd) != 0)
72753a6d45SSherry Moore 		;
73753a6d45SSherry Moore 
74753a6d45SSherry Moore 	/* unknown command */
75753a6d45SSherry Moore 	if (n < 0)
76753a6d45SSherry Moore 		return;
77753a6d45SSherry Moore 
78753a6d45SSherry Moore 	/* we found command, fill lp fields */
79753a6d45SSherry Moore 	lp->gl_flags = grub_cmd_descs[n].gcd_flags;
80753a6d45SSherry Moore 	lp->gl_cmdtp = grub_cmd_descs[n].gcd_num;
81753a6d45SSherry Moore }
82753a6d45SSherry Moore 
83753a6d45SSherry Moore 
84753a6d45SSherry Moore static void
check_entry(grub_entry_t * ent)85753a6d45SSherry Moore check_entry(grub_entry_t *ent)
86753a6d45SSherry Moore {
87753a6d45SSherry Moore 	int i;
88753a6d45SSherry Moore 	uint_t emask;
89753a6d45SSherry Moore 	grub_line_t *lp;
90753a6d45SSherry Moore 	const grub_line_t * const lend = ent->ge_end->gl_next;
91753a6d45SSherry Moore 
92753a6d45SSherry Moore 	emask = 0;
93753a6d45SSherry Moore 	for (i = 0, lp = ent->ge_start; lend != lp; lp = lp->gl_next, ++i) {
94753a6d45SSherry Moore 		lp->gl_entry_num = ent->ge_entry_num;
95753a6d45SSherry Moore 		if (lp->gl_flags == GRUB_LINE_INVALID ||
96753a6d45SSherry Moore 		    lp->gl_flags == GRUB_LINE_GLOBAL) {
97753a6d45SSherry Moore 			emask |= 1 << i;
98753a6d45SSherry Moore 			lp->gl_flags = GRUB_LINE_INVALID;
99753a6d45SSherry Moore 		}
100753a6d45SSherry Moore 	}
101753a6d45SSherry Moore 
102753a6d45SSherry Moore 	if ((ent->ge_emask = emask) == 0)
103753a6d45SSherry Moore 		ent->ge_flags |= GRBM_VALID_FLAG;
104753a6d45SSherry Moore }
105753a6d45SSherry Moore 
106753a6d45SSherry Moore static int
add_entry(grub_menu_t * mp,grub_line_t * start,grub_line_t * end)107753a6d45SSherry Moore add_entry(grub_menu_t *mp, grub_line_t *start, grub_line_t *end)
108753a6d45SSherry Moore {
109753a6d45SSherry Moore 	grub_entry_t *ent;
110753a6d45SSherry Moore 
111753a6d45SSherry Moore 	if ((ent = calloc(1, sizeof (*ent))) == NULL)
112753a6d45SSherry Moore 		return (errno);
113753a6d45SSherry Moore 
114753a6d45SSherry Moore 	ent->ge_start = start;
115753a6d45SSherry Moore 	ent->ge_end = end;
116753a6d45SSherry Moore 
117753a6d45SSherry Moore 	if (mp->gm_ent_end == NULL) {
118753a6d45SSherry Moore 		mp->gm_ent_start = ent;
119753a6d45SSherry Moore 	} else {
120753a6d45SSherry Moore 		mp->gm_ent_end->ge_next = ent;
121753a6d45SSherry Moore 		ent->ge_prev = mp->gm_ent_end;
122753a6d45SSherry Moore 	}
123753a6d45SSherry Moore 	mp->gm_ent_end = ent;
124753a6d45SSherry Moore 	ent->ge_entry_num = mp->gm_entry_num++;
125753a6d45SSherry Moore 	ent->ge_menu = mp;
126753a6d45SSherry Moore 	return (0);
127753a6d45SSherry Moore }
128753a6d45SSherry Moore 
129753a6d45SSherry Moore static void
default_entry(grub_menu_t * mp)130753a6d45SSherry Moore default_entry(grub_menu_t *mp)
131753a6d45SSherry Moore {
132753a6d45SSherry Moore 	uint_t defent;
133753a6d45SSherry Moore 	grub_line_t *lp;
134753a6d45SSherry Moore 	grub_entry_t *ent;
135753a6d45SSherry Moore 
136753a6d45SSherry Moore 	defent = 0;
137753a6d45SSherry Moore 	lp = mp->gm_curdefault;
138753a6d45SSherry Moore 
139753a6d45SSherry Moore 	if (lp != NULL && lp->gl_flags == GRUB_LINE_GLOBAL &&
140753a6d45SSherry Moore 	    lp->gl_cmdtp == GRBM_DEFAULT_CMD) {
141753a6d45SSherry Moore 		defent  = strtoul(lp->gl_arg, NULL, 0);
142753a6d45SSherry Moore 		if (defent >= mp->gm_entry_num)
143753a6d45SSherry Moore 			defent = 0;
144753a6d45SSherry Moore 	}
145753a6d45SSherry Moore 
146753a6d45SSherry Moore 	for (ent = mp->gm_ent_start; ent != NULL && defent != ent->ge_entry_num;
147753a6d45SSherry Moore 	    ent = ent->ge_next)
148753a6d45SSherry Moore 		;
149753a6d45SSherry Moore 
150753a6d45SSherry Moore 	mp->gm_ent_default = ent;
151753a6d45SSherry Moore }
152753a6d45SSherry Moore 
153753a6d45SSherry Moore static void
free_line(grub_line_t * lp)154753a6d45SSherry Moore free_line(grub_line_t *lp)
155753a6d45SSherry Moore {
156753a6d45SSherry Moore 	if (lp == NULL)
157753a6d45SSherry Moore 		return;
158753a6d45SSherry Moore 
159753a6d45SSherry Moore 	free(lp->gl_cmd);
160753a6d45SSherry Moore 	free(lp->gl_sep);
161753a6d45SSherry Moore 	free(lp->gl_arg);
162753a6d45SSherry Moore 	free(lp->gl_line);
163753a6d45SSherry Moore 	free(lp);
164753a6d45SSherry Moore }
165753a6d45SSherry Moore 
166753a6d45SSherry Moore static void
free_linelist(grub_line_t * line)167753a6d45SSherry Moore free_linelist(grub_line_t *line)
168753a6d45SSherry Moore {
169753a6d45SSherry Moore 	grub_line_t *lp;
170753a6d45SSherry Moore 
171753a6d45SSherry Moore 	if (line == NULL)
172753a6d45SSherry Moore 		return;
173753a6d45SSherry Moore 
174753a6d45SSherry Moore 	while (line) {
175753a6d45SSherry Moore 		lp = line;
176753a6d45SSherry Moore 		line = lp->gl_next;
177753a6d45SSherry Moore 		free_line(lp);
178753a6d45SSherry Moore 	}
179753a6d45SSherry Moore }
180753a6d45SSherry Moore 
181753a6d45SSherry Moore static void
free_entries(grub_menu_t * mp)182753a6d45SSherry Moore free_entries(grub_menu_t *mp)
183753a6d45SSherry Moore {
184753a6d45SSherry Moore 	grub_entry_t *ent, *tmp;
185753a6d45SSherry Moore 
186753a6d45SSherry Moore 	if (mp == NULL)
187753a6d45SSherry Moore 		return;
188753a6d45SSherry Moore 
189753a6d45SSherry Moore 	for (ent = mp->gm_ent_start; (tmp = ent) != NULL;
190753a6d45SSherry Moore 	    ent = tmp->ge_next, free(tmp))
191753a6d45SSherry Moore 		;
192753a6d45SSherry Moore 
193753a6d45SSherry Moore 	mp->gm_ent_start = NULL;
194753a6d45SSherry Moore 	mp->gm_ent_end = NULL;
195753a6d45SSherry Moore }
196753a6d45SSherry Moore 
197753a6d45SSherry Moore static int
grub_menu_append_line(grub_menu_t * mp,const char * line)198753a6d45SSherry Moore grub_menu_append_line(grub_menu_t *mp, const char *line)
199753a6d45SSherry Moore {
200753a6d45SSherry Moore 	int rc;
201753a6d45SSherry Moore 	size_t n;
202753a6d45SSherry Moore 	grub_line_t *lp;
203753a6d45SSherry Moore 
204753a6d45SSherry Moore 	if (line == NULL)
205753a6d45SSherry Moore 		return (EINVAL);
206753a6d45SSherry Moore 
207753a6d45SSherry Moore 	rc = 0;
208753a6d45SSherry Moore 	lp = NULL;
209753a6d45SSherry Moore 	if ((lp = calloc(1, sizeof (*lp))) == NULL ||
210753a6d45SSherry Moore 	    (lp->gl_line = strdup(line)) == NULL) {
211753a6d45SSherry Moore 		free(lp);
212753a6d45SSherry Moore 		return (errno);
213753a6d45SSherry Moore 	}
214753a6d45SSherry Moore 
215753a6d45SSherry Moore 	/* skip initial white space */
216753a6d45SSherry Moore 	line += strspn(line, " \t");
217753a6d45SSherry Moore 
218753a6d45SSherry Moore 	/* process comment line */
219753a6d45SSherry Moore 	if (line[0] == '#') {
220753a6d45SSherry Moore 		if ((lp->gl_cmd =
221753a6d45SSherry Moore 		    strdup(grub_cmd_descs[GRBM_COMMENT_CMD].gcd_cmd)) == NULL ||
222753a6d45SSherry Moore 		    (lp->gl_sep =
223753a6d45SSherry Moore 		    strdup(grub_cmd_descs[GRBM_EMPTY_CMD].gcd_cmd)) == NULL ||
224753a6d45SSherry Moore 		    (lp->gl_arg = strdup(line + 1)) == NULL)
225753a6d45SSherry Moore 			rc = errno;
226753a6d45SSherry Moore 	} else {
227753a6d45SSherry Moore 		/* get command */
228753a6d45SSherry Moore 		n = strcspn(line, " \t=");
229753a6d45SSherry Moore 		if ((lp->gl_cmd = malloc(n + 1)) == NULL)
230753a6d45SSherry Moore 			rc = errno;
231753a6d45SSherry Moore 		else
232753a6d45SSherry Moore 			(void) strlcpy(lp->gl_cmd, line, n + 1);
233753a6d45SSherry Moore 
234753a6d45SSherry Moore 		line += n;
235753a6d45SSherry Moore 
236753a6d45SSherry Moore 		/* get separator */
237753a6d45SSherry Moore 		n = strspn(line, " \t=");
238753a6d45SSherry Moore 		if ((lp->gl_sep = malloc(n + 1)) == NULL)
239753a6d45SSherry Moore 			rc = errno;
240753a6d45SSherry Moore 		else
241753a6d45SSherry Moore 			(void) strlcpy(lp->gl_sep, line, n + 1);
242753a6d45SSherry Moore 
243753a6d45SSherry Moore 		line += n;
244753a6d45SSherry Moore 
245753a6d45SSherry Moore 		/* get arguments */
246753a6d45SSherry Moore 		if ((lp->gl_arg = strdup(line)) == NULL)
247753a6d45SSherry Moore 			rc = errno;
248753a6d45SSherry Moore 	}
249753a6d45SSherry Moore 
250753a6d45SSherry Moore 	if (rc != 0) {
251753a6d45SSherry Moore 		free_line(lp);
252753a6d45SSherry Moore 		return (rc);
253753a6d45SSherry Moore 	}
254753a6d45SSherry Moore 
255753a6d45SSherry Moore 	append_line(mp, lp);
256753a6d45SSherry Moore 	process_line(mp);
257753a6d45SSherry Moore 	return (0);
258753a6d45SSherry Moore }
259753a6d45SSherry Moore 
260753a6d45SSherry Moore static int
grub_menu_process(grub_menu_t * mp)261753a6d45SSherry Moore grub_menu_process(grub_menu_t *mp)
262753a6d45SSherry Moore {
263753a6d45SSherry Moore 	int ret;
264753a6d45SSherry Moore 	grub_entry_t *ent;
265753a6d45SSherry Moore 	grub_line_t *line, *start;
266753a6d45SSherry Moore 
267753a6d45SSherry Moore 	/* Free remaininig entries, if any */
268753a6d45SSherry Moore 	free_entries(mp);
269753a6d45SSherry Moore 
270753a6d45SSherry Moore 	/*
271753a6d45SSherry Moore 	 * Walk through lines, till first 'title' command is encountered.
272753a6d45SSherry Moore 	 * Initialize globals.
273753a6d45SSherry Moore 	 */
274753a6d45SSherry Moore 	for (line = mp->gm_start; line != NULL; line = line->gl_next) {
275753a6d45SSherry Moore 		if (line->gl_flags == GRUB_LINE_GLOBAL &&
276753a6d45SSherry Moore 		    line->gl_cmdtp == GRBM_DEFAULT_CMD)
277753a6d45SSherry Moore 			mp->gm_curdefault = line;
278753a6d45SSherry Moore 		else if (line->gl_cmdtp == GRBM_TITLE_CMD)
279753a6d45SSherry Moore 			break;
280753a6d45SSherry Moore 	}
281753a6d45SSherry Moore 
282753a6d45SSherry Moore 	/*
283753a6d45SSherry Moore 	 * Walk through remaining lines and recreate menu entries.
284753a6d45SSherry Moore 	 */
285753a6d45SSherry Moore 	for (start = NULL; line != NULL; line = line->gl_next) {
286753a6d45SSherry Moore 		if (line->gl_cmdtp == GRBM_TITLE_CMD) {
287753a6d45SSherry Moore 			/* is first entry */
288753a6d45SSherry Moore 			if (start != NULL &&
289753a6d45SSherry Moore 			    (ret = add_entry(mp, start, line->gl_prev)) != 0)
290753a6d45SSherry Moore 				return (ret);
291753a6d45SSherry Moore 			start = line;
292753a6d45SSherry Moore 		}
293753a6d45SSherry Moore 	}
294753a6d45SSherry Moore 
295753a6d45SSherry Moore 	/* Add last entry */
296753a6d45SSherry Moore 	if (start != NULL && (ret = add_entry(mp, start, mp->gm_end)) != 0)
297753a6d45SSherry Moore 		return (ret);
298753a6d45SSherry Moore 
299753a6d45SSherry Moore 	for (ent = mp->gm_ent_start; NULL != ent; ent = ent->ge_next)
300753a6d45SSherry Moore 		check_entry(ent);
301753a6d45SSherry Moore 
302753a6d45SSherry Moore 	default_entry(mp);
303753a6d45SSherry Moore 
304753a6d45SSherry Moore 	return (0);
305753a6d45SSherry Moore }
306753a6d45SSherry Moore 
307753a6d45SSherry Moore static int
grub_fs_init(grub_fs_t * fs)308753a6d45SSherry Moore grub_fs_init(grub_fs_t *fs)
309753a6d45SSherry Moore {
310753a6d45SSherry Moore 	assert(fs);
311753a6d45SSherry Moore 	if ((fs->gf_lzfh = libzfs_init()) == NULL ||
312753a6d45SSherry Moore 	    (fs->gf_diroot = di_init("/", DINFOCPYALL | DINFOPATH))
313753a6d45SSherry Moore 	    == DI_NODE_NIL ||
314753a6d45SSherry Moore 	    (fs->gf_dvlh = di_devlink_init(NULL, 0)) == DI_LINK_NIL) {
315753a6d45SSherry Moore 		return (EG_INITFS);
316753a6d45SSherry Moore 	}
317753a6d45SSherry Moore 	return (0);
318753a6d45SSherry Moore }
319753a6d45SSherry Moore 
320753a6d45SSherry Moore static void
grub_fs_fini(grub_fs_t * fs)321753a6d45SSherry Moore grub_fs_fini(grub_fs_t *fs)
322753a6d45SSherry Moore {
323753a6d45SSherry Moore 	if (fs == NULL)
324753a6d45SSherry Moore 		return;
325753a6d45SSherry Moore 
326753a6d45SSherry Moore 	if (fs->gf_dvlh != DI_LINK_NIL)
327753a6d45SSherry Moore 		(void) di_devlink_fini(&fs->gf_dvlh);
328753a6d45SSherry Moore 	if (fs->gf_diroot != DI_NODE_NIL)
329753a6d45SSherry Moore 		di_fini(fs->gf_diroot);
330753a6d45SSherry Moore 	if (fs->gf_lzfh != NULL)
331753a6d45SSherry Moore 		libzfs_fini(fs->gf_lzfh);
332753a6d45SSherry Moore 	(void) memset(fs, 0, sizeof (*fs));
333753a6d45SSherry Moore }
334753a6d45SSherry Moore 
335753a6d45SSherry Moore /*
336753a6d45SSherry Moore  * Reads and parses GRUB menu file into a grub_menu_t data structure.
337753a6d45SSherry Moore  * If grub_menu_path file path is NULL, will use 'currently active'
338753a6d45SSherry Moore  * GRUB menu file.
339753a6d45SSherry Moore  *
340753a6d45SSherry Moore  * Memory for the menu data structure is allocated within the routine.
341753a6d45SSherry Moore  * Caller must call grub_menu_fini() to release memory after calling
342753a6d45SSherry Moore  * grub_menu_init().
343753a6d45SSherry Moore  */
344753a6d45SSherry Moore int
grub_menu_init(const char * path,grub_menu_t ** menup)345753a6d45SSherry Moore grub_menu_init(const char *path, grub_menu_t **menup)
346753a6d45SSherry Moore {
347753a6d45SSherry Moore 	FILE *fp;
348753a6d45SSherry Moore 	char *cp;
349753a6d45SSherry Moore 	grub_menu_t *mp;
350753a6d45SSherry Moore 	int len, n, ret;
351753a6d45SSherry Moore 	char buf[GRBM_MAXLINE];
352753a6d45SSherry Moore 
353753a6d45SSherry Moore 	if (menup == NULL)
354753a6d45SSherry Moore 		return (EINVAL);
355753a6d45SSherry Moore 
356753a6d45SSherry Moore 	/*
357753a6d45SSherry Moore 	 * Allocate space, perform initialization
358753a6d45SSherry Moore 	 */
359753a6d45SSherry Moore 	if ((mp = calloc(1, sizeof (*mp))) == NULL) {
360753a6d45SSherry Moore 		*menup = mp;
361753a6d45SSherry Moore 		return (errno);
362753a6d45SSherry Moore 	}
363753a6d45SSherry Moore 
364753a6d45SSherry Moore 	if ((ret = grub_fs_init(&mp->gm_fs)) != 0 ||
365753a6d45SSherry Moore 	    (ret = grub_current_root(&mp->gm_fs, &mp->gm_root)) != 0)
366753a6d45SSherry Moore 		goto err_out1;
367753a6d45SSherry Moore 
368753a6d45SSherry Moore 	if (path == NULL) {
369753a6d45SSherry Moore 		/*
370753a6d45SSherry Moore 		 * Use default grub-menu.
371753a6d45SSherry Moore 		 * If top dataset is not mounted, mount it now.
372753a6d45SSherry Moore 		 */
373753a6d45SSherry Moore 		if (mp->gm_root.gr_fs[GRBM_FS_TOP].gfs_mountp[0] == 0) {
374753a6d45SSherry Moore 			if ((ret = grub_fsd_mount_tmp(mp->gm_root.gr_fs +
375753a6d45SSherry Moore 			    GRBM_FS_TOP, mp->gm_root.gr_fstyp)) != 0)
376753a6d45SSherry Moore 				goto err_out1;
377753a6d45SSherry Moore 		}
378753a6d45SSherry Moore 		(void) snprintf(mp->gm_path, sizeof (mp->gm_path),
379753a6d45SSherry Moore 		    "%s/%s", mp->gm_root.gr_fs[GRBM_FS_TOP].gfs_mountp,
380753a6d45SSherry Moore 		    GRUB_MENU);
381753a6d45SSherry Moore 	} else {
382753a6d45SSherry Moore 		(void) strlcpy(mp->gm_path, path, sizeof (mp->gm_path));
383753a6d45SSherry Moore 	}
384753a6d45SSherry Moore 
385753a6d45SSherry Moore 	if ((fp = fopen(mp->gm_path, "r")) == NULL) {
386753a6d45SSherry Moore 		ret = errno;
387753a6d45SSherry Moore 		goto err_out1;
388753a6d45SSherry Moore 	}
389753a6d45SSherry Moore 
390753a6d45SSherry Moore 	cp = buf;
391753a6d45SSherry Moore 	len = sizeof (buf);
392753a6d45SSherry Moore 
393753a6d45SSherry Moore 	while (fgets(cp, len, fp) != NULL) {
394753a6d45SSherry Moore 
395753a6d45SSherry Moore 		if (IS_LINE2BIG(cp, len, n)) {
396753a6d45SSherry Moore 			ret = E2BIG;
397753a6d45SSherry Moore 			break;
398753a6d45SSherry Moore 		}
399753a6d45SSherry Moore 
400753a6d45SSherry Moore 		/* remove white space at the end of line */
401*372a97b1SKonstantin Ananyev 		for (; n != 0 && isspace(cp[n - 1]); --n)
402753a6d45SSherry Moore 			;
403753a6d45SSherry Moore 		cp[n] = '\0';
404753a6d45SSherry Moore 
405*372a97b1SKonstantin Ananyev 		if (n > 0 && cp[n - 1] == '\\') {
406753a6d45SSherry Moore 			len -= n - 1;
407753a6d45SSherry Moore 			assert(len >= 2);
408753a6d45SSherry Moore 			cp += n - 1;
409753a6d45SSherry Moore 			continue;
410753a6d45SSherry Moore 		}
411753a6d45SSherry Moore 		if ((ret = grub_menu_append_line(mp, buf)) != 0)
412753a6d45SSherry Moore 			break;
413753a6d45SSherry Moore 
414753a6d45SSherry Moore 		cp = buf;
415753a6d45SSherry Moore 		len = sizeof (buf);
416753a6d45SSherry Moore 	}
417753a6d45SSherry Moore 
418753a6d45SSherry Moore 	if (fclose(fp) == EOF)
419753a6d45SSherry Moore 		ret = errno;
420753a6d45SSherry Moore 	else if (ret == 0)
421753a6d45SSherry Moore 		ret = grub_menu_process(mp);
422753a6d45SSherry Moore 
423753a6d45SSherry Moore err_out1:
424753a6d45SSherry Moore 	grub_fsd_umount_tmp(mp->gm_root.gr_fs + GRBM_FS_TOP);
425753a6d45SSherry Moore 	if (0 != ret) {
426753a6d45SSherry Moore 		grub_menu_fini(mp);
427753a6d45SSherry Moore 		mp = NULL;
428753a6d45SSherry Moore 	}
429753a6d45SSherry Moore 	*menup = mp;
430753a6d45SSherry Moore 	return (ret);
431753a6d45SSherry Moore }
432753a6d45SSherry Moore 
433753a6d45SSherry Moore void
grub_menu_fini(grub_menu_t * mp)434753a6d45SSherry Moore grub_menu_fini(grub_menu_t *mp)
435753a6d45SSherry Moore {
436753a6d45SSherry Moore 	if (mp == NULL)
437753a6d45SSherry Moore 		return;
438753a6d45SSherry Moore 
439753a6d45SSherry Moore 	grub_fs_fini(&mp->gm_fs);
440753a6d45SSherry Moore 	free_entries(mp);
441753a6d45SSherry Moore 	free_linelist(mp->gm_start);
442753a6d45SSherry Moore 	free(mp);
443753a6d45SSherry Moore }
444753a6d45SSherry Moore 
445753a6d45SSherry Moore grub_line_t *
grub_menu_next_line(const grub_menu_t * mp,const grub_line_t * lp)446753a6d45SSherry Moore grub_menu_next_line(const grub_menu_t *mp, const grub_line_t *lp)
447753a6d45SSherry Moore {
448753a6d45SSherry Moore 	assert(mp);
449753a6d45SSherry Moore 	if (lp == NULL)
450753a6d45SSherry Moore 		return (mp->gm_start);
451753a6d45SSherry Moore 	else
452753a6d45SSherry Moore 		return (lp->gl_next);
453753a6d45SSherry Moore }
454753a6d45SSherry Moore 
455753a6d45SSherry Moore grub_line_t *
grub_menu_prev_line(const grub_menu_t * mp,const grub_line_t * lp)456753a6d45SSherry Moore grub_menu_prev_line(const grub_menu_t *mp, const grub_line_t *lp)
457753a6d45SSherry Moore {
458753a6d45SSherry Moore 	assert(mp);
459753a6d45SSherry Moore 	if (lp == NULL)
460753a6d45SSherry Moore 		return (mp->gm_end);
461753a6d45SSherry Moore 	else
462753a6d45SSherry Moore 		return (lp->gl_prev);
463753a6d45SSherry Moore }
464753a6d45SSherry Moore 
465753a6d45SSherry Moore grub_line_t *
grub_menu_get_line(const grub_menu_t * mp,int num)466753a6d45SSherry Moore grub_menu_get_line(const grub_menu_t *mp, int num)
467753a6d45SSherry Moore {
468753a6d45SSherry Moore 	grub_line_t *lp;
469753a6d45SSherry Moore 
470753a6d45SSherry Moore 	assert(mp);
471753a6d45SSherry Moore 	if (num > mp->gm_line_num)
472753a6d45SSherry Moore 		return (NULL);
473753a6d45SSherry Moore 	for (lp = mp->gm_start; lp != NULL && num != lp->gl_line_num;
474753a6d45SSherry Moore 	    lp = lp->gl_next)
475753a6d45SSherry Moore 		;
476753a6d45SSherry Moore 	return (lp);
477753a6d45SSherry Moore }
478753a6d45SSherry Moore 
479753a6d45SSherry Moore size_t
grub_menu_get_cmdline(const grub_menu_t * mp,int num,char * cmdl,size_t size)480753a6d45SSherry Moore grub_menu_get_cmdline(const grub_menu_t *mp, int num, char *cmdl, size_t size)
481753a6d45SSherry Moore {
482753a6d45SSherry Moore 	grub_entry_t *ent;
483753a6d45SSherry Moore 
484753a6d45SSherry Moore 	assert(mp);
485753a6d45SSherry Moore 	if ((ent = grub_menu_get_entry(mp, num)) == NULL)
486753a6d45SSherry Moore 		return (size_t)(-1);
487753a6d45SSherry Moore 
488753a6d45SSherry Moore 	return (grub_entry_get_cmdline(ent, cmdl, size));
489753a6d45SSherry Moore }
490753a6d45SSherry Moore 
491753a6d45SSherry Moore grub_entry_t *
grub_menu_next_entry(const grub_menu_t * mp,const grub_entry_t * ent)492753a6d45SSherry Moore grub_menu_next_entry(const grub_menu_t *mp, const grub_entry_t *ent)
493753a6d45SSherry Moore {
494753a6d45SSherry Moore 	assert(mp);
495753a6d45SSherry Moore 	if (ent == NULL) {
496753a6d45SSherry Moore 		return (mp->gm_ent_start);
497753a6d45SSherry Moore 	} else {
498753a6d45SSherry Moore 		assert(mp == ent->ge_menu);
499753a6d45SSherry Moore 		return (ent->ge_next);
500753a6d45SSherry Moore 	}
501753a6d45SSherry Moore }
502753a6d45SSherry Moore 
503753a6d45SSherry Moore grub_entry_t *
grub_menu_prev_entry(const grub_menu_t * mp,const grub_entry_t * ent)504753a6d45SSherry Moore grub_menu_prev_entry(const grub_menu_t *mp, const grub_entry_t *ent)
505753a6d45SSherry Moore {
506753a6d45SSherry Moore 	assert(mp);
507753a6d45SSherry Moore 	if (ent == NULL) {
508753a6d45SSherry Moore 		return (mp->gm_ent_end);
509753a6d45SSherry Moore 	} else {
510753a6d45SSherry Moore 		assert(mp == ent->ge_menu);
511753a6d45SSherry Moore 		return (ent->ge_prev);
512753a6d45SSherry Moore 	}
513753a6d45SSherry Moore }
514753a6d45SSherry Moore 
515753a6d45SSherry Moore grub_entry_t *
grub_menu_get_entry(const grub_menu_t * mp,int num)516753a6d45SSherry Moore grub_menu_get_entry(const grub_menu_t *mp, int num)
517753a6d45SSherry Moore {
518753a6d45SSherry Moore 	grub_entry_t *ent;
519753a6d45SSherry Moore 
520753a6d45SSherry Moore 	assert(mp);
521753a6d45SSherry Moore 	if (num == GRUB_ENTRY_DEFAULT) {
522753a6d45SSherry Moore 		ent = mp->gm_ent_default;
523753a6d45SSherry Moore 	} else if (num >= mp->gm_entry_num) {
524753a6d45SSherry Moore 		ent = NULL;
525753a6d45SSherry Moore 	} else {
526753a6d45SSherry Moore 		for (ent = mp->gm_ent_start;
527753a6d45SSherry Moore 		    ent != NULL && num != ent->ge_entry_num;
528753a6d45SSherry Moore 		    ent = ent->ge_next)
529753a6d45SSherry Moore 			;
530753a6d45SSherry Moore 	}
531753a6d45SSherry Moore 	return (ent);
532753a6d45SSherry Moore }
533