]>
Commit | Line | Data |
---|---|---|
ce5bf700 | 1 | /* main.c - the normal mode main routine */ |
2 | /* | |
3 | * PUPA -- Preliminary Universal Programming Architecture for GRUB | |
4 | * Copyright (C) 2000,2001,2002 Free Software Foundation, Inc. | |
5 | * Copyright (C) 2002,2003 Yoshinori K. Okuji <okuji@enbug.org> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation; either version 2 of the License, or | |
10 | * (at your option) any later version. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with this program; if not, write to the Free Software | |
19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
20 | */ | |
21 | ||
22 | #include <pupa/kernel.h> | |
23 | #include <pupa/normal.h> | |
24 | #include <pupa/dl.h> | |
25 | #include <pupa/rescue.h> | |
26 | #include <pupa/misc.h> | |
27 | #include <pupa/file.h> | |
28 | #include <pupa/mm.h> | |
29 | #include <pupa/term.h> | |
30 | ||
31 | pupa_jmp_buf pupa_exit_env; | |
32 | ||
33 | /* Read a line from the file FILE. */ | |
34 | static int | |
35 | get_line (pupa_file_t file, char cmdline[], int max_len) | |
36 | { | |
37 | char c; | |
38 | int pos = 0; | |
39 | int literal = 0; | |
40 | int comment = 0; | |
41 | ||
42 | while (1) | |
43 | { | |
44 | if (pupa_file_read (file, &c, 1) != 1) | |
45 | break; | |
46 | ||
47 | /* Skip all carriage returns. */ | |
48 | if (c == '\r') | |
49 | continue; | |
50 | ||
51 | /* Replace tabs with spaces. */ | |
52 | if (c == '\t') | |
53 | c = ' '; | |
54 | ||
55 | /* The previous is a backslash, then... */ | |
56 | if (literal) | |
57 | { | |
58 | /* If it is a newline, replace it with a space and continue. */ | |
59 | if (c == '\n') | |
60 | { | |
61 | c = ' '; | |
62 | ||
63 | /* Go back to overwrite the backslash. */ | |
64 | if (pos > 0) | |
65 | pos--; | |
66 | } | |
67 | ||
68 | literal = 0; | |
69 | } | |
70 | ||
71 | if (c == '\\') | |
72 | literal = 1; | |
73 | ||
74 | if (comment) | |
75 | { | |
76 | if (c == '\n') | |
77 | comment = 0; | |
78 | } | |
79 | else if (pos == 0) | |
80 | { | |
81 | if (c == '#') | |
82 | comment = 1; | |
83 | else if (! pupa_isspace (c)) | |
84 | cmdline[pos++] = c; | |
85 | } | |
86 | else | |
87 | { | |
88 | if (c == '\n') | |
89 | break; | |
90 | ||
91 | if (pos < max_len) | |
92 | cmdline[pos++] = c; | |
93 | } | |
94 | } | |
95 | ||
96 | cmdline[pos] = '\0'; | |
97 | ||
98 | return pos; | |
99 | } | |
100 | ||
101 | static void | |
102 | free_menu (pupa_menu_t menu) | |
103 | { | |
104 | pupa_menu_entry_t entry = menu->entry_list; | |
105 | ||
106 | while (entry) | |
107 | { | |
108 | pupa_menu_entry_t next_entry = entry->next; | |
109 | pupa_command_list_t cmd = entry->command_list; | |
110 | ||
111 | while (cmd) | |
112 | { | |
113 | pupa_command_list_t next_cmd = cmd->next; | |
114 | ||
115 | pupa_free ((void *) cmd->command); | |
116 | cmd = next_cmd; | |
117 | } | |
118 | ||
119 | pupa_free ((void *) entry->title); | |
120 | entry = next_entry; | |
121 | } | |
122 | ||
123 | pupa_free (menu); | |
124 | } | |
125 | ||
126 | /* Read the config file CONFIG and return a menu. If no entry is present, | |
127 | return NULL. */ | |
128 | static pupa_menu_t | |
129 | read_config_file (const char *config) | |
130 | { | |
131 | pupa_file_t file; | |
132 | static char cmdline[PUPA_MAX_CMDLINE]; | |
133 | pupa_menu_t menu; | |
134 | pupa_menu_entry_t *next_entry, cur_entry = 0; | |
135 | pupa_command_list_t *next_cmd, cur_cmd; | |
136 | ||
137 | /* Try to open the config file. */ | |
138 | file = pupa_file_open (config); | |
139 | if (! file) | |
140 | return 0; | |
141 | ||
142 | /* Initialize the menu. */ | |
143 | menu = (pupa_menu_t) pupa_malloc (sizeof (*menu)); | |
144 | if (! menu) | |
145 | { | |
146 | pupa_file_close (file); | |
147 | return 0; | |
148 | } | |
149 | menu->default_entry = 0; | |
150 | menu->fallback_entry = -1; | |
151 | menu->timeout = -1; | |
152 | menu->size = 0; | |
153 | menu->entry_list = 0; | |
154 | ||
155 | next_entry = &(menu->entry_list); | |
156 | next_cmd = 0; | |
157 | ||
158 | /* Read each line. */ | |
159 | while (get_line (file, cmdline, sizeof (cmdline))) | |
160 | { | |
161 | pupa_command_t cmd; | |
162 | ||
163 | cmd = pupa_command_find (cmdline); | |
164 | pupa_errno = PUPA_ERR_NONE; | |
165 | if (! cmd) | |
166 | { | |
167 | pupa_printf ("Unknown command `%s' is ignored.\n", cmdline); | |
168 | continue; | |
169 | } | |
170 | ||
171 | if (cmd->flags & PUPA_COMMAND_FLAG_TITLE) | |
172 | { | |
173 | char *p; | |
174 | ||
175 | cur_entry = (pupa_menu_entry_t) pupa_malloc (sizeof (*cur_entry)); | |
176 | if (! cur_entry) | |
177 | goto fail; | |
178 | ||
179 | p = pupa_strchr (cmdline, ' '); | |
180 | if (p) | |
181 | cur_entry->title = pupa_strdup (p); | |
182 | else | |
183 | cur_entry->title = pupa_strdup (""); | |
184 | ||
185 | if (! cur_entry->title) | |
186 | { | |
187 | pupa_free (cur_entry); | |
188 | goto fail; | |
189 | } | |
190 | ||
191 | cur_entry->num = 0; | |
192 | cur_entry->command_list = 0; | |
193 | cur_entry->next = 0; | |
194 | ||
195 | *next_entry = cur_entry; | |
196 | next_entry = &(cur_entry->next); | |
197 | ||
198 | next_cmd = &(cur_entry->command_list); | |
199 | ||
200 | menu->size++; | |
201 | } | |
202 | else if (! cur_entry) | |
203 | { | |
204 | /* Run the command if possible. */ | |
205 | if (cmd->flags & PUPA_COMMAND_FLAG_MENU) | |
206 | { | |
207 | pupa_command_execute (cmdline); | |
208 | pupa_print_error (); | |
209 | pupa_errno = PUPA_ERR_NONE; | |
210 | } | |
211 | else | |
212 | { | |
213 | pupa_printf ("Invalid command `%s' is ignored.\n", cmdline); | |
214 | continue; | |
215 | } | |
216 | } | |
217 | else | |
218 | { | |
219 | cur_cmd = (pupa_command_list_t) pupa_malloc (sizeof (*cur_cmd)); | |
220 | if (! cur_cmd) | |
221 | goto fail; | |
222 | ||
223 | cur_cmd->command = pupa_strdup (cmdline); | |
224 | if (! cur_cmd->command) | |
225 | { | |
226 | pupa_free (cur_cmd); | |
227 | goto fail; | |
228 | } | |
229 | ||
230 | cur_cmd->next = 0; | |
231 | ||
232 | *next_cmd = cur_cmd; | |
233 | next_cmd = &(cur_cmd->next); | |
234 | ||
235 | cur_entry->num++; | |
236 | } | |
237 | } | |
238 | ||
239 | fail: | |
240 | ||
241 | pupa_file_close (file); | |
242 | ||
243 | /* If no entry was found or any error occurred, return NULL. */ | |
244 | if (menu->size == 0 || pupa_errno != PUPA_ERR_NONE) | |
245 | { | |
246 | free_menu (menu); | |
247 | return 0; | |
248 | } | |
249 | ||
250 | /* Check values of the default entry and the fallback one. */ | |
251 | if (menu->fallback_entry >= menu->size) | |
252 | menu->fallback_entry = -1; | |
253 | ||
254 | if (menu->default_entry < 0 || menu->default_entry >= menu->size) | |
255 | { | |
256 | if (menu->fallback_entry < 0) | |
257 | menu->default_entry = 0; | |
258 | else | |
259 | { | |
260 | menu->default_entry = menu->fallback_entry; | |
261 | menu->fallback_entry = -1; | |
262 | } | |
263 | } | |
264 | ||
265 | return menu; | |
266 | } | |
267 | ||
268 | /* This starts the normal mode. */ | |
269 | void | |
270 | pupa_enter_normal_mode (const char *config) | |
271 | { | |
272 | if (pupa_setjmp (pupa_exit_env) == 0) | |
273 | pupa_normal_execute (config, 0); | |
274 | } | |
275 | ||
276 | /* Initialize the screen. */ | |
277 | void | |
278 | pupa_normal_init_page (void) | |
279 | { | |
280 | pupa_cls (); | |
281 | pupa_printf ("\n\ | |
282 | PUPA version %s\n\n", | |
283 | PACKAGE_VERSION); | |
284 | } | |
285 | ||
286 | /* Read the config file CONFIG and execute the menu interface or | |
287 | the command-line interface. */ | |
288 | void | |
289 | pupa_normal_execute (const char *config, int nested) | |
290 | { | |
291 | pupa_menu_t menu = 0; | |
292 | ||
293 | if (config) | |
294 | { | |
295 | menu = read_config_file (config); | |
296 | ||
297 | /* Ignore any error. */ | |
298 | pupa_errno = PUPA_ERR_NONE; | |
299 | } | |
300 | ||
301 | if (menu) | |
302 | pupa_menu_run (menu, nested); | |
303 | else | |
304 | pupa_cmdline_run (nested); | |
305 | } | |
306 | ||
307 | /* Enter normal mode from rescue mode. */ | |
308 | static void | |
309 | pupa_rescue_cmd_normal (int argc, char *argv[]) | |
310 | { | |
311 | if (argc == 0) | |
312 | { | |
313 | /* Guess the config filename. */ | |
314 | char *config; | |
315 | const char *prefix; | |
316 | ||
317 | prefix = pupa_dl_get_prefix (); | |
318 | if (prefix) | |
319 | { | |
320 | config = pupa_malloc (pupa_strlen (prefix) + sizeof ("/pupa.cfg")); | |
321 | if (! config) | |
322 | return; | |
323 | ||
324 | pupa_sprintf (config, "%s/pupa.cfg", prefix); | |
325 | pupa_enter_normal_mode (config); | |
326 | pupa_free (config); | |
327 | } | |
328 | else | |
329 | pupa_enter_normal_mode (0); | |
330 | } | |
331 | else | |
332 | pupa_enter_normal_mode (argv[0]); | |
333 | } | |
334 | ||
335 | PUPA_MOD_INIT | |
336 | { | |
337 | /* Normal mode shouldn't be unloaded. */ | |
338 | pupa_dl_ref (mod); | |
339 | ||
340 | /* Register a command "normal" for the rescue mode. */ | |
341 | pupa_rescue_register_command ("normal", pupa_rescue_cmd_normal, | |
342 | "enter normal mode"); | |
343 | ||
344 | /* This registers some built-in commands. */ | |
345 | pupa_command_init (); | |
346 | } | |
347 | ||
348 | PUPA_MOD_FINI | |
349 | { | |
350 | pupa_rescue_unregister_command ("normal"); | |
351 | } |