Fix cross compilation (e.g. on Darwin). Following changes to make.tmpl,
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / normal / completion.c
blob2c9b9e9312a3d4de38fe9252ddb88709d5b271d3
1 /* completion.c - complete a command, a disk, a partition or a file */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2007,2008,2009 Free Software Foundation, Inc.
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
20 #include <grub/normal.h>
21 #include <grub/misc.h>
22 #include <grub/err.h>
23 #include <grub/mm.h>
24 #include <grub/partition.h>
25 #include <grub/disk.h>
26 #include <grub/file.h>
27 #include <grub/parser.h>
28 #include <grub/extcmd.h>
29 #include <grub/charset.h>
31 /* The current word. */
32 static const char *current_word;
34 /* The matched string. */
35 static char *match;
37 /* The count of candidates. */
38 static int num_found;
40 /* The string to be appended. */
41 static const char *suffix;
43 /* The callback function to print items. */
44 static void (*print_func) (const char *, grub_completion_type_t, int);
46 /* The state the command line is in. */
47 static grub_parser_state_t cmdline_state;
50 /* Add a string to the list of possible completions. COMPLETION is the
51 string that should be added. EXTRA will be appended if COMPLETION
52 matches uniquely. The type TYPE specifies what kind of data is added. */
53 static int
54 add_completion (const char *completion, const char *extra,
55 grub_completion_type_t type)
57 if (grub_strncmp (current_word, completion, grub_strlen (current_word)) == 0)
59 num_found++;
61 switch (num_found)
63 case 1:
64 match = grub_strdup (completion);
65 if (! match)
66 return 1;
67 suffix = extra;
68 break;
70 case 2:
71 if (print_func)
72 print_func (match, type, 0);
74 /* Fall through. */
76 default:
78 char *s = match;
79 const char *t = completion;
81 if (print_func)
82 print_func (completion, type, num_found - 1);
84 /* Detect the matched portion. */
85 while (*s && *t && *s == *t)
87 s++;
88 t++;
90 s = match + grub_getend (match, s);
92 *s = '\0';
94 break;
98 return 0;
101 static int
102 iterate_partition (grub_disk_t disk, const grub_partition_t p,
103 void *data __attribute__ ((unused)))
105 const char *disk_name = disk->name;
106 char *name;
107 int ret;
108 char *part_name;
110 part_name = grub_partition_get_name (p);
111 if (! part_name)
112 return 1;
114 name = grub_xasprintf ("%s,%s", disk_name, part_name);
115 grub_free (part_name);
117 if (! name)
118 return 1;
120 ret = add_completion (name, ")", GRUB_COMPLETION_TYPE_PARTITION);
121 grub_free (name);
122 return ret;
125 static int
126 iterate_dir (const char *filename, const struct grub_dirhook_info *info,
127 void *data __attribute__ ((unused)))
129 if (! info->dir)
131 const char *prefix;
132 if (cmdline_state == GRUB_PARSER_STATE_DQUOTE)
133 prefix = "\" ";
134 else if (cmdline_state == GRUB_PARSER_STATE_QUOTE)
135 prefix = "\' ";
136 else
137 prefix = " ";
139 if (add_completion (filename, prefix, GRUB_COMPLETION_TYPE_FILE))
140 return 1;
142 else if (grub_strcmp (filename, ".") && grub_strcmp (filename, ".."))
144 char *fname;
146 fname = grub_xasprintf ("%s/", filename);
147 if (add_completion (fname, "", GRUB_COMPLETION_TYPE_FILE))
149 grub_free (fname);
150 return 1;
152 grub_free (fname);
155 return 0;
158 static int
159 iterate_dev (const char *devname, void *data __attribute__ ((unused)))
161 grub_device_t dev;
163 /* Complete the partition part. */
164 dev = grub_device_open (devname);
166 if (!dev)
168 grub_errno = GRUB_ERR_NONE;
169 return 0;
172 if (grub_strcmp (devname, current_word) == 0)
174 if (add_completion (devname, ")", GRUB_COMPLETION_TYPE_PARTITION))
176 grub_device_close (dev);
177 return 1;
180 if (dev->disk)
181 if (grub_partition_iterate (dev->disk, iterate_partition, NULL))
183 grub_device_close (dev);
184 return 1;
187 else if (add_completion (devname, "", GRUB_COMPLETION_TYPE_DEVICE))
189 grub_device_close (dev);
190 return 1;
193 grub_device_close (dev);
194 grub_errno = GRUB_ERR_NONE;
195 return 0;
198 /* Complete a device. */
199 static int
200 complete_device (void)
202 /* Check if this is a device or a partition. */
203 char *p = grub_strchr (++current_word, ',');
204 grub_device_t dev;
206 if (! p)
208 /* Complete the disk part. */
209 if (grub_disk_dev_iterate (iterate_dev, NULL))
210 return 1;
212 else
214 /* Complete the partition part. */
215 *p = '\0';
216 dev = grub_device_open (current_word);
217 *p = ',';
218 grub_errno = GRUB_ERR_NONE;
220 if (dev)
222 if (dev->disk)
224 if (grub_partition_iterate (dev->disk, iterate_partition, NULL))
226 grub_device_close (dev);
227 return 1;
231 grub_device_close (dev);
233 else
234 return 1;
237 return 0;
240 /* Complete a file. */
241 static int
242 complete_file (void)
244 char *device;
245 char *dir;
246 char *last_dir;
247 grub_fs_t fs;
248 grub_device_t dev;
249 int ret = 0;
251 device = grub_file_get_device_name (current_word);
252 if (grub_errno != GRUB_ERR_NONE)
253 return 1;
255 dev = grub_device_open (device);
256 if (! dev)
258 ret = 1;
259 goto fail;
262 fs = grub_fs_probe (dev);
263 if (! fs)
265 ret = 1;
266 goto fail;
269 dir = grub_strchr (current_word + (device ? 2 + grub_strlen (device) : 0),
270 '/');
271 last_dir = grub_strrchr (current_word, '/');
272 if (dir)
274 char *dirfile;
276 current_word = last_dir + 1;
278 dir = grub_strdup (dir);
279 if (! dir)
281 ret = 1;
282 goto fail;
285 /* Cut away the filename part. */
286 dirfile = grub_strrchr (dir, '/');
287 dirfile[1] = '\0';
289 /* Iterate the directory. */
290 (fs->dir) (dev, dir, iterate_dir, NULL);
292 grub_free (dir);
294 if (grub_errno)
296 ret = 1;
297 goto fail;
300 else
302 current_word += grub_strlen (current_word);
303 match = grub_strdup ("/");
304 if (! match)
306 ret = 1;
307 goto fail;
310 suffix = "";
311 num_found = 1;
314 fail:
315 if (dev)
316 grub_device_close (dev);
317 grub_free (device);
318 return ret;
321 /* Complete an argument. */
322 static int
323 complete_arguments (char *command)
325 grub_command_t cmd;
326 grub_extcmd_t ext;
327 const struct grub_arg_option *option;
328 char shortarg[] = "- ";
330 cmd = grub_command_find (command);
332 if (!cmd || !(cmd->flags & GRUB_COMMAND_FLAG_EXTCMD))
333 return 0;
335 ext = cmd->data;
336 if (!ext->options)
337 return 0;
339 if (add_completion ("-u", " ", GRUB_COMPLETION_TYPE_ARGUMENT))
340 return 1;
342 /* Add the short arguments. */
343 for (option = ext->options; option->doc; option++)
345 if (! option->shortarg)
346 continue;
348 shortarg[1] = option->shortarg;
349 if (add_completion (shortarg, " ", GRUB_COMPLETION_TYPE_ARGUMENT))
350 return 1;
354 /* First add the built-in arguments. */
355 if (add_completion ("--help", " ", GRUB_COMPLETION_TYPE_ARGUMENT))
356 return 1;
357 if (add_completion ("--usage", " ", GRUB_COMPLETION_TYPE_ARGUMENT))
358 return 1;
360 /* Add the long arguments. */
361 for (option = ext->options; option->doc; option++)
363 char *longarg;
364 if (!option->longarg)
365 continue;
367 longarg = grub_xasprintf ("--%s", option->longarg);
368 if (!longarg)
369 return 1;
371 if (add_completion (longarg, " ", GRUB_COMPLETION_TYPE_ARGUMENT))
373 grub_free (longarg);
374 return 1;
376 grub_free (longarg);
379 return 0;
383 static grub_parser_state_t
384 get_state (const char *cmdline)
386 grub_parser_state_t state = GRUB_PARSER_STATE_TEXT;
387 char use;
389 while (*cmdline)
390 state = grub_parser_cmdline_state (state, *(cmdline++), &use);
391 return state;
395 /* Try to complete the string in BUF. Return the characters that
396 should be added to the string. This command outputs the possible
397 completions by calling HOOK, in that case set RESTORE to 1 so the
398 caller can restore the prompt. */
399 char *
400 grub_normal_do_completion (char *buf, int *restore,
401 void (*hook) (const char *, grub_completion_type_t, int))
403 int argc;
404 char **argv;
406 /* Initialize variables. */
407 match = 0;
408 num_found = 0;
409 suffix = "";
410 print_func = hook;
412 *restore = 1;
414 if (grub_parser_split_cmdline (buf, 0, 0, &argc, &argv))
415 return 0;
417 if (argc == 0)
418 current_word = "";
419 else
420 current_word = argv[argc - 1];
422 if (argc > 1 && ! grub_strcmp (argv[0], "set"))
424 char *equals = grub_strchr (current_word, '=');
425 if (equals)
426 /* Complete the value of the variable. */
427 current_word = equals + 1;
430 /* Determine the state the command line is in, depending on the
431 state, it can be determined how to complete. */
432 cmdline_state = get_state (buf);
434 if (argc == 1 || argc == 0)
436 /* Complete a command. */
437 grub_command_t cmd;
438 FOR_COMMANDS(cmd)
440 if (cmd->prio & GRUB_COMMAND_FLAG_ACTIVE)
442 if (add_completion (cmd->name, " ", GRUB_COMPLETION_TYPE_COMMAND))
443 goto fail;
447 else if (*current_word == '-')
449 if (complete_arguments (buf))
450 goto fail;
452 else if (*current_word == '(' && ! grub_strchr (current_word, ')'))
454 /* Complete a device. */
455 if (complete_device ())
456 goto fail;
458 else
460 /* Complete a file. */
461 if (complete_file ())
462 goto fail;
465 /* If more than one match is found those matches will be printed and
466 the prompt should be restored. */
467 if (num_found > 1)
468 *restore = 1;
469 else
470 *restore = 0;
472 /* Return the part that matches. */
473 if (match)
475 char *ret;
476 char *escstr;
477 char *newstr;
478 int current_len;
479 int match_len;
480 int spaces = 0;
482 current_len = grub_strlen (current_word);
483 match_len = grub_strlen (match);
485 /* Count the number of spaces that have to be escaped. XXX:
486 More than just spaces have to be escaped. */
487 for (escstr = match + current_len; *escstr; escstr++)
488 if (*escstr == ' ')
489 spaces++;
491 ret = grub_malloc (match_len - current_len + grub_strlen (suffix) + spaces + 1);
492 newstr = ret;
493 for (escstr = match + current_len; *escstr; escstr++)
495 if (*escstr == ' ' && cmdline_state != GRUB_PARSER_STATE_QUOTE
496 && cmdline_state != GRUB_PARSER_STATE_QUOTE)
497 *(newstr++) = '\\';
498 *(newstr++) = *escstr;
500 *newstr = '\0';
502 if (num_found == 1)
503 grub_strcpy (newstr, suffix);
505 if (*ret == '\0')
507 grub_free (ret);
508 goto fail;
511 if (argc != 0)
512 grub_free (argv[0]);
513 grub_free (match);
514 return ret;
517 fail:
518 if (argc != 0)
520 grub_free (argv[0]);
521 grub_free (argv);
523 grub_free (match);
524 grub_errno = GRUB_ERR_NONE;
526 return 0;