diff --git a/ChangeLog b/ChangeLog index ef523a29d..8a0c70d5c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,45 @@ +2003-12-03 Marco Gerards + + * fs/ext2.c (pupa_ext2_label): New function. + (pupa_ext2_fs): Added label. + * fs/fat.c (pupa_fat_label): New function. + (pupa_fat_fs): Added label. + * include/pupa/fs.h (struct pupa_fs): Added prototype label. + + * kern/misc.c (pupa_strndup): New function. + * include/pupa/misc.h (pupa_strndup): New prototype. + + * include/pupa/normal.h: Include . + (pupa_set_history): New prototype. + (pupa_iterate_commands): New prototype. + * normal/cmdline.c: Include , + , . + (hist_size): New variable. + (hist_lines): Likewise. + (hist_end): Likewise. + (hist_used): Likewise. + (pupa_set_history): New function. + (pupa_history_get): Likewise. + (pupa_history_add): Likewise. + (pupa_history_replace): Likewise. + (pupa_tab_complete): Likewise. + (pupa_cmdline_run): Added tab completion and history buffer. Tab + completion shows partitionnames while completing partitions, this + feature was suggested by Jeff Bailey. + * normal/command.c (pupa_iterate_commands): New function. + * normal/main.c (PUPA_DEFAULT_HISTORY_SIZE): New macro. + (pupa_normal_init): Initialize history buffer. + (PUPA_MOD_INIT): Likewise. + (pupa_normal_fini): Free the history buffer. + (PUPA_MOD_FINI): Likewise. + + * util/console.c (pupa_ncurses_getkey): Accept 127 as backspace + key. + + * aclocal.m4 (pupa_I386_CHECK_REGPARM_BUG): New DEFUN. + * configure.ac [i386]: Check for regparam bug. + (NESTED_FUNC_ATTR) [! i386]: Defined. + 2003-11-17 Marco Gerards * conf/i386-pc.rmk (sbin_UTILITIES): Added pupa-emu. diff --git a/aclocal.m4 b/aclocal.m4 index 9f8e1e715..a15858a56 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -290,3 +290,42 @@ else AC_MSG_ERROR([neither end nor _end is defined]) fi ]) + +dnl Check if the C compiler has a bug while using nested functions when +dnl mregparm is used on the i386. Some gcc versions do not pass the third +dnl parameter correctly to the nested function. +dnl Written by Marco Gerards. +AC_DEFUN(pupa_I386_CHECK_REGPARM_BUG, +[AC_REQUIRE([AC_PROG_CC]) +AC_MSG_CHECKING([if GCC has the regparm=3 bug]) +AC_CACHE_VAL(pupa_cv_i386_check_nested_functions, +[AC_RUN_IFELSE([AC_LANG_SOURCE( +[[int *p; + +int +main () +{ + int test; + + int __attribute__ ((__regparm__ (3))) nestedfunc (int a, int b, int c) + { + return (&test == p); + } + + p = &test; + return nestedfunc (0, 0, 0); +} +]])], + [pupa_cv_i386_check_nested_functions=yes], + [pupa_cv_i386_check_nested_functions=no])]) + +AC_MSG_RESULT([$pupa_cv_i386_check_nested_functions]) + +if test "x$pupa_cv_i386_check_nested_functions" = xyes; then + AC_DEFINE([NESTED_FUNC_ATTR], + [__attribute__ ((__regparm__ (2)))], + [Catch gcc bug]) +else + AC_DEFINE([NESTED_FUNC_ATTR], [], [Catch gcc bug]) +fi +]) diff --git a/config.h.in b/config.h.in index 362c9bf65..32ae6d617 100644 --- a/config.h.in +++ b/config.h.in @@ -49,6 +49,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H +/* Catch gcc bug */ +#undef NESTED_FUNC_ATTR + /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT diff --git a/configure b/configure index de08ac5dd..7f1d8b647 100644 --- a/configure +++ b/configure @@ -3253,9 +3253,93 @@ fi echo "$as_me:$LINENO: result: $pupa_cv_i386_asm_absolute_without_asterisk" >&5 echo "${ECHO_T}$pupa_cv_i386_asm_absolute_without_asterisk" >&6 + +echo "$as_me:$LINENO: checking if GCC has the regparm=3 bug" >&5 +echo $ECHO_N "checking if GCC has the regparm=3 bug... $ECHO_C" >&6 +if test "${pupa_cv_i386_check_nested_functions+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run test program while cross compiling +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +int *p; + +int +main () +{ + int test; + + int __attribute__ ((__regparm__ (3))) nestedfunc (int a, int b, int c) + { + return (&test == p); + } + + p = &test; + return nestedfunc (0, 0, 0); +} + +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + pupa_cv_i386_check_nested_functions=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +pupa_cv_i386_check_nested_functions=no +fi +rm -f core core.* *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi fi +echo "$as_me:$LINENO: result: $pupa_cv_i386_check_nested_functions" >&5 +echo "${ECHO_T}$pupa_cv_i386_check_nested_functions" >&6 + +if test "x$pupa_cv_i386_check_nested_functions" = xyes; then + +cat >>confdefs.h <<\_ACEOF +#define NESTED_FUNC_ATTR __attribute__ ((__regparm__ (2))) +_ACEOF + +else + +cat >>confdefs.h <<\_ACEOF +#define NESTED_FUNC_ATTR +_ACEOF + +fi + +else + +cat >>confdefs.h <<\_ACEOF +#define NESTED_FUNC_ATTR +_ACEOF + +fi + # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: diff --git a/configure.ac b/configure.ac index caa59dea9..2901d5f43 100644 --- a/configure.ac +++ b/configure.ac @@ -88,9 +88,11 @@ if test "x$host_cpu" = xi386; then pupa_I386_ASM_PREFIX_REQUIREMENT pupa_I386_ASM_ADDR32 pupa_I386_ASM_ABSOLUTE_WITHOUT_ASTERISK + pupa_I386_CHECK_REGPARM_BUG +else + AC_DEFINE([NESTED_FUNC_ATTR], [],[Catch gcc bug]) fi - AC_PROG_INSTALL AC_PROG_MAKE_SET AC_CHECK_TOOL(OBJCOPY, objcopy) diff --git a/fs/ext2.c b/fs/ext2.c index f800be155..9f1cca24f 100644 --- a/fs/ext2.c +++ b/fs/ext2.c @@ -687,6 +687,31 @@ pupa_ext2_dir (pupa_device_t device, const char *path, return pupa_errno; } +static pupa_err_t +pupa_ext2_label (pupa_device_t device, char **label) +{ + struct pupa_ext2_data *data; + pupa_disk_t disk = device->disk; + +#ifndef PUPA_UTIL + pupa_dl_ref (my_mod); +#endif + + data = pupa_ext2_mount (disk); + if (data) + *label = pupa_strndup (data->sblock.volume_name, 14); + else + *label = 0; + +#ifndef PUPA_UTIL + pupa_dl_unref (my_mod); +#endif + + pupa_free (data); + + return pupa_errno; +} + static struct pupa_fs pupa_ext2_fs = { @@ -695,6 +720,7 @@ static struct pupa_fs pupa_ext2_fs = .open = pupa_ext2_open, .read = pupa_ext2_read, .close = pupa_ext2_close, + .label = pupa_ext2_label, .next = 0 }; diff --git a/fs/fat.c b/fs/fat.c index 49b89f5b5..723727631 100644 --- a/fs/fat.c +++ b/fs/fat.c @@ -3,6 +3,7 @@ * PUPA -- Preliminary Universal Programming Architecture for GRUB * Copyright (C) 2000,2001 Free Software Foundation, Inc. * Copyright (C) 2002 Yoshinori K. Okuji + * Copyright (C) 2003 Marco Gerards . * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -771,6 +772,70 @@ pupa_fat_close (pupa_file_t file) return pupa_errno; } +static pupa_err_t +pupa_fat_label (pupa_device_t device, char **label) +{ + struct pupa_fat_data *data; + pupa_disk_t disk = device->disk; + pupa_ssize_t offset = -sizeof(struct pupa_fat_dir_entry); + + +#ifndef PUPA_UTIL + pupa_dl_ref (my_mod); +#endif + + data = pupa_fat_mount (disk); + if (! data) + goto fail; + + if (! (data->attr & PUPA_FAT_ATTR_DIRECTORY)) + { + pupa_error (PUPA_ERR_BAD_FILE_TYPE, "not a directory"); + return 0; + } + + while (1) + { + struct pupa_fat_dir_entry dir; + + /* Adjust the offset. */ + offset += sizeof (dir); + + /* Read a directory entry. */ + if ((pupa_fat_read_data (disk, data, 0, + offset, sizeof (dir), (char *) &dir) + != sizeof (dir)) + || dir.name[0] == 0) + { + if (pupa_errno != PUPA_ERR_NONE) + goto fail; + else + { + *label = 0; + return PUPA_ERR_NONE; + } + } + + if (dir.attr == PUPA_FAT_ATTR_VOLUME_ID) + { + *label = pupa_strndup (dir.name, 11); + return PUPA_ERR_NONE; + } + } + + *label = 0; + + fail: + +#ifndef PUPA_UTIL + pupa_dl_unref (my_mod); +#endif + + pupa_free (data); + + return pupa_errno; +} + static struct pupa_fs pupa_fat_fs = { .name = "fat", @@ -778,6 +843,7 @@ static struct pupa_fs pupa_fat_fs = .open = pupa_fat_open, .read = pupa_fat_read, .close = pupa_fat_close, + .label = pupa_fat_label, .next = 0 }; diff --git a/include/grub/fs.h b/include/grub/fs.h index 49b5f9593..bf23b51c3 100644 --- a/include/grub/fs.h +++ b/include/grub/fs.h @@ -2,6 +2,7 @@ /* * PUPA -- Preliminary Universal Programming Architecture for GRUB * Copyright (C) 2002 Yoshinori K. Okuji + * Copyright (C) 2003 Marco Gerards . * * PUPA is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -46,6 +47,11 @@ struct pupa_fs /* Close the file FILE. */ pupa_err_t (*close) (struct pupa_file *file); + + /* Return the label of the device DEVICE in LABEL. The label is + returned in a pupa_malloc'ed buffer and should be freed by the + caller. */ + pupa_err_t (*label) (pupa_device_t device, char **label); /* The next filesystem. */ struct pupa_fs *next; diff --git a/include/grub/misc.h b/include/grub/misc.h index 8bc3fd95b..25673aa83 100644 --- a/include/grub/misc.h +++ b/include/grub/misc.h @@ -47,6 +47,7 @@ int EXPORT_FUNC(pupa_isalpha) (int c); int EXPORT_FUNC(pupa_tolower) (int c); unsigned long EXPORT_FUNC(pupa_strtoul) (const char *str, char **end, int base); char *EXPORT_FUNC(pupa_strdup) (const char *s); +char *EXPORT_FUNC(pupa_strndup) (const char *s, pupa_size_t n); void *EXPORT_FUNC(pupa_memset) (void *s, int c, pupa_size_t n); pupa_size_t EXPORT_FUNC(pupa_strlen) (const char *s); int EXPORT_FUNC(pupa_printf) (const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); diff --git a/include/grub/normal.h b/include/grub/normal.h index 308c69b77..022a7a847 100644 --- a/include/grub/normal.h +++ b/include/grub/normal.h @@ -2,6 +2,7 @@ /* * PUPA -- Preliminary Universal Programming Architecture for GRUB * Copyright (C) 2002,2003 Yoshinori K. Okuji + * Copyright (C) 2003 Marco Gerards . * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,6 +24,7 @@ #include #include +#include /* The maximum size of a command-line. */ #define PUPA_MAX_CMDLINE 1600 @@ -125,6 +127,8 @@ void EXPORT_FUNC(pupa_register_command) (const char *name, const char *description); void EXPORT_FUNC(pupa_unregister_command) (const char *name); pupa_command_t pupa_command_find (char *cmdline); +pupa_err_t pupa_set_history (int newsize); +int pupa_iterate_commands (int (*iterate) (pupa_command_t)); int pupa_command_execute (char *cmdline); void pupa_command_init (void); void pupa_normal_init_page (void); diff --git a/kern/misc.c b/kern/misc.c index e1fa032a8..09595bd05 100644 --- a/kern/misc.c +++ b/kern/misc.c @@ -301,6 +301,23 @@ pupa_strdup (const char *s) return pupa_memcpy (p, s, len); } +char * +pupa_strndup (const char *s, pupa_size_t n) +{ + pupa_size_t len = 0; + char *p = (char *) s; + + while (*(p++) && len < n) + len++; + + len = pupa_strlen (s) + 1; + p = (char *) pupa_malloc (len); + if (! p) + return 0; + + return pupa_memcpy (p, s, len); +} + void * pupa_memset (void *s, int c, pupa_size_t n) { diff --git a/normal/cmdline.c b/normal/cmdline.c index d6810cd34..5f0ffd9c0 100644 --- a/normal/cmdline.c +++ b/normal/cmdline.c @@ -2,6 +2,7 @@ * PUPA -- Preliminary Universal Programming Architecture for GRUB * Copyright (C) 1999,2000,2001,2002 Free Software Foundation, Inc. * Copyright (C) 2003 Yoshinori K. Okuji + * Copyright (C) 2003 Marco Gerards * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,9 +25,429 @@ #include #include #include +#include +#include +#include static char *kill_buf; +static int hist_size; +static char **hist_lines = 0; +static int hist_pos = 0; +static int hist_end = 0; +static int hist_used = 0; + +pupa_err_t +pupa_set_history (int newsize) +{ + char **old_hist_lines = hist_lines; + hist_lines = pupa_malloc (sizeof (char *) * newsize); + + /* Copy the old lines into the new buffer. */ + if (old_hist_lines) + { + /* Remove the lines that don't fit in the new buffer. */ + if (newsize < hist_used) + { + int i; + int delsize = hist_used - newsize; + hist_used = newsize; + + for (i = 0; i < delsize; i++) + { + int pos = hist_end - i; + if (pos > hist_size) + pos -= hist_size; + pupa_free (old_hist_lines[pos]); + } + + hist_end -= delsize; + if (hist_end < 0) + hist_end = hist_size - hist_end; + } + + if (hist_pos < hist_end) + pupa_memmove (hist_lines, old_hist_lines + hist_pos, + (hist_end - hist_pos) * sizeof (char *)); + else + { + /* Copy the first part. */ + pupa_memmove (hist_lines, old_hist_lines, + hist_pos * sizeof (char *)); + + + /* Copy the last part. */ + pupa_memmove (hist_lines + hist_pos, old_hist_lines + hist_pos, + (hist_size - hist_pos) * sizeof (char *)); + + } + } + + pupa_free (old_hist_lines); + + hist_size = newsize; + hist_pos = 0; + hist_end = hist_used; + return 0; +} + +/* Get the entry POS from the history where `0' is the newest + entry. */ +static char * +pupa_history_get (int pos) +{ + pos = (hist_pos + pos) % hist_size; + return hist_lines[pos]; +} + + +/* Insert a new history line S on the top of the history. */ +static void +pupa_history_add (char *s) +{ + /* Remove the oldest entry in the history to make room for a new + entry. */ + if (hist_used + 1 > hist_size) + { + hist_end--; + if (hist_end < 0) + hist_end = hist_size + hist_end; + + pupa_free (hist_lines[hist_end]); + } + else + hist_used++; + + /* Move to the next position. */ + hist_pos--; + if (hist_pos < 0) + hist_pos = hist_size + hist_pos; + + /* Insert into history. */ + hist_lines[hist_pos] = pupa_strdup (s); +} + +/* Replace the history entry on position POS with the string S. */ +static void +pupa_history_replace (int pos, char *s) +{ + pos = (hist_pos + pos) % hist_size; + pupa_free (hist_lines[pos]); + hist_lines[pos] = pupa_strdup (s); +} + +/* Try to complete the string in BUF, return the characters that + should be added to the string. This command outputs the possible + completions, in that case set RESTORE to 1 so the caller can + restore the prompt. */ +static char * +pupa_tab_complete (char *buf, int *restore) +{ + char *pos = buf; + char *path; + + char *found = 0; + int begin; + int end; + int len; + int numfound = 0; + + /* The disk that is used for pupa_partition_iterate. */ + pupa_device_t partdev; + + /* String that is added when matched. */ + char *matchstr; + + void print_simple_completion (char *comp) + { + pupa_printf (" %s", comp); + } + + void print_partition_completion (char *comp) + { + pupa_fs_t fs = 0; + pupa_device_t part; + char devname[20]; + + pupa_sprintf (devname, "%s,%s", partdev->disk->name, comp); + part = pupa_device_open (devname); + if (!part) + pupa_printf ("\n\tPartition num:%s, Filesystem cannot be accessed", + comp); + else + { + char *label; + + fs = pupa_fs_probe (part); + /* Ignore all errors. */ + pupa_errno = 0; + + pupa_printf ("\n\tPartition num:%s, Filesystem type %s", + comp, fs ? fs->name : "Unknown"); + + if (fs) + { + (fs->label) (part, &label); + if (pupa_errno == PUPA_ERR_NONE) + { + if (label && pupa_strlen (label)) + pupa_printf (", Label: %s", label); + pupa_free (label); + } + pupa_errno = PUPA_ERR_NONE; + } + pupa_device_close (part); + } + } + + /* Add a string to the list of possible completions. COMP is the + string that should be added. If this string completely matches + add the string MATCH to the input after adding COMP. The string + WHAT contains a discription of the kind of data that is added. + Use PRINT_COMPLETION to show the completions if there are + multiple matches. XXX: Because of a bug in gcc it is required to + use __regparm__ in some cases. */ + + int NESTED_FUNC_ATTR + add_completion (const char *comp, const char *match, const char *what, + void (*print_completion) (char *)) + { + /* Bug in strncmp then len ==0. */ + if (!len || pupa_strncmp (pos, comp, len) == 0) + { + numfound++; + + if (numfound == 1) + { + begin = len; + found = pupa_strdup (comp); + end = pupa_strlen (found); + matchstr = (char *) match; + } + /* Multiple matches found, print the first instead of completing. */ + else if (numfound == 2) + { + pupa_printf ("\nPossible %s are: ", what); + print_completion (found); + } + + if (numfound > 1) + { + char *s1 = found; + const char *s2 = comp; + int cnt = 0; + + print_completion ((char *) comp); + + /* Find out how many characters match. */ + while ((cnt < end) && *s1 && *s2 && (*s1 == *s2)) + { + s1++; + s2++; + cnt++; + } + end = cnt; + } + } + + return 0; + } + + int iterate_part (const pupa_partition_t p) + { + add_completion (pupa_partition_get_name (p), ")", "partitions", + print_partition_completion); + return 0; + } + + int iterate_dir (const char *filename, int dir) + { + if (!dir) + add_completion (filename, " ", "files", print_simple_completion); + else + { + char fname[pupa_strlen (filename) + 2]; + pupa_strcpy (fname, filename); + pupa_sprintf (fname, "%s/", filename); + add_completion (fname, "", "files", print_simple_completion); + } + return 0; + } + + int iterate_dev (const char *devname) + { + pupa_device_t dev; + + /* Complete the partition part. */ + dev = pupa_device_open (devname); + + if (dev) + { + if (dev->disk && dev->disk->has_partitions) + add_completion (devname, ",", "disks", print_simple_completion); + else + add_completion (devname, ")", "disks", print_simple_completion); + } + + return 0; + } + + int iterate_commands (pupa_command_t cmd) + { + if (cmd->flags & PUPA_COMMAND_FLAG_CMDLINE) + add_completion (cmd->name, " ", "commands", print_simple_completion); + return 0; + } + + /* Remove blank space on the beginning of the line. */ + while (*pos == ' ') + pos++; + + /* Check if the string is a command or path. */ + path = pupa_strchr (pos, ' '); + + if (!path) + { + /* Tab complete a command. */ + len = pupa_strlen (pos); + + pupa_iterate_commands (iterate_commands); + } + else + { + pos = path; + + /* Remove blank space on the beginning of the line. */ + while (*pos == ' ') + pos++; + + /* Check if this is a completion for a device name. */ + if (*pos == '(' && !pupa_strchr (pos, ')')) + { + /* Check if this is a device or partition. */ + char *partition = pupa_strchr (++pos, ','); + + if (!partition) + { + /* Complete the disk part. */ + len = pupa_strlen (pos); + pupa_disk_dev_iterate (iterate_dev); + if (pupa_errno) + goto fail; + } + else + { + *partition = '\0'; + + /* Complete the partition part. */ + partdev = pupa_device_open (pos); + *partition = ','; + pupa_errno = PUPA_ERR_NONE; + + if (partdev) + { + if (partdev->disk && partdev->disk->has_partitions) + { + pos = partition + 1; + len = pupa_strlen (pos); + + pupa_partition_iterate (partdev->disk, iterate_part); + if (pupa_errno) + pupa_errno = 0; + } + + pupa_device_close (partdev); + } + else + goto fail; + } + } + else + { + char *device = pupa_file_get_device_name (pos); + pupa_device_t dev; + pupa_fs_t fs; + + dev = pupa_device_open (device); + if (!dev) + goto fail; + + fs = pupa_fs_probe (dev); + if (pupa_errno) + goto fail; + + pos = pupa_strrchr (pos, '/'); + if (pos) + { + char *dir; + char *dirfile; + pos++; + len = pupa_strlen (pos); + + dir = pupa_strchr (path, '/'); + if (!dir) + { + *restore = 0; + return 0; + } + + dir = pupa_strdup (dir); + + /* Cut away the filename part. */ + dirfile = pupa_strrchr (dir, '/'); + dirfile[1] = '\0'; + + /* Tab complete a file. */ + (fs->dir) (dev, dir, iterate_dir); + if (dev) + pupa_device_close (dev); + + pupa_free (device); + pupa_free (dir); + + if (pupa_errno) + goto fail; + } + else + { + found = pupa_strdup ("/"); + matchstr = ""; + numfound = 1; + begin = 0; + end = 1; + } + } + + } + + /* If more than one match is found those matches will be printed and + the prompt should be restored. */ + if (numfound > 1) + *restore = 1; + else + *restore = 0; + + /* Return the part that matches. */ + if (end && found) + { + char *insert; + insert = pupa_malloc (end - begin + 1 + sizeof (matchstr)); + pupa_strncpy (insert, found + begin, end - begin); + insert[end - begin] = '\0'; + if (numfound == 1) + pupa_strcat (insert, matchstr); + pupa_free (found); + + return insert; + } + + fail: + pupa_free (found); + pupa_errno = PUPA_ERR_NONE; + + return 0; +} + void pupa_cmdline_run (int nested) { @@ -82,6 +503,7 @@ pupa_cmdline_get (const char *prompt, char cmdline[], unsigned max_len, pupa_size_t plen; char buf[max_len]; int key; + int histpos = 0; auto void cl_insert (const char *str); auto void cl_delete (unsigned len); auto void cl_print (int pos, int c); @@ -167,6 +589,8 @@ pupa_cmdline_get (const char *prompt, char cmdline[], unsigned max_len, cl_insert (cmdline); + pupa_history_add (buf); + while ((key = PUPA_TERM_ASCII_CHAR (pupa_getkey ())) != '\n' && key != '\r') { if (readline) @@ -200,7 +624,34 @@ pupa_cmdline_get (const char *prompt, char cmdline[], unsigned max_len, break; case 9: /* Ctrl-i or TAB */ - /* FIXME */ + { + char *insert; + int restore; + + /* Backup the next character and make it 0 so it will + be easy to use string functions. */ + char backup = buf[lpos]; + buf[lpos] = '\0'; + + + insert = pupa_tab_complete (buf, &restore); + /* Restore the original string. */ + buf[lpos] = backup; + + if (restore) + { + /* Restore the prompt. */ + pupa_printf ("\n%s%s", prompt, buf); + xpos = plen; + ystart = ypos = (pupa_getxy () & 0xFF); + } + + if (insert) + { + cl_insert (insert); + pupa_free (insert); + } + } break; case 11: /* Ctrl-k */ @@ -217,11 +668,34 @@ pupa_cmdline_get (const char *prompt, char cmdline[], unsigned max_len, break; case 14: /* Ctrl-n */ - /* FIXME */ - break; + { + char *hist; + lpos = 0; + + if (histpos > 0) + histpos--; + + cl_delete (llen); + hist = pupa_history_get (histpos); + cl_insert (hist); + + break; + } case 16: /* Ctrl-p */ - /* FIXME */ + { + char *hist; + + lpos = 0; + + if (histpos < hist_used - 1) + histpos++; + + cl_delete (llen); + hist = pupa_history_get (histpos); + + cl_insert (hist); + } break; case 21: /* Ctrl-u */ @@ -282,6 +756,8 @@ pupa_cmdline_get (const char *prompt, char cmdline[], unsigned max_len, } break; } + + pupa_history_replace (histpos, buf); } pupa_putchar ('\n'); diff --git a/normal/command.c b/normal/command.c index c1c125c46..8bef9d0dc 100644 --- a/normal/command.c +++ b/normal/command.c @@ -1,6 +1,7 @@ /* * PUPA -- Preliminary Universal Programming Architecture for GRUB * Copyright (C) 2003 Yoshinori K. Okuji + * Copyright (C) 2003 Marco Gerards . * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -95,6 +96,15 @@ pupa_command_find (char *cmdline) return cmd; } +int +pupa_iterate_commands (int (*iterate) (pupa_command_t)) +{ + pupa_command_t cmd; + for (cmd = pupa_command_list; cmd; cmd = cmd->next) + iterate (cmd); + return 0; +} + int pupa_command_execute (char *cmdline) { diff --git a/normal/main.c b/normal/main.c index 52749f79d..8753fd7e7 100644 --- a/normal/main.c +++ b/normal/main.c @@ -3,6 +3,7 @@ * PUPA -- Preliminary Universal Programming Architecture for GRUB * Copyright (C) 2000,2001,2002 Free Software Foundation, Inc. * Copyright (C) 2002,2003 Yoshinori K. Okuji + * Copyright (C) 2003 Marco Gerards . * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,6 +31,8 @@ pupa_jmp_buf pupa_exit_env; +#define PUPA_DEFAULT_HISTORY_SIZE 50 + /* Read a line from the file FILE. */ static int get_line (pupa_file_t file, char cmdline[], int max_len) @@ -332,11 +335,12 @@ pupa_rescue_cmd_normal (int argc, char *argv[]) pupa_enter_normal_mode (argv[0]); } - #ifdef PUPA_UTIL void pupa_normal_init (void) { + pupa_set_history (PUPA_DEFAULT_HISTORY_SIZE); + /* Register a command "normal" for the rescue mode. */ pupa_rescue_register_command ("normal", pupa_rescue_cmd_normal, "enter normal mode"); @@ -349,6 +353,7 @@ pupa_normal_init (void) void pupa_normal_fini (void) { + pupa_set_history (0); pupa_rescue_unregister_command ("normal"); } @@ -358,6 +363,8 @@ PUPA_MOD_INIT /* Normal mode shouldn't be unloaded. */ pupa_dl_ref (mod); + pupa_set_history (PUPA_DEFAULT_HISTORY_SIZE); + /* Register a command "normal" for the rescue mode. */ pupa_rescue_register_command ("normal", pupa_rescue_cmd_normal, "enter normal mode"); @@ -368,6 +375,7 @@ PUPA_MOD_INIT PUPA_MOD_FINI { + pupa_set_history (0); pupa_rescue_unregister_command ("normal"); } #endif /* ! PUPA_UTIL */ diff --git a/util/console.c b/util/console.c index d5cc72b9c..d391598ed 100644 --- a/util/console.c +++ b/util/console.c @@ -95,6 +95,9 @@ pupa_ncurses_getkey (void) break; case KEY_BACKSPACE: + /* XXX: For some reason ncurses on xterm does not return + KEY_BACKSPACE. */ + case 127: c = PUPA_CONSOLE_KEY_BACKSPACE; break;