[BACK]Return to readline.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / lib / libedit

Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.

Diff for /src/lib/libedit/readline.c between version 1.15 and 1.146.2.1

version 1.15, 2001/01/01 15:52:25 version 1.146.2.1, 2018/06/25 07:25:35
Line 15 
Line 15 
  * 2. Redistributions in binary form must reproduce the above copyright   * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the   *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.   *    documentation and/or other materials provided with the distribution.
  * 3. All advertising materials mentioning features or use of this software  
  *    must display the following acknowledgement:  
  *      This product includes software developed by the NetBSD  
  *      Foundation, Inc. and its contributors.  
  * 4. Neither the name of The NetBSD Foundation nor the names of its  
  *    contributors may be used to endorse or promote products derived  
  *    from this software without specific prior written permission.  
  *   *
  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS   * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED   * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
Line 36 
Line 29 
  * POSSIBILITY OF SUCH DAMAGE.   * POSSIBILITY OF SUCH DAMAGE.
  */   */
   
 #include <sys/cdefs.h>  #include "config.h"
 #if !defined(lint) && !defined(SCCSID)  #if !defined(lint) && !defined(SCCSID)
 __RCSID("$NetBSD$");  __RCSID("$NetBSD$");
 #endif /* not lint && not SCCSID */  #endif /* not lint && not SCCSID */
   
 #include <sys/types.h>  #include <sys/types.h>
 #include <sys/stat.h>  #include <sys/stat.h>
 #include <stdio.h>  #include <ctype.h>
 #include <dirent.h>  #include <dirent.h>
 #include <string.h>  #include <errno.h>
   #include <fcntl.h>
   #include <limits.h>
 #include <pwd.h>  #include <pwd.h>
 #include <ctype.h>  #include <setjmp.h>
   #include <stdint.h>
   #include <stdio.h>
 #include <stdlib.h>  #include <stdlib.h>
   #include <string.h>
 #include <unistd.h>  #include <unistd.h>
 #include <limits.h>  #include <vis.h>
 #include "histedit.h"  
 #include "readline.h"  #include "readline/readline.h"
 #include "sys.h"  
 #include "el.h"  #include "el.h"
 #include "fcns.h"               /* for EL_NUM_FCNS */  #include "fcns.h"
   #include "filecomplete.h"
   
   void rl_prep_terminal(int);
   void rl_deprep_terminal(void);
   
 /* for rl_complete() */  /* for rl_complete() */
 #define TAB             '\r'  #define TAB             '\r'
   
 /* see comment at the #ifdef for sense of this */  /* see comment at the #ifdef for sense of this */
 #define GDB_411_HACK  /* #define GDB_411_HACK */
   
 /* readline compatibility stuff - look at readline sources/documentation */  /* readline compatibility stuff - look at readline sources/documentation */
 /* to see what these variables mean */  /* to see what these variables mean */
 const char *rl_library_version = "EditLine wrapper";  const char *rl_library_version = "EditLine wrapper";
 char *rl_readline_name = "";  int rl_readline_version = RL_READLINE_VERSION;
   static char empty[] = { '\0' };
   static char expand_chars[] = { ' ', '\t', '\n', '=', '(', '\0' };
   static char break_chars[] = { ' ', '\t', '\n', '"', '\\', '\'', '`', '@', '$',
       '>', '<', '=', ';', '|', '&', '{', '(', '\0' };
   char *rl_readline_name = empty;
 FILE *rl_instream = NULL;  FILE *rl_instream = NULL;
 FILE *rl_outstream = NULL;  FILE *rl_outstream = NULL;
 int rl_point = 0;  int rl_point = 0;
 int rl_end = 0;  int rl_end = 0;
 char *rl_line_buffer = NULL;  char *rl_line_buffer = NULL;
   rl_vcpfunc_t *rl_linefunc = NULL;
   int rl_done = 0;
   rl_hook_func_t *rl_event_hook = NULL;
   KEYMAP_ENTRY_ARRAY emacs_standard_keymap,
       emacs_meta_keymap,
       emacs_ctlx_keymap;
   /*
    * The following is not implemented; we always catch signals in the
    * libedit fashion: set handlers on entry to el_gets() and clear them
    * on the way out. This simplistic approach works for most cases; if
    * it does not work for your application, please let us know.
    */
   int rl_catch_signals = 1;
   int rl_catch_sigwinch = 1;
   
 int history_base = 1;           /* probably never subject to change */  int history_base = 1;           /* probably never subject to change */
 int history_length = 0;  int history_length = 0;
   int history_offset = 0;
 int max_input_history = 0;  int max_input_history = 0;
 char history_expansion_char = '!';  char history_expansion_char = '!';
 char history_subst_char = '^';  char history_subst_char = '^';
 char *history_no_expand_chars = " \t\n=(";  char *history_no_expand_chars = expand_chars;
 Function *history_inhibit_expansion_function = NULL;  Function *history_inhibit_expansion_function = NULL;
   char *history_arg_extract(int start, int end, const char *str);
   
 int rl_inhibit_completion = 0;  int rl_inhibit_completion = 0;
 int rl_attempted_completion_over = 0;  int rl_attempted_completion_over = 0;
 char *rl_basic_word_break_characters = " \t\n\"\\'`@$><=;|&{(";  char *rl_basic_word_break_characters = break_chars;
 char *rl_completer_word_break_characters = NULL;  char *rl_completer_word_break_characters = NULL;
 char *rl_completer_quote_characters = NULL;  char *rl_completer_quote_characters = NULL;
 CPFunction *rl_completion_entry_function = NULL;  rl_compentry_func_t *rl_completion_entry_function = NULL;
 CPPFunction *rl_attempted_completion_function = NULL;  char *(*rl_completion_word_break_hook)(void) = NULL;
   rl_completion_func_t *rl_attempted_completion_function = NULL;
   Function *rl_pre_input_hook = NULL;
   Function *rl_startup1_hook = NULL;
   int (*rl_getc_function)(FILE *) = NULL;
   char *rl_terminal_name = NULL;
   int rl_already_prompted = 0;
   int rl_filename_completion_desired = 0;
   int rl_ignore_completion_duplicates = 0;
   int readline_echoing_p = 1;
   int _rl_print_completions_horizontally = 0;
   VFunction *rl_redisplay_function = NULL;
   Function *rl_startup_hook = NULL;
   int rl_did_startup_hook = 0;
   VFunction *rl_completion_display_matches_hook = NULL;
   VFunction *rl_prep_term_function = (VFunction *)rl_prep_terminal;
   VFunction *rl_deprep_term_function = (VFunction *)rl_deprep_terminal;
   KEYMAP_ENTRY_ARRAY emacs_meta_keymap;
   
 /*  /*
    * The current prompt string.
    */
   char *rl_prompt = NULL;
   /*
  * This is set to character indicating type of completion being done by   * This is set to character indicating type of completion being done by
  * rl_complete_internal(); this is available for application completion   * rl_complete_internal(); this is available for application completion
  * functions.   * functions.
Line 107  int rl_completion_query_items = 100;
Line 150  int rl_completion_query_items = 100;
  * in the parsed text when it is passed to the completion function.   * in the parsed text when it is passed to the completion function.
  * Shell uses this to help determine what kind of completing to do.   * Shell uses this to help determine what kind of completing to do.
  */   */
 char *rl_special_prefixes = (char *)NULL;  char *rl_special_prefixes = NULL;
   
 /*  /*
  * This is the character appended to the completed words if at the end of   * This is the character appended to the completed words if at the end of
Line 117  int rl_completion_append_character = ' '
Line 160  int rl_completion_append_character = ' '
   
 /* stuff below is used internally by libedit for readline emulation */  /* stuff below is used internally by libedit for readline emulation */
   
 /* if not zero, non-unique completions always show list of possible matches */  
 static int _rl_complete_show_all = 0;  
   
 static History *h = NULL;  static History *h = NULL;
 static EditLine *e = NULL;  static EditLine *e = NULL;
 static int el_rl_complete_cmdnum = 0;  static rl_command_func_t *map[256];
   static jmp_buf topbuf;
   
 /* internal functions */  /* internal functions */
 static unsigned char     _el_rl_complete(EditLine *, int);  static unsigned char     _el_rl_complete(EditLine *, int);
   static unsigned char     _el_rl_tstp(EditLine *, int);
 static char             *_get_prompt(EditLine *);  static char             *_get_prompt(EditLine *);
 static HIST_ENTRY       *_move_history(int);  static int               _getc_function(EditLine *, wchar_t *);
 static int               _history_search_gen(const char *, int, int);  static int               _history_expand_command(const char *, size_t, size_t,
 static int               _history_expand_command(const char *, size_t, char **);      char **);
 static char             *_rl_compat_sub(const char *, const char *,  static char             *_rl_compat_sub(const char *, const char *,
                             const char *, int);      const char *, int);
 static int               rl_complete_internal(int);  static int               _rl_event_read_char(EditLine *, wchar_t *);
 static int               _rl_qsort_string_compare(const void *, const void *);  static void              _rl_update_pos(void);
   
 /*  
  * needed for prompt switching in readline()  
  */  
 static char *el_rl_prompt = NULL;  
   
   static HIST_ENTRY rl_he;
   
 /* ARGSUSED */  /* ARGSUSED */
 static char *  static char *
 _get_prompt(EditLine *el)  _get_prompt(EditLine *el __attribute__((__unused__)))
 {  {
         return (el_rl_prompt);          rl_already_prompted = 1;
           return rl_prompt;
 }  }
   
   
 /*  /*
  * generic function for moving around history   * read one key from user defined input function
  */   */
 static HIST_ENTRY *  static int
 _move_history(int op)  /*ARGSUSED*/
   _getc_function(EditLine *el __attribute__((__unused__)), wchar_t *c)
 {  {
         HistEvent ev;          int i;
         static HIST_ENTRY rl_he;  
   
         if (history(h, &ev, op) != 0)          i = (*rl_getc_function)(rl_instream);
                 return (HIST_ENTRY *) NULL;          if (i == -1)
                   return 0;
           *c = (wchar_t)i;
           return 1;
   }
   
         rl_he.line = ev.str;  static void
         rl_he.data = "";  _resize_fun(EditLine *el, void *a)
   {
           const LineInfo *li;
           char **ap = a;
   
         return (&rl_he);          li = el_line(el);
           /* a cheesy way to get rid of const cast. */
           *ap = memchr(li->buffer, *li->buffer, (size_t)1);
 }  }
   
   static const char *
   _default_history_file(void)
   {
           struct passwd *p;
           static char *path;
           size_t len;
   
           if (path)
                   return path;
   
           if ((p = getpwuid(getuid())) == NULL)
                   return NULL;
   
           len = strlen(p->pw_dir) + sizeof("/.history");
           if ((path = malloc(len)) == NULL)
                   return NULL;
   
           (void)snprintf(path, len, "%s/.history", p->pw_dir);
           return path;
   }
   
 /*  /*
  * READLINE compatibility stuff   * READLINE compatibility stuff
  */   */
   
 /*  /*
    * Set the prompt
    */
   int
   rl_set_prompt(const char *prompt)
   {
           char *p;
   
           if (!prompt)
                   prompt = "";
           if (rl_prompt != NULL && strcmp(rl_prompt, prompt) == 0)
                   return 0;
           if (rl_prompt)
                   el_free(rl_prompt);
           rl_prompt = strdup(prompt);
           if (rl_prompt == NULL)
                   return -1;
   
           while ((p = strchr(rl_prompt, RL_PROMPT_END_IGNORE)) != NULL)
                   *p = RL_PROMPT_START_IGNORE;
   
           return 0;
   }
   
   /*
  * initialize rl compat stuff   * initialize rl compat stuff
  */   */
 int  int
 rl_initialize(void)  rl_initialize(void)
 {  {
         HistEvent ev;          HistEvent ev;
         const LineInfo *li;          int editmode = 1;
         int i;          struct termios t;
   
         if (e != NULL)          if (e != NULL)
                 el_end(e);                  el_end(e);
Line 191  rl_initialize(void)
Line 283  rl_initialize(void)
                 rl_instream = stdin;                  rl_instream = stdin;
         if (!rl_outstream)          if (!rl_outstream)
                 rl_outstream = stdout;                  rl_outstream = stdout;
         e = el_init(rl_readline_name, rl_instream, rl_outstream, stderr);  
           /*
            * See if we don't really want to run the editor
            */
           if (tcgetattr(fileno(rl_instream), &t) != -1 && (t.c_lflag & ECHO) == 0)
                   editmode = 0;
   
           e = el_init_internal(rl_readline_name, rl_instream, rl_outstream,
               stderr, fileno(rl_instream), fileno(rl_outstream), fileno(stderr),
               NO_RESET);
   
           if (!editmode)
                   el_set(e, EL_EDITMODE, 0);
   
         h = history_init();          h = history_init();
         if (!e || !h)          if (!e || !h)
                 return (-1);                  return -1;
   
         history(h, &ev, H_SETSIZE, INT_MAX);    /* unlimited */          history(h, &ev, H_SETSIZE, INT_MAX);    /* unlimited */
         history_length = 0;          history_length = 0;
         max_input_history = INT_MAX;          max_input_history = INT_MAX;
         el_set(e, EL_HIST, history, h);          el_set(e, EL_HIST, history, h);
   
           /* Setup resize function */
           el_set(e, EL_RESIZE, _resize_fun, &rl_line_buffer);
   
           /* setup getc function if valid */
           if (rl_getc_function)
                   el_set(e, EL_GETCFN, _getc_function);
   
         /* for proper prompt printing in readline() */          /* for proper prompt printing in readline() */
         el_rl_prompt = strdup("");          if (rl_set_prompt("") == -1) {
         el_set(e, EL_PROMPT, _get_prompt);                  history_end(h);
         el_set(e, EL_SIGNAL, 1);                  el_end(e);
                   return -1;
           }
           el_set(e, EL_PROMPT, _get_prompt, RL_PROMPT_START_IGNORE);
           el_set(e, EL_SIGNAL, rl_catch_signals);
   
         /* set default mode to "emacs"-style and read setting afterwards */          /* set default mode to "emacs"-style and read setting afterwards */
         /* so this can be overriden */          /* so this can be overridden */
         el_set(e, EL_EDITOR, "emacs");          el_set(e, EL_EDITOR, "emacs");
           if (rl_terminal_name != NULL)
                   el_set(e, EL_TERMINAL, rl_terminal_name);
           else
                   el_get(e, EL_TERMINAL, &rl_terminal_name);
   
         /*          /*
          * Word completition - this has to go AFTER rebinding keys           * Word completion - this has to go AFTER rebinding keys
          * to emacs-style.           * to emacs-style.
          */           */
         el_set(e, EL_ADDFN, "rl_complete",          el_set(e, EL_ADDFN, "rl_complete",
             "ReadLine compatible completition function",              "ReadLine compatible completion function",
             _el_rl_complete);              _el_rl_complete);
         el_set(e, EL_BIND, "^I", "rl_complete", NULL);          el_set(e, EL_BIND, "^I", "rl_complete", NULL);
   
         /*          /*
          * Find out where the rl_complete function was added; this is           * Send TSTP when ^Z is pressed.
          * used later to detect that lastcmd was also rl_complete.  
          */           */
         for(i=EL_NUM_FCNS; i < e->el_map.nfunc; i++) {          el_set(e, EL_ADDFN, "rl_tstp",
                 if (e->el_map.func[i] == _el_rl_complete) {              "ReadLine compatible suspend function",
                         el_rl_complete_cmdnum = i;              _el_rl_tstp);
                         break;          el_set(e, EL_BIND, "^Z", "rl_tstp", NULL);
                 }  
         }          /*
            * Set some readline compatible key-bindings.
            */
           el_set(e, EL_BIND, "^R", "em-inc-search-prev", NULL);
   
           /*
            * Allow the use of Home/End keys.
            */
           el_set(e, EL_BIND, "\\e[1~", "ed-move-to-beg", NULL);
           el_set(e, EL_BIND, "\\e[4~", "ed-move-to-end", NULL);
           el_set(e, EL_BIND, "\\e[7~", "ed-move-to-beg", NULL);
           el_set(e, EL_BIND, "\\e[8~", "ed-move-to-end", NULL);
           el_set(e, EL_BIND, "\\e[H", "ed-move-to-beg", NULL);
           el_set(e, EL_BIND, "\\e[F", "ed-move-to-end", NULL);
   
           /*
            * Allow the use of the Delete/Insert keys.
            */
           el_set(e, EL_BIND, "\\e[3~", "ed-delete-next-char", NULL);
           el_set(e, EL_BIND, "\\e[2~", "ed-quoted-insert", NULL);
   
           /*
            * Ctrl-left-arrow and Ctrl-right-arrow for word moving.
            */
           el_set(e, EL_BIND, "\\e[1;5C", "em-next-word", NULL);
           el_set(e, EL_BIND, "\\e[1;5D", "ed-prev-word", NULL);
           el_set(e, EL_BIND, "\\e[5C", "em-next-word", NULL);
           el_set(e, EL_BIND, "\\e[5D", "ed-prev-word", NULL);
           el_set(e, EL_BIND, "\\e\\e[C", "em-next-word", NULL);
           el_set(e, EL_BIND, "\\e\\e[D", "ed-prev-word", NULL);
   
         /* read settings from configuration file */          /* read settings from configuration file */
         el_source(e, NULL);          el_source(e, NULL);
   
Line 238  rl_initialize(void)
Line 385  rl_initialize(void)
          * Unfortunately, some applications really do use rl_point           * Unfortunately, some applications really do use rl_point
          * and rl_line_buffer directly.           * and rl_line_buffer directly.
          */           */
         li = el_line(e);          _resize_fun(e, &rl_line_buffer);
         /* LINTED const cast */          _rl_update_pos();
         rl_line_buffer = (char *) li->buffer;  
         rl_point = rl_end = 0;          tty_end(e, TCSADRAIN);
   
         return (0);          return 0;
 }  }
   
   
Line 252  rl_initialize(void)
Line 399  rl_initialize(void)
  * trailing newline (if there is any)   * trailing newline (if there is any)
  */   */
 char *  char *
 readline(const char *prompt)  readline(const char *p)
 {  {
         HistEvent ev;          HistEvent ev;
           const char * volatile prompt = p;
         int count;          int count;
         const char *ret;          const char *ret;
           char *buf;
           static int used_event_hook;
   
         if (e == NULL || h == NULL)          if (e == NULL || h == NULL)
                 rl_initialize();                  rl_initialize();
           if (rl_did_startup_hook == 0 && rl_startup_hook) {
                   rl_did_startup_hook = 1;
                   (*rl_startup_hook)(NULL, 0);
           }
           tty_init(e);
   
   
           rl_done = 0;
   
           (void)setjmp(topbuf);
           buf = NULL;
   
         /* update prompt accordingly to what has been passed */          /* update prompt accordingly to what has been passed */
         if (!prompt)          if (rl_set_prompt(prompt) == -1)
                 prompt = "";                  goto out;
         if (strcmp(el_rl_prompt, prompt) != 0) {  
                 free(el_rl_prompt);          if (rl_pre_input_hook)
                 el_rl_prompt = strdup(prompt);                  (*rl_pre_input_hook)(NULL, 0);
   
           if (rl_event_hook && !(e->el_flags & NO_TTY)) {
                   el_set(e, EL_GETCFN, _rl_event_read_char);
                   used_event_hook = 1;
         }          }
   
           if (!rl_event_hook && used_event_hook) {
                   el_set(e, EL_GETCFN, EL_BUILTIN_GETCFN);
                   used_event_hook = 0;
           }
   
           rl_already_prompted = 0;
   
         /* get one line from input stream */          /* get one line from input stream */
         ret = el_gets(e, &count);          ret = el_gets(e, &count);
   
         if (ret && count > 0) {          if (ret && count > 0) {
                 char *foo;  
                 int lastidx;                  int lastidx;
   
                 foo = strdup(ret);                  buf = strdup(ret);
                   if (buf == NULL)
                           goto out;
                 lastidx = count - 1;                  lastidx = count - 1;
                 if (foo[lastidx] == '\n')                  if (buf[lastidx] == '\n')
                         foo[lastidx] = '\0';                          buf[lastidx] = '\0';
   
                 ret = foo;  
         } else          } else
                 ret = NULL;                  buf = NULL;
   
         history(h, &ev, H_GETSIZE);          history(h, &ev, H_GETSIZE);
         history_length = ev.num;          history_length = ev.num;
   
         /* LINTED const cast */  out:
         return (char *) ret;          tty_end(e, TCSADRAIN);
           return buf;
 }  }
   
 /*  /*
Line 304  using_history(void)
Line 477  using_history(void)
 {  {
         if (h == NULL || e == NULL)          if (h == NULL || e == NULL)
                 rl_initialize();                  rl_initialize();
           history_offset = history_length;
 }  }
   
   
 /*  /*
  * substitute ``what'' with ``with'', returning resulting string; if   * substitute ``what'' with ``with'', returning resulting string; if
  * globally == 1, substitutes all occurences of what, otherwise only the   * globally == 1, substitutes all occurrences of what, otherwise only the
  * first one   * first one
  */   */
 static char *  static char *
 _rl_compat_sub(const char *str, const char *what, const char *with,  _rl_compat_sub(const char *str, const char *what, const char *with,
     int globally)      int globally)
 {  {
         char *result;          const   char    *s;
         const char *temp, *new;          char    *r, *result;
         int len, with_len, what_len, add;          size_t  len, with_len, what_len;
         size_t size, i;  
   
         result = malloc((size = 16));          len = strlen(str);
         temp = str;  
         with_len = strlen(with);          with_len = strlen(with);
         what_len = strlen(what);          what_len = strlen(what);
         len = 0;  
         do {          /* calculate length we need for result */
                 new = strstr(temp, what);          s = str;
                 if (new) {          while (*s) {
                         i = new - temp;                  if (*s == *what && !strncmp(s, what, what_len)) {
                         add = i + with_len;                          len += with_len - what_len;
                         if (i + add + 1 >= size) {                          if (!globally)
                                 size += add + 1;                                  break;
                                 result = realloc(result, size);                          s += what_len;
                         }                  } else
                         (void) strncpy(&result[len], temp, i);                          s++;
                         len += i;          }
                         (void) strcpy(&result[len], with);      /* safe */          r = result = el_malloc((len + 1) * sizeof(*r));
                         len += with_len;          if (result == NULL)
                         temp = new + what_len;                  return NULL;
                 } else {          s = str;
                         add = strlen(temp);          while (*s) {
                         if (len + add + 1 >= size) {                  if (*s == *what && !strncmp(s, what, what_len)) {
                                 size += add + 1;                          (void)strncpy(r, with, with_len);
                                 result = realloc(result, size);                          r += with_len;
                           s += what_len;
                           if (!globally) {
                                   (void)strcpy(r, s);
                                   return result;
                         }                          }
                         (void) strcpy(&result[len], temp);      /* safe */                  } else
                         len += add;                          *r++ = *s++;
                         temp = NULL;          }
           *r = '\0';
           return result;
   }
   
   static  char    *last_search_pat;       /* last !?pat[?] search pattern */
   static  char    *last_search_match;     /* last !?pat[?] that matched */
   
   const char *
   get_history_event(const char *cmd, int *cindex, int qchar)
   {
           int idx, sign, sub, num, begin, ret;
           size_t len;
           char    *pat;
           const char *rptr;
           HistEvent ev;
   
           idx = *cindex;
           if (cmd[idx++] != history_expansion_char)
                   return NULL;
   
           /* find out which event to take */
           if (cmd[idx] == history_expansion_char || cmd[idx] == '\0') {
                   if (history(h, &ev, H_FIRST) != 0)
                           return NULL;
                   *cindex = cmd[idx]? (idx + 1):idx;
                   return ev.str;
           }
           sign = 0;
           if (cmd[idx] == '-') {
                   sign = 1;
                   idx++;
           }
   
           if ('0' <= cmd[idx] && cmd[idx] <= '9') {
                   HIST_ENTRY *he;
   
                   num = 0;
                   while (cmd[idx] && '0' <= cmd[idx] && cmd[idx] <= '9') {
                           num = num * 10 + cmd[idx] - '0';
                           idx++;
                 }                  }
         } while (temp && globally);                  if (sign)
         result[len] = '\0';                          num = history_length - num + history_base;
   
         return (result);                  if (!(he = history_get(num)))
 }                          return NULL;
   
                   *cindex = idx;
                   return he->line;
           }
           sub = 0;
           if (cmd[idx] == '?') {
                   sub = 1;
                   idx++;
           }
           begin = idx;
           while (cmd[idx]) {
                   if (cmd[idx] == '\n')
                           break;
                   if (sub && cmd[idx] == '?')
                           break;
                   if (!sub && (cmd[idx] == ':' || cmd[idx] == ' '
                                       || cmd[idx] == '\t' || cmd[idx] == qchar))
                           break;
                   idx++;
           }
           len = (size_t)idx - (size_t)begin;
           if (sub && cmd[idx] == '?')
                   idx++;
           if (sub && len == 0 && last_search_pat && *last_search_pat)
                   pat = last_search_pat;
           else if (len == 0)
                   return NULL;
           else {
                   if ((pat = el_malloc((len + 1) * sizeof(*pat))) == NULL)
                           return NULL;
                   (void)strncpy(pat, cmd + begin, len);
                   pat[len] = '\0';
           }
   
           if (history(h, &ev, H_CURR) != 0) {
                   if (pat != last_search_pat)
                           el_free(pat);
                   return NULL;
           }
           num = ev.num;
   
           if (sub) {
                   if (pat != last_search_pat) {
                           if (last_search_pat)
                                   el_free(last_search_pat);
                           last_search_pat = pat;
                   }
                   ret = history_search(pat, -1);
           } else
                   ret = history_search_prefix(pat, -1);
   
           if (ret == -1) {
                   /* restore to end of list on failed search */
                   history(h, &ev, H_FIRST);
                   (void)fprintf(rl_outstream, "%s: Event not found\n", pat);
                   if (pat != last_search_pat)
                           el_free(pat);
                   return NULL;
           }
   
           if (sub && len) {
                   if (last_search_match && last_search_match != pat)
                           el_free(last_search_match);
                   last_search_match = pat;
           }
   
           if (pat != last_search_pat)
                   el_free(pat);
   
           if (history(h, &ev, H_CURR) != 0)
                   return NULL;
           *cindex = idx;
           rptr = ev.str;
   
           /* roll back to original position */
           (void)history(h, &ev, H_SET, num);
   
           return rptr;
   }
   
 /*  /*
  * the real function doing history expansion - takes as argument command   * the real function doing history expansion - takes as argument command
  * to do and data upon which the command should be executed   * to do and data upon which the command should be executed
  * does expansion the way I've understood readline documentation   * does expansion the way I've understood readline documentation
  * word designator ``%'' isn't supported (yet ?)  
  *   *
  * returns 0 if data was not modified, 1 if it was and 2 if the string   * returns 0 if data was not modified, 1 if it was and 2 if the string
  * should be only printed and not executed; in case of error,   * should be only printed and not executed; in case of error,
  * returns -1 and *result points to NULL   * returns -1 and *result points to NULL
  * it's callers responsibility to free() string returned in *result   * it's the caller's responsibility to free() the string returned in *result
  */   */
 static int  static int
 _history_expand_command(const char *command, size_t cmdlen, char **result)  _history_expand_command(const char *command, size_t offs, size_t cmdlen,
       char **result)
 {  {
         char **arr, *tempcmd, *line, *search = NULL, *cmd;          char *tmp, *search = NULL, *aptr;
         const char *event_data = NULL;          const char *ptr, *cmd;
         static char *from = NULL, *to = NULL;          static char *from = NULL, *to = NULL;
         int start = -1, end = -1, max, i, idx;          int start, end, idx, has_mods = 0;
         int h_on = 0, t_on = 0, r_on = 0, e_on = 0, p_on = 0, g_on = 0;          int p_on = 0, g_on = 0;
         int event_num = 0, retval;  
         size_t cmdsize;  
   
         *result = NULL;          *result = NULL;
           aptr = NULL;
           ptr = NULL;
   
         cmd = alloca(cmdlen + 1);          /* First get event specifier */
         (void) strncpy(cmd, command, cmdlen);          idx = 0;
         cmd[cmdlen] = 0;  
   
         idx = 1;          if (strchr(":^*$", command[offs + 1])) {
         /* find out which event to take */                  char str[4];
         if (cmd[idx] == history_expansion_char) {                  /*
                 event_num = history_length;                  * "!:" is shorthand for "!!:".
                 idx++;                  * "!^", "!*" and "!$" are shorthand for
                   * "!!:^", "!!:*" and "!!:$" respectively.
                   */
                   str[0] = str[1] = '!';
                   str[2] = '0';
                   ptr = get_history_event(str, &idx, 0);
                   idx = (command[offs + 1] == ':')? 1:0;
                   has_mods = 1;
         } else {          } else {
                 int off, num;                  if (command[offs + 1] == '#') {
                 size_t len;                          /* use command so far */
                 off = idx;                          if ((aptr = el_malloc((offs + 1) * sizeof(*aptr)))
                 while (cmd[off] && !strchr(":^$*-%", cmd[off]))                              == NULL)
                         off++;                                  return -1;
                 num = atoi(&cmd[idx]);                          (void)strncpy(aptr, command, offs);
                 if (num != 0) {                          aptr[offs] = '\0';
                         event_num = num;                          idx = 1;
                         if (num < 0)  
                                 event_num += history_length + 1;  
                 } else {                  } else {
                         int prefix = 1, curr_num;                          int     qchar;
                         HistEvent ev;  
   
                         len = off - idx;                          qchar = (offs > 0 && command[offs - 1] == '"')? '"':0;
                         if (cmd[idx] == '?') {                          ptr = get_history_event(command + offs, &idx, qchar);
                                 idx++, len--;                  }
                                 if (cmd[off - 1] == '?')                  has_mods = command[offs + (size_t)idx] == ':';
                                         len--;          }
                                 else if (cmd[off] != '\n' && cmd[off] != '\0')  
                                         return (-1);  
                                 prefix = 0;  
                         }  
                         search = alloca(len + 1);  
                         (void) strncpy(search, &cmd[idx], len);  
                         search[len] = '\0';  
   
                         if (history(h, &ev, H_CURR) != 0)  
                                 return (-1);  
                         curr_num = ev.num;  
   
                         if (prefix)          if (ptr == NULL && aptr == NULL)
                                 retval = history_search_prefix(search, -1);                  return -1;
                         else  
                                 retval = history_search(search, -1);  
   
                         if (retval == -1) {          if (!has_mods) {
                                 fprintf(rl_outstream, "%s: Event not found\n",                  *result = strdup(aptr ? aptr : ptr);
                                     search);                  if (aptr)
                                 return (-1);                          el_free(aptr);
                         }                  if (*result == NULL)
                         if (history(h, &ev, H_CURR) != 0)                          return -1;
                                 return (-1);                  return 1;
                         event_data = ev.str;          }
   
                         /* roll back to original position */          cmd = command + offs + idx + 1;
                         history(h, &ev, H_NEXT_EVENT, curr_num);  
                 }          /* Now parse any word designators */
                 idx = off;  
         }          if (*cmd == '%')        /* last word matched by ?pat? */
                   tmp = strdup(last_search_match? last_search_match:"");
         if (!event_data && event_num >= 0) {          else if (strchr("^*$-0123456789", *cmd)) {
                 HIST_ENTRY *rl_he;                  start = end = -1;
                 rl_he = history_get(event_num);                  if (*cmd == '^')
                 if (!rl_he)                          start = end = 1, cmd++;
                         return (0);                  else if (*cmd == '$')
                 event_data = rl_he->line;                          start = -1, cmd++;
                   else if (*cmd == '*')
                           start = 1, cmd++;
                  else if (*cmd == '-' || isdigit((unsigned char) *cmd)) {
                           start = 0;
                           while (*cmd && '0' <= *cmd && *cmd <= '9')
                                   start = start * 10 + *cmd++ - '0';
   
                           if (*cmd == '-') {
                                   if (isdigit((unsigned char) cmd[1])) {
                                           cmd++;
                                           end = 0;
                                           while (*cmd && '0' <= *cmd && *cmd <= '9')
                                                   end = end * 10 + *cmd++ - '0';
                                   } else if (cmd[1] == '$') {
                                           cmd += 2;
                                           end = -1;
                                   } else {
                                           cmd++;
                                           end = -2;
                                   }
                           } else if (*cmd == '*')
                                   end = -1, cmd++;
                           else
                                   end = start;
                   }
                   tmp = history_arg_extract(start, end, aptr? aptr:ptr);
                   if (tmp == NULL) {
                           (void)fprintf(rl_outstream, "%s: Bad word specifier",
                               command + offs + idx);
                           if (aptr)
                                   el_free(aptr);
                           return -1;
                   }
         } else          } else
                 return (-1);                  tmp = strdup(aptr? aptr:ptr);
   
         if (cmd[idx] != ':')          if (aptr)
                 return (-1);                  el_free(aptr);
         cmd += idx + 1;  
           if (*cmd == '\0' || ((size_t)(cmd - (command + offs)) >= cmdlen)) {
         /* recognize cmd */                  *result = tmp;
         if (*cmd == '^')                  return 1;
                 start = end = 1, cmd++;  
         else if (*cmd == '$')  
                 start = end = -1, cmd++;  
         else if (*cmd == '*')  
                 start = 1, end = -1, cmd++;  
         else if (isdigit((unsigned char) *cmd)) {  
                 const char *temp;  
                 int shifted = 0;  
   
                 start = atoi(cmd);  
                 temp = cmd;  
                 for (; isdigit((unsigned char) *cmd); cmd++);  
                 if (temp != cmd)  
                         shifted = 1;  
                 if (shifted && *cmd == '-') {  
                         if (!isdigit((unsigned char) *(cmd + 1)))  
                                 end = -2;  
                         else {  
                                 end = atoi(cmd + 1);  
                                 for (; isdigit((unsigned char) *cmd); cmd++);  
                         }  
                 } else if (shifted && *cmd == '*')  
                         end = -1, cmd++;  
                 else if (shifted)  
                         end = start;  
         }          }
         if (*cmd == ':')  
                 cmd++;  
   
         line = strdup(event_data);  
         for (; *cmd; cmd++) {          for (; *cmd; cmd++) {
                 if (*cmd == ':')                  if (*cmd == ':')
                         continue;                          continue;
                 else if (*cmd == 'h')                  else if (*cmd == 'h') {         /* remove trailing path */
                         h_on = 1 | g_on, g_on = 0;                          if ((aptr = strrchr(tmp, '/')) != NULL)
                 else if (*cmd == 't')                                  *aptr = '\0';
                         t_on = 1 | g_on, g_on = 0;                  } else if (*cmd == 't') {       /* remove leading path */
                 else if (*cmd == 'r')                          if ((aptr = strrchr(tmp, '/')) != NULL) {
                         r_on = 1 | g_on, g_on = 0;                                  aptr = strdup(aptr + 1);
                 else if (*cmd == 'e')                                  el_free(tmp);
                         e_on = 1 | g_on, g_on = 0;                                  tmp = aptr;
                 else if (*cmd == 'p')                          }
                         p_on = 1 | g_on, g_on = 0;                  } else if (*cmd == 'r') {       /* remove trailing suffix */
                           if ((aptr = strrchr(tmp, '.')) != NULL)
                                   *aptr = '\0';
                   } else if (*cmd == 'e') {       /* remove all but suffix */
                           if ((aptr = strrchr(tmp, '.')) != NULL) {
                                   aptr = strdup(aptr);
                                   el_free(tmp);
                                   tmp = aptr;
                           }
                   } else if (*cmd == 'p')         /* print only */
                           p_on = 1;
                 else if (*cmd == 'g')                  else if (*cmd == 'g')
                         g_on = 2;                          g_on = 2;
                 else if (*cmd == 's' || *cmd == '&') {                  else if (*cmd == 's' || *cmd == '&') {
                         char *what, *with, delim;                          char *what, *with, delim;
                         int len, from_len;                          size_t len, from_len;
                         size_t size;                          size_t size;
   
                         if (*cmd == '&' && (from == NULL || to == NULL))                          if (*cmd == '&' && (from == NULL || to == NULL))
Line 512  _history_expand_command(const char *comm
Line 811  _history_expand_command(const char *comm
                         else if (*cmd == 's') {                          else if (*cmd == 's') {
                                 delim = *(++cmd), cmd++;                                  delim = *(++cmd), cmd++;
                                 size = 16;                                  size = 16;
                                 what = realloc(from, size);                                  what = el_realloc(from, size * sizeof(*what));
                                   if (what == NULL) {
                                           el_free(from);
                                           el_free(tmp);
                                           return 0;
                                   }
                                 len = 0;                                  len = 0;
                                 for (; *cmd && *cmd != delim; cmd++) {                                  for (; *cmd && *cmd != delim; cmd++) {
                                         if (*cmd == '\\'                                          if (*cmd == '\\' && cmd[1] == delim)
                                             && *(cmd + 1) == delim)  
                                                 cmd++;                                                  cmd++;
                                         if (len >= size)                                          if (len >= size) {
                                                 what = realloc(what,                                                  char *nwhat;
                                                     (size <<= 1));                                                  nwhat = el_realloc(what,
                                                       (size <<= 1) *
                                                       sizeof(*nwhat));
                                                   if (nwhat == NULL) {
                                                           el_free(what);
                                                           el_free(tmp);
                                                           return 0;
                                                   }
                                                   what = nwhat;
                                           }
                                         what[len++] = *cmd;                                          what[len++] = *cmd;
                                 }                                  }
                                 what[len] = '\0';                                  what[len] = '\0';
                                 from = what;                                  from = what;
                                 if (*what == '\0') {                                  if (*what == '\0') {
                                         free(what);                                          el_free(what);
                                         if (search)                                          if (search) {
                                                 from = strdup(search);                                                  from = strdup(search);
                                         else {                                                  if (from == NULL) {
                                                           el_free(tmp);
                                                           return 0;
                                                   }
                                           } else {
                                                 from = NULL;                                                  from = NULL;
                                                 return (-1);                                                  el_free(tmp);
                                                   return -1;
                                         }                                          }
                                 }                                  }
                                 cmd++;  /* shift after delim */                                  cmd++;  /* shift after delim */
Line 539  _history_expand_command(const char *comm
Line 856  _history_expand_command(const char *comm
                                         continue;                                          continue;
   
                                 size = 16;                                  size = 16;
                                 with = realloc(to, size);                                  with = el_realloc(to, size * sizeof(*with));
                                   if (with == NULL) {
                                           el_free(to);
                                           el_free(tmp);
                                           return -1;
                                   }
                                 len = 0;                                  len = 0;
                                 from_len = strlen(from);                                  from_len = strlen(from);
                                 for (; *cmd && *cmd != delim; cmd++) {                                  for (; *cmd && *cmd != delim; cmd++) {
                                         if (len + from_len + 1 >= size) {                                          if (len + from_len + 1 >= size) {
                                                   char *nwith;
                                                 size += from_len + 1;                                                  size += from_len + 1;
                                                 with = realloc(with, size);                                                  nwith = el_realloc(with,
                                                       size * sizeof(*nwith));
                                                   if (nwith == NULL) {
                                                           el_free(with);
                                                           el_free(tmp);
                                                           return -1;
                                                   }
                                                   with = nwith;
                                         }                                          }
                                         if (*cmd == '&') {                                          if (*cmd == '&') {
                                                 /* safe */                                                  /* safe */
                                                 (void) strcpy(&with[len], from);                                                  (void)strcpy(&with[len], from);
                                                 len += from_len;                                                  len += from_len;
                                                 continue;                                                  continue;
                                         }                                          }
Line 561  _history_expand_command(const char *comm
Line 891  _history_expand_command(const char *comm
                                 }                                  }
                                 with[len] = '\0';                                  with[len] = '\0';
                                 to = with;                                  to = with;
                           }
   
                                 tempcmd = _rl_compat_sub(line, from, to,                          aptr = _rl_compat_sub(tmp, from, to, g_on);
                                     (g_on) ? 1 : 0);                          if (aptr) {
                                 free(line);                                  el_free(tmp);
                                 line = tempcmd;                                  tmp = aptr;
                                 g_on = 0;  
                         }                          }
                           g_on = 0;
                 }                  }
         }          }
           *result = tmp;
         arr = history_tokenize(line);          return p_on? 2:1;
         free(line);             /* no more needed */  
         if (arr && *arr == NULL)  
                 free(arr), arr = NULL;  
         if (!arr)  
                 return (-1);  
   
         /* find out max valid idx to array of array */  
         max = 0;  
         for (i = 0; arr[i]; i++)  
                 max++;  
         max--;  
   
         /* set boundaries to something relevant */  
         if (start < 0)  
                 start = 1;  
         if (end < 0)  
                 end = max - ((end < -1) ? 1 : 0);  
   
         /* check boundaries ... */  
         if (start > max || end > max || start > end)  
                 return (-1);  
   
         for (i = 0; i <= max; i++) {  
                 char *temp;  
                 if (h_on && (i == 1 || h_on > 1) &&  
                     (temp = strrchr(arr[i], '/')))  
                         *(temp + 1) = '\0';  
                 if (t_on && (i == 1 || t_on > 1) &&  
                     (temp = strrchr(arr[i], '/')))  
                         (void) strcpy(arr[i], temp + 1);  
                 if (r_on && (i == 1 || r_on > 1) &&  
                     (temp = strrchr(arr[i], '.')))  
                         *temp = '\0';  
                 if (e_on && (i == 1 || e_on > 1) &&  
                     (temp = strrchr(arr[i], '.')))  
                         (void) strcpy(arr[i], temp);  
         }  
   
         cmdsize = 1, cmdlen = 0;  
         tempcmd = malloc(cmdsize);  
         for (i = start; start <= i && i <= end; i++) {  
                 int arr_len;  
   
                 arr_len = strlen(arr[i]);  
                 if (cmdlen + arr_len + 1 >= cmdsize) {  
                         cmdsize += arr_len + 1;  
                         tempcmd = realloc(tempcmd, cmdsize);  
                 }  
                 (void) strcpy(&tempcmd[cmdlen], arr[i]);        /* safe */  
                 cmdlen += arr_len;  
                 tempcmd[cmdlen++] = ' ';        /* add a space */  
         }  
         while (cmdlen > 0 && isspace((unsigned char) tempcmd[cmdlen - 1]))  
                 cmdlen--;  
         tempcmd[cmdlen] = '\0';  
   
         *result = tempcmd;  
   
         for (i = 0; i <= max; i++)  
                 free(arr[i]);  
         free(arr), arr = (char **) NULL;  
         return (p_on) ? 2 : 1;  
 }  }
   
   
Line 643  _history_expand_command(const char *comm
Line 912  _history_expand_command(const char *comm
 int  int
 history_expand(char *str, char **output)  history_expand(char *str, char **output)
 {  {
         int i, retval = 0, idx;          int ret = 0;
         size_t size;          size_t idx, i, size;
         char *temp, *result;          char *tmp, *result;
   
         if (h == NULL || e == NULL)          if (h == NULL || e == NULL)
                 rl_initialize();                  rl_initialize();
   
         *output = strdup(str);  /* do it early */          if (history_expansion_char == 0) {
                   *output = strdup(str);
                   return 0;
           }
   
           *output = NULL;
         if (str[0] == history_subst_char) {          if (str[0] == history_subst_char) {
                 /* ^foo^foo2^ is equivalent to !!:s^foo^foo2^ */                  /* ^foo^foo2^ is equivalent to !!:s^foo^foo2^ */
                 temp = alloca(4 + strlen(str) + 1);                  *output = el_malloc((strlen(str) + 4 + 1) * sizeof(**output));
                 temp[0] = temp[1] = history_expansion_char;                  if (*output == NULL)
                 temp[2] = ':';                          return 0;
                 temp[3] = 's';                  (*output)[0] = (*output)[1] = history_expansion_char;
                 (void) strcpy(temp + 4, str);                  (*output)[2] = ':';
                 str = temp;                  (*output)[3] = 's';
                   (void)strcpy((*output) + 4, str);
                   str = *output;
           } else {
                   *output = strdup(str);
                   if (*output == NULL)
                           return 0;
         }          }
 #define ADD_STRING(what, len)                                           \  
   #define ADD_STRING(what, len, fr)                                       \
         {                                                               \          {                                                               \
                 if (idx + len + 1 > size)                               \                  if (idx + len + 1 > size) {                             \
                         result = realloc(result, (size += len + 1));    \                          char *nresult = el_realloc(result,              \
                               (size += len + 1) * sizeof(*nresult));      \
                           if (nresult == NULL) {                          \
                                   el_free(*output);                       \
                                   if (/*CONSTCOND*/fr)                    \
                                           el_free(tmp);                   \
                                   return 0;                               \
                           }                                               \
                           result = nresult;                               \
                   }                                                       \
                 (void)strncpy(&result[idx], what, len);                 \                  (void)strncpy(&result[idx], what, len);                 \
                 idx += len;                                             \                  idx += len;                                             \
                 result[idx] = '\0';                                     \                  result[idx] = '\0';                                     \
Line 672  history_expand(char *str, char **output)
Line 961  history_expand(char *str, char **output)
   
         result = NULL;          result = NULL;
         size = idx = 0;          size = idx = 0;
           tmp = NULL;
         for (i = 0; str[i];) {          for (i = 0; str[i];) {
                 int start, j, loop_again;                  int qchar, loop_again;
                 size_t len;                  size_t len, start, j;
   
                   qchar = 0;
                 loop_again = 1;                  loop_again = 1;
                 start = j = i;                  start = j = i;
 loop:  loop:
                 for (; str[j]; j++) {                  for (; str[j]; j++) {
                         if (str[j] == '\\' &&                          if (str[j] == '\\' &&
                             str[j + 1] == history_expansion_char) {                              str[j + 1] == history_expansion_char) {
                                 (void) strcpy(&str[j], &str[j + 1]);                                  len = strlen(&str[j + 1]) + 1;
                                   memmove(&str[j], &str[j + 1], len);
                                 continue;                                  continue;
                         }                          }
                         if (!loop_again) {                          if (!loop_again) {
                                 if (str[j] == '?') {                                  if (isspace((unsigned char) str[j])
                                         while (str[j] && str[++j] != '?');                                      || str[j] == qchar)
                                         if (str[j] == '?')  
                                                 j++;  
                                 } else if (isspace((unsigned char) str[j]))  
                                         break;                                          break;
                         }                          }
                         if (str[j] == history_expansion_char                          if (str[j] == history_expansion_char
                             && !strchr(history_no_expand_chars, str[j + 1])                              && !strchr(history_no_expand_chars, str[j + 1])
                             && (!history_inhibit_expansion_function ||                              && (!history_inhibit_expansion_function ||
                             (*history_inhibit_expansion_function)(str, j) == 0))                              (*history_inhibit_expansion_function)(str,
                               (int)j) == 0))
                                 break;                                  break;
                 }                  }
   
                 if (str[j] && str[j + 1] != '#' && loop_again) {                  if (str[j] && loop_again) {
                         i = j;                          i = j;
                           qchar = (j > 0 && str[j - 1] == '"' )? '"':0;
                         j++;                          j++;
                         if (str[j] == history_expansion_char)                          if (str[j] == history_expansion_char)
                                 j++;                                  j++;
Line 709  loop:
Line 1000  loop:
                         goto loop;                          goto loop;
                 }                  }
                 len = i - start;                  len = i - start;
                 temp = &str[start];                  ADD_STRING(&str[start], len, 0);
                 ADD_STRING(temp, len);  
   
                 if (str[i] == '\0' || str[i] != history_expansion_char                  if (str[i] == '\0' || str[i] != history_expansion_char) {
                     || str[i + 1] == '#') {  
                         len = j - i;                          len = j - i;
                         temp = &str[i];                          ADD_STRING(&str[i], len, 0);
                         ADD_STRING(temp, len);  
                         if (start == 0)                          if (start == 0)
                                 retval = 0;                                  ret = 0;
                         else                          else
                                 retval = 1;                                  ret = 1;
                         break;                          break;
                 }                  }
                 retval = _history_expand_command(&str[i], (size_t) (j - i),                  ret = _history_expand_command (str, i, (j - i), &tmp);
                     &temp);                  if (ret > 0 && tmp) {
                 if (retval != -1) {                          len = strlen(tmp);
                         len = strlen(temp);                          ADD_STRING(tmp, len, 1);
                         ADD_STRING(temp, len);                  }
                   if (tmp) {
                           el_free(tmp);
                           tmp = NULL;
                 }                  }
                 i = j;                  i = j;
         }                       /* for(i ...) */          }
   
         if (retval == 2) {          /* ret is 2 for "print only" option */
                 add_history(temp);          if (ret == 2) {
                   add_history(result);
 #ifdef GDB_411_HACK  #ifdef GDB_411_HACK
                 /* gdb 4.11 has been shipped with readline, where */                  /* gdb 4.11 has been shipped with readline, where */
                 /* history_expand() returned -1 when the line     */                  /* history_expand() returned -1 when the line     */
                 /* should not be executed; in readline 2.1+       */                  /* should not be executed; in readline 2.1+       */
                 /* it should return 2 in such a case              */                  /* it should return 2 in such a case              */
                 retval = -1;                  ret = -1;
 #endif  #endif
         }          }
         free(*output);          el_free(*output);
         *output = result;          *output = result;
   
         return (retval);          return ret;
 }  }
   
   /*
   * Return a string consisting of arguments of "str" from "start" to "end".
   */
   char *
   history_arg_extract(int start, int end, const char *str)
   {
           size_t  i, len, max;
           char    **arr, *result = NULL;
   
           arr = history_tokenize(str);
           if (!arr)
                   return NULL;
           if (arr && *arr == NULL)
                   goto out;
   
           for (max = 0; arr[max]; max++)
                   continue;
           max--;
   
           if (start == '$')
                   start = (int)max;
           if (end == '$')
                   end = (int)max;
           if (end < 0)
                   end = (int)max + end + 1;
           if (start < 0)
                   start = end;
   
           if (start < 0 || end < 0 || (size_t)start > max ||
               (size_t)end > max || start > end)
                   goto out;
   
           for (i = (size_t)start, len = 0; i <= (size_t)end; i++)
                   len += strlen(arr[i]) + 1;
           len++;
           result = el_malloc(len * sizeof(*result));
           if (result == NULL)
                   goto out;
   
           for (i = (size_t)start, len = 0; i <= (size_t)end; i++) {
                   (void)strcpy(result + len, arr[i]);
                   len += strlen(arr[i]);
                   if (i < (size_t)end)
                           result[len++] = ' ';
           }
           result[len] = '\0';
   
   out:
           for (i = 0; arr[i]; i++)
                   el_free(arr[i]);
           el_free(arr);
   
           return result;
   }
   
 /*  /*
  * Parse the string into individual tokens, similarily to how shell would do it.   * Parse the string into individual tokens,
    * similar to how shell would do it.
  */   */
 char **  char **
 history_tokenize(const char *str)  history_tokenize(const char *str)
 {  {
         int size = 1, result_idx = 0, i, start;          int size = 1, idx = 0, i, start;
         size_t len;          size_t len;
         char **result = NULL, *temp, delim = '\0';          char **result = NULL, *temp, delim = '\0';
   
         for (i = 0; str[i]; i++) {          for (i = 0; str[i];) {
                 while (isspace((unsigned char) str[i]))                  while (isspace((unsigned char) str[i]))
                         i++;                          i++;
                 start = i;                  start = i;
                 for (; str[i]; i++) {                  for (; str[i];) {
                         if (str[i] == '\\') {                          if (str[i] == '\\') {
                                 if (str[i+1] != '\0')                                  if (str[i+1] != '\0')
                                         i++;                                          i++;
Line 775  history_tokenize(const char *str)
Line 1122  history_tokenize(const char *str)
                                 break;                                  break;
                         else if (!delim && strchr("'`\"", str[i]))                          else if (!delim && strchr("'`\"", str[i]))
                                 delim = str[i];                                  delim = str[i];
                           if (str[i])
                                   i++;
                 }                  }
   
                 if (result_idx + 2 >= size) {                  if (idx + 2 >= size) {
                           char **nresult;
                         size <<= 1;                          size <<= 1;
                         result = realloc(result, size * sizeof(char *));                          nresult = el_realloc(result, (size_t)size * sizeof(*nresult));
                           if (nresult == NULL) {
                                   el_free(result);
                                   return NULL;
                           }
                           result = nresult;
                 }                  }
                 len = i - start;                  len = (size_t)i - (size_t)start;
                 temp = malloc(len + 1);                  temp = el_malloc((size_t)(len + 1) * sizeof(*temp));
                 (void) strncpy(temp, &str[start], len);                  if (temp == NULL) {
                           for (i = 0; i < idx; i++)
                                   el_free(result[i]);
                           el_free(result);
                           return NULL;
                   }
                   (void)strncpy(temp, &str[start], len);
                 temp[len] = '\0';                  temp[len] = '\0';
                 result[result_idx++] = temp;                  result[idx++] = temp;
                 result[result_idx] = NULL;                  result[idx] = NULL;
                   if (str[i])
                           i++;
         }          }
           return result;
         return (result);  
 }  }
   
   
Line 800  void
Line 1162  void
 stifle_history(int max)  stifle_history(int max)
 {  {
         HistEvent ev;          HistEvent ev;
           HIST_ENTRY *he;
   
         if (h == NULL || e == NULL)          if (h == NULL || e == NULL)
                 rl_initialize();                  rl_initialize();
   
         if (history(h, &ev, H_SETSIZE, max) == 0)          if (history(h, &ev, H_SETSIZE, max) == 0) {
                 max_input_history = max;                  max_input_history = max;
                   if (history_length > max)
                           history_base = history_length - max;
                   while (history_length > max) {
                           he = remove_history(0);
                           el_free(he->data);
                           el_free((void *)(unsigned long)he->line);
                           el_free(he);
                   }
           }
 }  }
   
   
Line 821  unstifle_history(void)
Line 1193  unstifle_history(void)
         history(h, &ev, H_SETSIZE, INT_MAX);          history(h, &ev, H_SETSIZE, INT_MAX);
         omax = max_input_history;          omax = max_input_history;
         max_input_history = INT_MAX;          max_input_history = INT_MAX;
         return (omax);          /* some value _must_ be returned */          return omax;            /* some value _must_ be returned */
 }  }
   
   
Line 830  history_is_stifled(void)
Line 1202  history_is_stifled(void)
 {  {
   
         /* cannot return true answer */          /* cannot return true answer */
         return (max_input_history != INT_MAX);          return max_input_history != INT_MAX;
   }
   
   static const char _history_tmp_template[] = "/tmp/.historyXXXXXX";
   
   int
   history_truncate_file (const char *filename, int nlines)
   {
           int ret = 0;
           FILE *fp, *tp;
           char template[sizeof(_history_tmp_template)];
           char buf[4096];
           int fd;
           char *cp;
           off_t off;
           int count = 0;
           ssize_t left = 0;
   
           if (filename == NULL && (filename = _default_history_file()) == NULL)
                   return errno;
           if ((fp = fopen(filename, "r+")) == NULL)
                   return errno;
           strcpy(template, _history_tmp_template);
           if ((fd = mkstemp(template)) == -1) {
                   ret = errno;
                   goto out1;
           }
   
           if ((tp = fdopen(fd, "r+")) == NULL) {
                   close(fd);
                   ret = errno;
                   goto out2;
           }
   
           for(;;) {
                   if (fread(buf, sizeof(buf), (size_t)1, fp) != 1) {
                           if (ferror(fp)) {
                                   ret = errno;
                                   break;
                           }
                           if (fseeko(fp, (off_t)sizeof(buf) * count, SEEK_SET) ==
                               (off_t)-1) {
                                   ret = errno;
                                   break;
                           }
                           left = (ssize_t)fread(buf, (size_t)1, sizeof(buf), fp);
                           if (ferror(fp)) {
                                   ret = errno;
                                   break;
                           }
                           if (left == 0) {
                                   count--;
                                   left = sizeof(buf);
                           } else if (fwrite(buf, (size_t)left, (size_t)1, tp)
                               != 1) {
                                   ret = errno;
                                   break;
                           }
                           fflush(tp);
                           break;
                   }
                   if (fwrite(buf, sizeof(buf), (size_t)1, tp) != 1) {
                           ret = errno;
                           break;
                   }
                   count++;
           }
           if (ret)
                   goto out3;
           cp = buf + left - 1;
           if(*cp != '\n')
                   cp++;
           for(;;) {
                   while (--cp >= buf) {
                           if (*cp == '\n') {
                                   if (--nlines == 0) {
                                           if (++cp >= buf + sizeof(buf)) {
                                                   count++;
                                                   cp = buf;
                                           }
                                           break;
                                   }
                           }
                   }
                   if (nlines <= 0 || count == 0)
                           break;
                   count--;
                   if (fseeko(tp, (off_t)sizeof(buf) * count, SEEK_SET) < 0) {
                           ret = errno;
                           break;
                   }
                   if (fread(buf, sizeof(buf), (size_t)1, tp) != 1) {
                           if (ferror(tp)) {
                                   ret = errno;
                                   break;
                           }
                           ret = EAGAIN;
                           break;
                   }
                   cp = buf + sizeof(buf);
           }
   
           if (ret || nlines > 0)
                   goto out3;
   
           if (fseeko(fp, (off_t)0, SEEK_SET) == (off_t)-1) {
                   ret = errno;
                   goto out3;
           }
   
           if (fseeko(tp, (off_t)sizeof(buf) * count + (cp - buf), SEEK_SET) ==
               (off_t)-1) {
                   ret = errno;
                   goto out3;
           }
   
           for(;;) {
                   if ((left = (ssize_t)fread(buf, (size_t)1, sizeof(buf), tp)) == 0) {
                           if (ferror(fp))
                                   ret = errno;
                           break;
                   }
                   if (fwrite(buf, (size_t)left, (size_t)1, fp) != 1) {
                           ret = errno;
                           break;
                   }
           }
           fflush(fp);
           if((off = ftello(fp)) > 0)
                   (void)ftruncate(fileno(fp), off);
   out3:
           fclose(tp);
   out2:
           unlink(template);
   out1:
           fclose(fp);
   
           return ret;
 }  }
   
   
Line 844  read_history(const char *filename)
Line 1353  read_history(const char *filename)
   
         if (h == NULL || e == NULL)          if (h == NULL || e == NULL)
                 rl_initialize();                  rl_initialize();
         return (history(h, &ev, H_LOAD, filename));          if (filename == NULL && (filename = _default_history_file()) == NULL)
                   return errno;
           errno = 0;
           if (history(h, &ev, H_LOAD, filename) == -1)
               return errno ? errno : EINVAL;
           if (history(h, &ev, H_GETSIZE) == 0)
                   history_length = ev.num;
           if (history_length < 0)
                   return EINVAL;
           return 0;
 }  }
   
   
Line 858  write_history(const char *filename)
Line 1376  write_history(const char *filename)
   
         if (h == NULL || e == NULL)          if (h == NULL || e == NULL)
                 rl_initialize();                  rl_initialize();
         return (history(h, &ev, H_SAVE, filename));          if (filename == NULL && (filename = _default_history_file()) == NULL)
                   return errno;
           return history(h, &ev, H_SAVE, filename) == -1 ?
               (errno ? errno : EINVAL) : 0;
 }  }
   
   int
   append_history(int n, const char *filename)
   {
           HistEvent ev;
           FILE *fp;
   
           if (h == NULL || e == NULL)
                   rl_initialize();
           if (filename == NULL && (filename = _default_history_file()) == NULL)
                   return errno;
   
           if ((fp = fopen(filename, "a")) == NULL)
                   return errno;
   
           if (history(h, &ev, H_NSAVE_FP, (size_t)n,  fp) == -1) {
                   int serrno = errno ? errno : EINVAL;
                   fclose(fp);
                   return serrno;
           }
           fclose(fp);
           return 0;
   }
   
 /*  /*
  * returns history ``num''th event   * returns history ``num''th event
Line 872  history_get(int num)
Line 1415  history_get(int num)
 {  {
         static HIST_ENTRY she;          static HIST_ENTRY she;
         HistEvent ev;          HistEvent ev;
         int i = 1, curr_num;          int curr_num;
   
         if (h == NULL || e == NULL)          if (h == NULL || e == NULL)
                 rl_initialize();                  rl_initialize();
   
         /* rewind to beginning */          if (num < history_base)
                   return NULL;
   
           /* save current position */
         if (history(h, &ev, H_CURR) != 0)          if (history(h, &ev, H_CURR) != 0)
                 return (NULL);                  return NULL;
         curr_num = ev.num;          curr_num = ev.num;
         if (history(h, &ev, H_LAST) != 0)  
                 return (NULL);  /* error */  
         while (i < num && history(h, &ev, H_PREV) == 0)  
                 i++;  
         if (i != num)  
                 return (NULL);  /* not so many entries */  
   
           /*
            * use H_DELDATA to set to nth history (without delete) by passing
            * (void **)-1  -- as in history_set_pos
            */
           if (history(h, &ev, H_DELDATA, num - history_base, (void **)-1) != 0)
                   goto out;
   
           /* get current entry */
           if (history(h, &ev, H_CURR) != 0)
                   goto out;
           if (history(h, &ev, H_NEXT_EVDATA, ev.num, &she.data) != 0)
                   goto out;
         she.line = ev.str;          she.line = ev.str;
         she.data = NULL;  
   
         /* rewind history to the same event it was before */          /* restore pointer to where it was */
         (void) history(h, &ev, H_FIRST);          (void)history(h, &ev, H_SET, curr_num);
         (void) history(h, &ev, H_NEXT_EVENT, curr_num);  
           return &she;
   
         return (&she);  out:
           /* restore pointer to where it was */
           (void)history(h, &ev, H_SET, curr_num);
           return NULL;
 }  }
   
   
Line 910  add_history(const char *line)
Line 1465  add_history(const char *line)
         if (h == NULL || e == NULL)          if (h == NULL || e == NULL)
                 rl_initialize();                  rl_initialize();
   
         (void) history(h, &ev, H_ENTER, line);          if (history(h, &ev, H_ENTER, line) == -1)
                   return 0;
   
           (void)history(h, &ev, H_GETSIZE);
           if (ev.num == history_length)
                   history_base++;
           else
                   history_length = ev.num;
           return 0;
   }
   
   
   /*
    * remove the specified entry from the history list and return it.
    */
   HIST_ENTRY *
   remove_history(int num)
   {
           HIST_ENTRY *he;
           HistEvent ev;
   
           if (h == NULL || e == NULL)
                   rl_initialize();
   
           if ((he = el_malloc(sizeof(*he))) == NULL)
                   return NULL;
   
           if (history(h, &ev, H_DELDATA, num, &he->data) != 0) {
                   el_free(he);
                   return NULL;
           }
   
           he->line = ev.str;
         if (history(h, &ev, H_GETSIZE) == 0)          if (history(h, &ev, H_GETSIZE) == 0)
                 history_length = ev.num;                  history_length = ev.num;
   
         return (!(history_length > 0)); /* return 0 if all is okay */          return he;
 }  }
   
   
 /*  /*
    * replace the line and data of the num-th entry
    */
   HIST_ENTRY *
   replace_history_entry(int num, const char *line, histdata_t data)
   {
           HIST_ENTRY *he;
           HistEvent ev;
           int curr_num;
   
           if (h == NULL || e == NULL)
                   rl_initialize();
   
           /* save current position */
           if (history(h, &ev, H_CURR) != 0)
                   return NULL;
           curr_num = ev.num;
   
           /* start from the oldest */
           if (history(h, &ev, H_LAST) != 0)
                   return NULL;    /* error */
   
           if ((he = el_malloc(sizeof(*he))) == NULL)
                   return NULL;
   
           /* look forwards for event matching specified offset */
           if (history(h, &ev, H_NEXT_EVDATA, num, &he->data))
                   goto out;
   
           he->line = strdup(ev.str);
           if (he->line == NULL)
                   goto out;
   
           if (history(h, &ev, H_REPLACE, line, data))
                   goto out;
   
           /* restore pointer to where it was */
           if (history(h, &ev, H_SET, curr_num))
                   goto out;
   
           return he;
   out:
           el_free(he);
           return NULL;
   }
   
   /*
  * clear the history list - delete all entries   * clear the history list - delete all entries
  */   */
 void  void
Line 926  clear_history(void)
Line 1559  clear_history(void)
 {  {
         HistEvent ev;          HistEvent ev;
   
         history(h, &ev, H_CLEAR);          if (h == NULL || e == NULL)
                   rl_initialize();
   
           (void)history(h, &ev, H_CLEAR);
           history_offset = history_length = 0;
 }  }
   
   
Line 936  clear_history(void)
Line 1573  clear_history(void)
 int  int
 where_history(void)  where_history(void)
 {  {
           return history_offset;
   }
   
   static HIST_ENTRY **_history_listp;
   static HIST_ENTRY *_history_list;
   
   HIST_ENTRY **
   history_list(void)
   {
         HistEvent ev;          HistEvent ev;
         int curr_num, off;          HIST_ENTRY **nlp, *nl;
           int i;
   
         if (history(h, &ev, H_CURR) != 0)          if (history(h, &ev, H_LAST) != 0)
                 return (0);                  return NULL;
         curr_num = ev.num;  
   
         history(h, &ev, H_FIRST);          if ((nlp = el_realloc(_history_listp,
         off = 1;              (size_t)history_length * sizeof(*nlp))) == NULL)
         while (ev.num != curr_num && history(h, &ev, H_NEXT) == 0)                  return NULL;
                 off++;          _history_listp = nlp;
   
           if ((nl = el_realloc(_history_list,
               (size_t)history_length * sizeof(*nl))) == NULL)
                   return NULL;
           _history_list = nl;
   
         return (off);          i = 0;
           do {
                   _history_listp[i] = &_history_list[i];
                   _history_list[i].line = ev.str;
                   _history_list[i].data = NULL;
                   if (i++ == history_length)
                           abort();
           } while (history(h, &ev, H_PREV) == 0);
           return _history_listp;
 }  }
   
   
 /*  /*
  * returns current history event or NULL if there is no such event   * returns current history event or NULL if there is no such event
  */   */
 HIST_ENTRY *  HIST_ENTRY *
 current_history(void)  current_history(void)
 {  {
           HistEvent ev;
   
           if (history(h, &ev, H_PREV_EVENT, history_offset + 1) != 0)
                   return NULL;
   
         return (_move_history(H_CURR));          rl_he.line = ev.str;
           rl_he.data = NULL;
           return &rl_he;
 }  }
   
   
Line 970  int
Line 1634  int
 history_total_bytes(void)  history_total_bytes(void)
 {  {
         HistEvent ev;          HistEvent ev;
         int curr_num, size;          int curr_num;
           size_t size;
   
         if (history(h, &ev, H_CURR) != 0)          if (history(h, &ev, H_CURR) != 0)
                 return (-1);                  return -1;
         curr_num = ev.num;          curr_num = ev.num;
   
         history(h, &ev, H_FIRST);          (void)history(h, &ev, H_FIRST);
         size = 0;          size = 0;
         do          do
                 size += strlen(ev.str);                  size += strlen(ev.str) * sizeof(*ev.str);
         while (history(h, &ev, H_NEXT) == 0);          while (history(h, &ev, H_NEXT) == 0);
   
         /* get to the same position as before */          /* get to the same position as before */
         history(h, &ev, H_PREV_EVENT, curr_num);          history(h, &ev, H_PREV_EVENT, curr_num);
   
         return (size);          return (int)size;
 }  }
   
   
Line 995  history_total_bytes(void)
Line 1660  history_total_bytes(void)
 int  int
 history_set_pos(int pos)  history_set_pos(int pos)
 {  {
         HistEvent ev;          if (pos >= history_length || pos < 0)
         int off, curr_num;                  return 0;
   
         if (pos > history_length || pos < 0)  
                 return (-1);  
   
         history(h, &ev, H_CURR);          history_offset = pos;
         curr_num = ev.num;          return 1;
         history(h, &ev, H_FIRST);  
         off = 0;  
         while (off < pos && history(h, &ev, H_NEXT) == 0)  
                 off++;  
   
         if (off != pos) {       /* do a rollback in case of error */  
                 history(h, &ev, H_FIRST);  
                 history(h, &ev, H_NEXT_EVENT, curr_num);  
                 return (-1);  
         }  
         return (0);  
 }  }
   
   
 /*  /*
  * returns previous event in history and shifts pointer accordingly   * returns previous event in history and shifts pointer accordingly
    * Note that readline and editline define directions in opposite ways.
  */   */
 HIST_ENTRY *  HIST_ENTRY *
 previous_history(void)  previous_history(void)
 {  {
           HistEvent ev;
   
           if (history_offset == 0)
                   return NULL;
   
           if (history(h, &ev, H_LAST) != 0)
                   return NULL;
   
         return (_move_history(H_PREV));          history_offset--;
           return current_history();
 }  }
   
   
Line 1034  previous_history(void)
Line 1694  previous_history(void)
 HIST_ENTRY *  HIST_ENTRY *
 next_history(void)  next_history(void)
 {  {
           HistEvent ev;
   
           if (history_offset >= history_length)
                   return NULL;
   
           if (history(h, &ev, H_LAST) != 0)
                   return NULL;
   
         return (_move_history(H_NEXT));          history_offset++;
           return current_history();
 }  }
   
   
 /*  /*
  * generic history search function   * searches for first history event containing the str
  */   */
 static int  int
 _history_search_gen(const char *str, int direction, int pos)  history_search(const char *str, int direction)
 {  {
         HistEvent ev;          HistEvent ev;
         const char *strp;          const char *strp;
         int curr_num;          int curr_num;
   
         if (history(h, &ev, H_CURR) != 0)          if (history(h, &ev, H_CURR) != 0)
                 return (-1);                  return -1;
         curr_num = ev.num;          curr_num = ev.num;
   
         for (;;) {          for (;;) {
                 strp = strstr(ev.str, str);                  if ((strp = strstr(ev.str, str)) != NULL)
                 if (strp && (pos < 0 || &ev.str[pos] == strp))                          return (int)(strp - ev.str);
                         return (int) (strp - ev.str);                  if (history(h, &ev, direction < 0 ? H_NEXT:H_PREV) != 0)
                 if (history(h, &ev, direction < 0 ? H_PREV : H_NEXT) != 0)  
                         break;                          break;
         }          }
           (void)history(h, &ev, H_SET, curr_num);
         history(h, &ev, direction < 0 ? H_NEXT_EVENT : H_PREV_EVENT, curr_num);          return -1;
   
         return (-1);  
 }  
   
   
 /*  
  * searches for first history event containing the str  
  */  
 int  
 history_search(const char *str, int direction)  
 {  
   
         return (_history_search_gen(str, direction, -1));  
 }  }
   
   
Line 1084  history_search(const char *str, int dire
Line 1738  history_search(const char *str, int dire
 int  int
 history_search_prefix(const char *str, int direction)  history_search_prefix(const char *str, int direction)
 {  {
           HistEvent ev;
   
         return (_history_search_gen(str, direction, 0));          return (history(h, &ev, direction < 0 ?
               H_PREV_STR : H_NEXT_STR, str));
 }  }
   
   
Line 1095  history_search_prefix(const char *str, i
Line 1751  history_search_prefix(const char *str, i
  */   */
 /* ARGSUSED */  /* ARGSUSED */
 int  int
 history_search_pos(const char *str, int direction, int pos)  history_search_pos(const char *str,
                      int direction __attribute__((__unused__)), int pos)
 {  {
         HistEvent ev;          HistEvent ev;
         int curr_num, off;          int curr_num, off;
Line 1104  history_search_pos(const char *str, int 
Line 1761  history_search_pos(const char *str, int 
         pos = (pos > 0) ? 1 : -1;          pos = (pos > 0) ? 1 : -1;
   
         if (history(h, &ev, H_CURR) != 0)          if (history(h, &ev, H_CURR) != 0)
                 return (-1);                  return -1;
         curr_num = ev.num;          curr_num = ev.num;
   
         if (history_set_pos(off) != 0 || history(h, &ev, H_CURR) != 0)          if (!history_set_pos(off) || history(h, &ev, H_CURR) != 0)
                 return (-1);                  return -1;
   
   
         for (;;) {          for (;;) {
                 if (strstr(ev.str, str))                  if (strstr(ev.str, str))
                         return (off);                          return off;
                 if (history(h, &ev, (pos < 0) ? H_PREV : H_NEXT) != 0)                  if (history(h, &ev, (pos < 0) ? H_PREV : H_NEXT) != 0)
                         break;                          break;
         }          }
   
         /* set "current" pointer back to previous state */          /* set "current" pointer back to previous state */
         history(h, &ev, (pos < 0) ? H_NEXT_EVENT : H_PREV_EVENT, curr_num);          (void)history(h, &ev,
               pos < 0 ? H_NEXT_EVENT : H_PREV_EVENT, curr_num);
   
         return (-1);          return -1;
 }  }
   
   
 /********************************/  /********************************/
 /* completition functions       */  /* completion functions */
   
 /*  
  * does tilde expansion of strings of type ``~user/foo''  
  * if ``user'' isn't valid user name or ``txt'' doesn't start  
  * w/ '~', returns pointer to strdup()ed copy of ``txt''  
  *  
  * it's callers's responsibility to free() returned string  
  */  
 char *  char *
 tilde_expand(char *txt)  tilde_expand(char *name)
 {  {
         struct passwd *pass;          return fn_tilde_expand(name);
         char *temp;  
         size_t len = 0;  
   
         if (txt[0] != '~')  
                 return (strdup(txt));  
   
         temp = strchr(txt + 1, '/');  
         if (temp == NULL)  
                 temp = strdup(txt + 1);  
         else {  
                 len = temp - txt + 1;   /* text until string after slash */  
                 temp = malloc(len);  
                 (void) strncpy(temp, txt + 1, len - 2);  
                 temp[len - 2] = '\0';  
         }  
         pass = getpwnam(temp);  
         free(temp);             /* value no more needed */  
         if (pass == NULL)  
                 return (strdup(txt));  
   
         /* update pointer txt to point at string immedially following */  
         /* first slash */  
         txt += len;  
   
         temp = malloc(strlen(pass->pw_dir) + 1 + strlen(txt) + 1);  
         (void) sprintf(temp, "%s/%s", pass->pw_dir, txt);  
   
         return (temp);  
 }  }
   
   
 /*  
  * return first found file name starting by the ``text'' or NULL if no  
  * such file can be found  
  * value of ``state'' is ignored  
  *  
  * it's caller's responsibility to free returned string  
  */  
 char *  char *
 filename_completion_function(const char *text, int state)  filename_completion_function(const char *name, int state)
 {  {
         static DIR *dir = NULL;          return fn_filename_completion_function(name, state);
         static char *filename = NULL, *dirname = NULL;  
         static size_t filename_len = 0;  
         struct dirent *entry;  
         char *temp;  
         size_t len;  
   
         if (state == 0 || dir == NULL) {  
                 if (dir != NULL) {  
                         closedir(dir);  
                         dir = NULL;  
                 }  
                 temp = strrchr(text, '/');  
                 if (temp) {  
                         temp++;  
                         filename = realloc(filename, strlen(temp) + 1);  
                         (void) strcpy(filename, temp);  
                         len = temp - text;      /* including last slash */  
                         dirname = realloc(dirname, len + 1);  
                         (void) strncpy(dirname, text, len);  
                         dirname[len] = '\0';  
                 } else {  
                         filename = strdup(text);  
                         dirname = NULL;  
                 }  
   
                 /* support for ``~user'' syntax */  
                 if (dirname && *dirname == '~') {  
                         temp = tilde_expand(dirname);  
                         dirname = realloc(dirname, strlen(temp) + 1);  
                         (void) strcpy(dirname, temp);   /* safe */  
                         free(temp);     /* no longer needed */  
                 }  
                 /* will be used in cycle */  
                 filename_len = strlen(filename);  
                 if (filename_len == 0)  
                         return (NULL);  /* no expansion possible */  
   
                 dir = opendir(dirname ? dirname : ".");  
                 if (!dir)  
                         return (NULL);  /* cannot open the directory */  
         }  
         /* find the match */  
         while ((entry = readdir(dir)) != NULL) {  
                 /* otherwise, get first entry where first */  
                 /* filename_len characters are equal      */  
                 if (entry->d_name[0] == filename[0]  
 #if defined(__SVR4) || defined(__linux__)  
                     && strlen(entry->d_name) >= filename_len  
 #else  
                     && entry->d_namlen >= filename_len  
 #endif  
                     && strncmp(entry->d_name, filename,  
                         filename_len) == 0)  
                         break;  
         }  
   
         if (entry) {            /* match found */  
   
                 struct stat stbuf;  
 #if defined(__SVR4) || defined(__linux__)  
                 len = strlen(entry->d_name) +  
 #else  
                 len = entry->d_namlen +  
 #endif  
                     ((dirname) ? strlen(dirname) : 0) + 1 + 1;  
                 temp = malloc(len);  
                 (void) sprintf(temp, "%s%s",  
                     dirname ? dirname : "", entry->d_name);     /* safe */  
   
                 /* test, if it's directory */  
                 if (stat(temp, &stbuf) == 0 && S_ISDIR(stbuf.st_mode))  
                         strcat(temp, "/");      /* safe */  
         } else  
                 temp = NULL;  
   
         return (temp);  
 }  }
   
   
 /*  /*
  * a completion generator for usernames; returns _first_ username   * a completion generator for usernames; returns _first_ username
  * which starts with supplied text   * which starts with supplied text
  * text contains a partial username preceded by random character   * text contains a partial username preceded by random character
  * (usually '~'); state is ignored   * (usually '~'); state resets search from start (??? should we do that anyway)
  * it's callers responsibility to free returned value   * it's the caller's responsibility to free the returned value
  */   */
 char *  char *
 username_completion_function(const char *text, int state)  username_completion_function(const char *text, int state)
 {  {
         struct passwd *pwd;  #if defined(HAVE_GETPW_R_POSIX) || defined(HAVE_GETPW_R_DRAFT)
           struct passwd pwres;
           char pwbuf[1024];
   #endif
           struct passwd *pass = NULL;
   
         if (text[0] == '\0')          if (text[0] == '\0')
                 return (NULL);                  return NULL;
   
         if (*text == '~')          if (*text == '~')
                 text++;                  text++;
Line 1281  username_completion_function(const char 
Line 1822  username_completion_function(const char 
         if (state == 0)          if (state == 0)
                 setpwent();                  setpwent();
   
         while ((pwd = getpwent()) && text[0] == pwd->pw_name[0]          while (
             && strcmp(text, pwd->pw_name) == 0);  #if defined(HAVE_GETPW_R_POSIX) || defined(HAVE_GETPW_R_DRAFT)
               getpwent_r(&pwres, pwbuf, sizeof(pwbuf), &pass) == 0 && pass != NULL
   #else
               (pass = getpwent()) != NULL
   #endif
               && text[0] == pass->pw_name[0]
               && strcmp(text, pass->pw_name) == 0)
                   continue;
   
         if (pwd == NULL) {          if (pass == NULL) {
                 endpwent();                  endpwent();
                 return (NULL);                  return NULL;
         }          }
         return (strdup(pwd->pw_name));          return strdup(pass->pw_name);
 }  }
   
   
 /*  /*
  * el-compatible wrapper around rl_complete; needed for key binding   * el-compatible wrapper to send TSTP on ^Z
  */   */
 /* ARGSUSED */  /* ARGSUSED */
 static unsigned char  static unsigned char
 _el_rl_complete(EditLine *el, int ch)  _el_rl_tstp(EditLine *el __attribute__((__unused__)), int ch __attribute__((__unused__)))
 {  {
         return (unsigned char) rl_complete(0, ch);          (void)kill(0, SIGTSTP);
           return CC_NORM;
 }  }
   
   static const char *
 /*  /*ARGSUSED*/
  * returns list of completitions for text given  _rl_completion_append_character_function(const char *dummy
  */      __attribute__((__unused__)))
 char **  
 completion_matches(const char *text, CPFunction *genfunc)  
 {  {
         char **match_list = NULL, *retstr, *prevstr;          static char buf[2];
         size_t match_list_len, max_equal, which, i;          buf[0] = (char)rl_completion_append_character;
         int matches;          buf[1] = '\0';
           return buf;
         if (h == NULL || e == NULL)  
                 rl_initialize();  
   
         matches = 0;  
         match_list_len = 1;  
         while ((retstr = (*genfunc) (text, matches)) != NULL) {  
                 if (matches + 1 >= match_list_len) {  
                         match_list_len <<= 1;  
                         match_list = realloc(match_list,  
                             match_list_len * sizeof(char *));  
                 }  
                 match_list[++matches] = retstr;  
         }  
   
         if (!match_list)  
                 return (char **) NULL;  /* nothing found */  
   
         /* find least denominator and insert it to match_list[0] */  
         which = 2;  
         prevstr = match_list[1];  
         max_equal = strlen(prevstr);  
         for (; which <= matches; which++) {  
                 for (i = 0; i < max_equal &&  
                     prevstr[i] == match_list[which][i]; i++)  
                         continue;  
                 max_equal = i;  
         }  
   
         retstr = malloc(max_equal + 1);  
         (void) strncpy(retstr, match_list[1], max_equal);  
         retstr[max_equal] = '\0';  
         match_list[0] = retstr;  
   
         /* add NULL as last pointer to the array */  
         if (matches + 1 >= match_list_len)  
                 match_list = realloc(match_list,  
                     (match_list_len + 1) * sizeof(char *));  
         match_list[matches + 1] = (char *) NULL;  
   
         return (match_list);  
 }  }
   
 /*  
  * Sort function for qsort(). Just wrapper around strcasecmp().  
  */  
 static int  
 _rl_qsort_string_compare(i1, i2)  
         const void *i1, *i2;  
 {  
         const char *s1 = ((const char **)i1)[0];  
         const char *s2 = ((const char **)i2)[0];  
   
         return strcasecmp(s1, s2);  
 }  
   
 /*  /*
  * Display list of strings in columnar format on readline's output stream.   * Display list of strings in columnar format on readline's output stream.
Line 1374  _rl_qsort_string_compare(i1, i2)
Line 1869  _rl_qsort_string_compare(i1, i2)
  * 'max' is maximum length of string in 'matches'.   * 'max' is maximum length of string in 'matches'.
  */   */
 void  void
 rl_display_match_list (matches, len, max)  rl_display_match_list(char **matches, int len, int max)
      char **matches;  
      int len, max;  
 {  {
         int i, idx, limit, count;  
         int screenwidth = e->el_term.t_size.h;  
   
         /*  
          * Find out how many entries can be put on one line, count  
          * with two spaces between strings.  
          */  
         limit = screenwidth / (max + 2);  
         if (limit == 0)  
                 limit = 1;  
   
         /* how many lines of output */  
         count = len / limit;  
         if (count * limit < len)  
                 count++;  
   
         /* Sort the items if they are not already sorted. */  
         qsort(&matches[1], len-1, sizeof(char *), _rl_qsort_string_compare);  
   
         idx = 1;          fn_display_match_list(e, matches, (size_t)len, (size_t)max,
         for(; count > 0; count--) {                  _rl_completion_append_character_function);
                 for(i=0; i < limit && matches[idx]; i++, idx++)  
                         fprintf(e->el_outfile, "%-*s  ", max, matches[idx]);  
                 fprintf(e->el_outfile, "\n");  
         }  
 }  }
   
 /*  /*
  * Complete the word at or before point, called by rl_complete()   * complete word at current point
  * 'what_to_do' says what to do with the completion.  
  * `?' means list the possible completions.  
  * TAB means do standard completion.  
  * `*' means insert all of the possible completions.  
  * `!' means to do standard completion, and list all possible completions if  
  * there is more than one.  
  *  
  * Note: '*' support is not implemented  
  */   */
 static int  /* ARGSUSED */
 rl_complete_internal(int what_to_do)  int
   rl_complete(int ignore __attribute__((__unused__)), int invoking_key)
 {  {
         CPFunction *complet_func;          static ct_buffer_t wbreak_conv, sprefix_conv;
         const LineInfo *li;          char *breakchars;
         char *temp, **matches;  
         const char *ctemp;  
         size_t len;  
   
         rl_completion_type = what_to_do;  
   
         if (h == NULL || e == NULL)          if (h == NULL || e == NULL)
                 rl_initialize();                  rl_initialize();
   
         complet_func = rl_completion_entry_function;          if (rl_inhibit_completion) {
         if (!complet_func)                  char arr[2];
                 complet_func = filename_completion_function;                  arr[0] = (char)invoking_key;
                   arr[1] = '\0';
         /* We now look backwards for the start of a filename/variable word */                  el_insertstr(e, arr);
         li = el_line(e);                  return CC_REFRESH;
         ctemp = (char *) li->cursor;  
         while (ctemp > li->buffer  
             && !strchr(rl_basic_word_break_characters, ctemp[-1])  
             && (!rl_special_prefixes  
                         || !strchr(rl_special_prefixes, ctemp[-1]) ) )  
                 ctemp--;  
   
         len = li->cursor - ctemp;  
         temp = alloca(len + 1);  
         (void) strncpy(temp, ctemp, len);  
         temp[len] = '\0';  
   
         /* these can be used by function called in completion_matches() */  
         /* or (*rl_attempted_completion_function)() */  
         rl_point = li->cursor - li->buffer;  
         rl_end = li->lastchar - li->buffer;  
   
         if (!rl_attempted_completion_function)  
                 matches = completion_matches(temp, complet_func);  
         else {  
                 int end = li->cursor - li->buffer;  
                 matches = (*rl_attempted_completion_function) (temp, (int)  
                     (end - len), end);  
         }          }
   
         if (matches) {          if (rl_completion_word_break_hook != NULL)
                 int i, retval = CC_REFRESH;                  breakchars = (*rl_completion_word_break_hook)();
                 int matches_num, maxlen, match_len, match_display=1;          else
                   breakchars = rl_basic_word_break_characters;
   
                 /*          _rl_update_pos();
                  * Only replace the completed string with common part of  
                  * possible matches if there is possible completion.  
                  */  
                 if (matches[0][0] != '\0') {  
                         el_deletestr(e, (int) len);  
                         el_insertstr(e, matches[0]);  
                 }  
   
                 if (what_to_do == '?')  
                         goto display_matches;  
   
                 if (matches[2] == NULL && strcmp(matches[0], matches[1]) == 0) {  
                         /*  
                          * We found exact match. Add a space after  
                          * it, unless we do filename completition and the  
                          * object is a directory.  
                          */  
                         size_t alen = strlen(matches[0]);  
                         if ((complet_func != filename_completion_function  
                               || (alen > 0 && (matches[0])[alen - 1] != '/'))  
                             && rl_completion_append_character) {  
                                 char buf[2];  
                                 buf[0] = rl_completion_append_character;  
                                 buf[1] = '\0';  
                                 el_insertstr(e, buf);  
                         }  
                 } else if (what_to_do == '!') {  
     display_matches:  
                         /*  
                          * More than one match and requested to list possible  
                          * matches.  
                          */  
   
                         for(i=1, maxlen=0; matches[i]; i++) {  
                                 match_len = strlen(matches[i]);  
                                 if (match_len > maxlen)  
                                         maxlen = match_len;  
                         }  
                         matches_num = i - 1;  
   
                         /* newline to get on next line from command line */  
                         fprintf(e->el_outfile, "\n");  
   
                         /*  
                          * If there are too many items, ask user for display  
                          * confirmation.  
                          */  
                         if (matches_num > rl_completion_query_items) {  
                                 fprintf(e->el_outfile,  
                                 "Display all %d possibilities? (y or n) ",  
                                         matches_num);  
                                 fflush(e->el_outfile);  
                                 if (getc(stdin) != 'y')  
                                         match_display = 0;  
                                 fprintf(e->el_outfile, "\n");  
                         }  
   
                         if (match_display)          /* Just look at how many global variables modify this operation! */
                                 rl_display_match_list(matches, matches_num,          return fn_complete(e,
                                         maxlen);              (rl_compentry_func_t *)rl_completion_entry_function,
                         retval = CC_REDISPLAY;              rl_attempted_completion_function,
                 } else {              ct_decode_string(rl_basic_word_break_characters, &wbreak_conv),
                         /* lcd is not a valid object - further specification */              ct_decode_string(breakchars, &sprefix_conv),
                         /* is needed */              _rl_completion_append_character_function,
                         el_beep(e);              (size_t)rl_completion_query_items,
                         retval = CC_NORM;              &rl_completion_type, &rl_attempted_completion_over,
                 }              &rl_point, &rl_end);
   
                 /* free elements of array and the array itself */  
                 for (i = 0; matches[i]; i++)  
                         free(matches[i]);  
                 free(matches), matches = NULL;  
   
                 return (retval);  
         }  
         return (CC_NORM);  
 }  }
   
   
 /*  /* ARGSUSED */
  * complete word at current point  static unsigned char
  */  _el_rl_complete(EditLine *el __attribute__((__unused__)), int ch)
 int  
 rl_complete(int ignore, int invoking_key)  
 {  {
         if (h == NULL || e == NULL)          return (unsigned char)rl_complete(0, ch);
                 rl_initialize();  
   
         if (rl_inhibit_completion) {  
                 rl_insert(ignore, invoking_key);  
                 return (CC_REFRESH);  
         } else if (e->el_state.lastcmd == el_rl_complete_cmdnum)  
                 return rl_complete_internal('?');  
         else if (_rl_complete_show_all)  
                 return rl_complete_internal('!');  
         else  
                 return (rl_complete_internal(TAB));  
 }  }
   
   
 /*  /*
  * misc other functions   * misc other functions
  */   */
Line 1574  rl_complete(int ignore, int invoking_key
Line 1934  rl_complete(int ignore, int invoking_key
  * bind key c to readline-type function func   * bind key c to readline-type function func
  */   */
 int  int
 rl_bind_key(int c, int func(int, int))  rl_bind_key(int c, rl_command_func_t *func)
 {  {
         int retval = -1;          int retval = -1;
   
Line 1586  rl_bind_key(int c, int func(int, int))
Line 1946  rl_bind_key(int c, int func(int, int))
                 e->el_map.key[c] = ED_INSERT;                  e->el_map.key[c] = ED_INSERT;
                 retval = 0;                  retval = 0;
         }          }
         return (retval);          return retval;
 }  }
   
   
Line 1602  rl_read_key(void)
Line 1962  rl_read_key(void)
         if (e == NULL || h == NULL)          if (e == NULL || h == NULL)
                 rl_initialize();                  rl_initialize();
   
         return (el_getc(e, fooarr));          return el_getc(e, fooarr);
 }  }
   
   
Line 1611  rl_read_key(void)
Line 1971  rl_read_key(void)
  */   */
 /* ARGSUSED */  /* ARGSUSED */
 void  void
 rl_reset_terminal(const char *p)  rl_reset_terminal(const char *p __attribute__((__unused__)))
 {  {
   
         if (h == NULL || e == NULL)          if (h == NULL || e == NULL)
Line 1632  rl_insert(int count, int c)
Line 1992  rl_insert(int count, int c)
                 rl_initialize();                  rl_initialize();
   
         /* XXX - int -> char conversion can lose on multichars */          /* XXX - int -> char conversion can lose on multichars */
         arr[0] = c;          arr[0] = (char)c;
         arr[1] = '\0';          arr[1] = '\0';
   
         for (; count > 0; count--)          for (; count > 0; count--)
                 el_push(e, arr);                  el_push(e, arr);
   
         return (0);          return 0;
   }
   
   int
   rl_insert_text(const char *text)
   {
           if (!text || *text == 0)
                   return 0;
   
           if (h == NULL || e == NULL)
                   rl_initialize();
   
           if (el_insertstr(e, text) < 0)
                   return 0;
           return (int)strlen(text);
   }
   
   /*ARGSUSED*/
   int
   rl_newline(int count __attribute__((__unused__)),
       int c __attribute__((__unused__)))
   {
           /*
            * Readline-4.0 appears to ignore the args.
            */
           return rl_insert(1, '\n');
   }
   
   /*ARGSUSED*/
   static unsigned char
   rl_bind_wrapper(EditLine *el __attribute__((__unused__)), unsigned char c)
   {
           if (map[c] == NULL)
               return CC_ERROR;
   
           _rl_update_pos();
   
           (*map[c])(1, c);
   
           /* If rl_done was set by the above call, deal with it here */
           if (rl_done)
                   return CC_EOF;
   
           return CC_NORM;
   }
   
   int
   rl_add_defun(const char *name, rl_command_func_t *fun, int c)
   {
           char dest[8];
           if ((size_t)c >= sizeof(map) / sizeof(map[0]) || c < 0)
                   return -1;
           map[(unsigned char)c] = fun;
           el_set(e, EL_ADDFN, name, name, rl_bind_wrapper);
           vis(dest, c, VIS_WHITE|VIS_NOSLASH, 0);
           el_set(e, EL_BIND, dest, name, NULL);
           return 0;
   }
   
   void
   rl_callback_read_char(void)
   {
           int count = 0, done = 0;
           const char *buf = el_gets(e, &count);
           char *wbuf;
   
           if (buf == NULL || count-- <= 0)
                   return;
           if (count == 0 && buf[0] == e->el_tty.t_c[TS_IO][C_EOF])
                   done = 1;
           if (buf[count] == '\n' || buf[count] == '\r')
                   done = 2;
   
           if (done && rl_linefunc != NULL) {
                   el_set(e, EL_UNBUFFERED, 0);
                   if (done == 2) {
                           if ((wbuf = strdup(buf)) != NULL)
                                   wbuf[count] = '\0';
                   } else
                           wbuf = NULL;
                   (*(void (*)(const char *))rl_linefunc)(wbuf);
                   el_set(e, EL_UNBUFFERED, 1);
           }
   }
   
   void
   rl_callback_handler_install(const char *prompt, rl_vcpfunc_t *linefunc)
   {
           if (e == NULL) {
                   rl_initialize();
           }
           (void)rl_set_prompt(prompt);
           rl_linefunc = linefunc;
           el_set(e, EL_UNBUFFERED, 1);
   }
   
   void
   rl_callback_handler_remove(void)
   {
           rl_linefunc = NULL;
           el_end(e);
           e = NULL;
   }
   
   void
   rl_redisplay(void)
   {
           char a[2];
           a[0] = (char)e->el_tty.t_c[TS_IO][C_REPRINT];
           a[1] = '\0';
           el_push(e, a);
   }
   
   int
   rl_get_previous_history(int count, int key)
   {
           char a[2];
           a[0] = (char)key;
           a[1] = '\0';
           while (count--)
                   el_push(e, a);
           return 0;
   }
   
   void
   /*ARGSUSED*/
   rl_prep_terminal(int meta_flag __attribute__((__unused__)))
   {
           el_set(e, EL_PREP_TERM, 1);
   }
   
   void
   rl_deprep_terminal(void)
   {
           el_set(e, EL_PREP_TERM, 0);
   }
   
   int
   rl_read_init_file(const char *s)
   {
           return el_source(e, s);
   }
   
   int
   rl_parse_and_bind(const char *line)
   {
           const char **argv;
           int argc;
           Tokenizer *tok;
   
           tok = tok_init(NULL);
           tok_str(tok, line, &argc, &argv);
           argc = el_parse(e, argc, argv);
           tok_end(tok);
           return argc ? 1 : 0;
   }
   
   int
   rl_variable_bind(const char *var, const char *value)
   {
           /*
            * The proper return value is undocument, but this is what the
            * readline source seems to do.
            */
           return el_set(e, EL_BIND, "", var, value, NULL) == -1 ? 1 : 0;
   }
   
   void
   rl_stuff_char(int c)
   {
           char buf[2];
   
           buf[0] = (char)c;
           buf[1] = '\0';
           el_insertstr(e, buf);
   }
   
   static int
   _rl_event_read_char(EditLine *el, wchar_t *wc)
   {
           char    ch;
           int     n;
           ssize_t num_read = 0;
   
           ch = '\0';
           *wc = L'\0';
           while (rl_event_hook) {
   
                   (*rl_event_hook)();
   
   #if defined(FIONREAD)
                   if (ioctl(el->el_infd, FIONREAD, &n) < 0)
                           return -1;
                   if (n)
                           num_read = read(el->el_infd, &ch, (size_t)1);
                   else
                           num_read = 0;
   #elif defined(F_SETFL) && defined(O_NDELAY)
                   if ((n = fcntl(el->el_infd, F_GETFL, 0)) < 0)
                           return -1;
                   if (fcntl(el->el_infd, F_SETFL, n|O_NDELAY) < 0)
                           return -1;
                   num_read = read(el->el_infd, &ch, 1);
                   if (fcntl(el->el_infd, F_SETFL, n))
                           return -1;
   #else
                   /* not non-blocking, but what you gonna do? */
                   num_read = read(el->el_infd, &ch, 1);
                   return -1;
   #endif
   
                   if (num_read < 0 && errno == EAGAIN)
                           continue;
                   if (num_read == 0)
                           continue;
                   break;
           }
           if (!rl_event_hook)
                   el_set(el, EL_GETCFN, EL_BUILTIN_GETCFN);
           *wc = (wchar_t)ch;
           return (int)num_read;
   }
   
   static void
   _rl_update_pos(void)
   {
           const LineInfo *li = el_line(e);
   
           rl_point = (int)(li->cursor - li->buffer);
           rl_end = (int)(li->lastchar - li->buffer);
   }
   
   void
   rl_get_screen_size(int *rows, int *cols)
   {
           if (rows)
                   el_get(e, EL_GETTC, "li", rows, (void *)0);
           if (cols)
                   el_get(e, EL_GETTC, "co", cols, (void *)0);
   }
   
   void
   rl_set_screen_size(int rows, int cols)
   {
           char buf[64];
           (void)snprintf(buf, sizeof(buf), "%d", rows);
           el_set(e, EL_SETTC, "li", buf, NULL);
           (void)snprintf(buf, sizeof(buf), "%d", cols);
           el_set(e, EL_SETTC, "co", buf, NULL);
   }
   
   char **
   rl_completion_matches(const char *str, rl_compentry_func_t *fun)
   {
           size_t len, max, i, j, min;
           char **list, *match, *a, *b;
   
           len = 1;
           max = 10;
           if ((list = el_malloc(max * sizeof(*list))) == NULL)
                   return NULL;
   
           while ((match = (*fun)(str, (int)(len - 1))) != NULL) {
                   list[len++] = match;
                   if (len == max) {
                           char **nl;
                           max += 10;
                           if ((nl = el_realloc(list, max * sizeof(*nl))) == NULL)
                                   goto out;
                           list = nl;
                   }
           }
           if (len == 1)
                   goto out;
           list[len] = NULL;
           if (len == 2) {
                   if ((list[0] = strdup(list[1])) == NULL)
                           goto out;
                   return list;
           }
           qsort(&list[1], len - 1, sizeof(*list),
               (int (*)(const void *, const void *)) strcmp);
           min = SIZE_MAX;
           for (i = 1, a = list[i]; i < len - 1; i++, a = b) {
                   b = list[i + 1];
                   for (j = 0; a[j] && a[j] == b[j]; j++)
                           continue;
                   if (min > j)
                           min = j;
           }
           if (min == 0 && *str) {
                   if ((list[0] = strdup(str)) == NULL)
                           goto out;
           } else {
                   if ((list[0] = el_malloc((min + 1) * sizeof(*list[0]))) == NULL)
                           goto out;
                   (void)memcpy(list[0], list[1], min);
                   list[0][min] = '\0';
           }
           return list;
   
   out:
           el_free(list);
           return NULL;
   }
   
   char *
   rl_filename_completion_function (const char *text, int state)
   {
           return fn_filename_completion_function(text, state);
   }
   
   void
   rl_forced_update_display(void)
   {
           el_set(e, EL_REFRESH);
   }
   
   int
   _rl_abort_internal(void)
   {
           el_beep(e);
           longjmp(topbuf, 1);
           /*NOTREACHED*/
   }
   
   int
   _rl_qsort_string_compare(char **s1, char **s2)
   {
           return strcoll(*s1, *s2);
   }
   
   HISTORY_STATE *
   history_get_history_state(void)
   {
           HISTORY_STATE *hs;
   
           if ((hs = el_malloc(sizeof(*hs))) == NULL)
                   return NULL;
           hs->length = history_length;
           return hs;
   }
   
   int
   /*ARGSUSED*/
   rl_kill_text(int from __attribute__((__unused__)),
       int to __attribute__((__unused__)))
   {
           return 0;
   }
   
   Keymap
   rl_make_bare_keymap(void)
   {
           return NULL;
   }
   
   Keymap
   rl_get_keymap(void)
   {
           return NULL;
   }
   
   void
   /*ARGSUSED*/
   rl_set_keymap(Keymap k __attribute__((__unused__)))
   {
   }
   
   int
   /*ARGSUSED*/
   rl_generic_bind(int type __attribute__((__unused__)),
       const char * keyseq __attribute__((__unused__)),
       const char * data __attribute__((__unused__)),
       Keymap k __attribute__((__unused__)))
   {
           return 0;
   }
   
   int
   /*ARGSUSED*/
   rl_bind_key_in_map(int key __attribute__((__unused__)),
       rl_command_func_t *fun __attribute__((__unused__)),
       Keymap k __attribute__((__unused__)))
   {
           return 0;
   }
   
   /* unsupported, but needed by python */
   void
   rl_cleanup_after_signal(void)
   {
   }
   
   int
   rl_on_new_line(void)
   {
           return 0;
   }
   
   void
   rl_free_line_state(void)
   {
   }
   
   int
   /*ARGSUSED*/
   rl_set_keyboard_input_timeout(int u __attribute__((__unused__)))
   {
           return 0;
   }
   
   void
   rl_resize_terminal(void)
   {
           el_resize(e);
 }  }

Legend:
Removed from v.1.15  
changed lines
  Added in v.1.146.2.1

CVSweb <webmaster@jp.NetBSD.org>