]> git.ipfire.org Git - thirdparty/bash.git/commitdiff
commit bash-20120316 snapshot
authorChet Ramey <chet.ramey@case.edu>
Mon, 9 Apr 2012 14:00:28 +0000 (10:00 -0400)
committerChet Ramey <chet.ramey@case.edu>
Mon, 9 Apr 2012 14:00:28 +0000 (10:00 -0400)
14 files changed:
CWRU/CWRU.chlog
CWRU/CWRU.chlog~
array.c
doc/bashref.texi
lib/readline/doc/history.texi
lib/readline/doc/rlman.texi
lib/readline/doc/rltech.texi
lib/readline/doc/rluserman.texi
lib/sh/mbschr.c
lib/sh/mbschr.c~ [new file with mode: 0644]
patchlevel.h
pcomplete.c
pcomplete.c~ [new file with mode: 0644]
tests/RUN-ONE-TEST

index 45bd120f28d1cf124250706cd6297c0ab768918d..78c67914f0eaf50c123f30e859e7563c7d68dde1 100644 (file)
@@ -13581,3 +13581,35 @@ config-top.h
 {jobs,nojobs}.c
        - check_window_size: default initial value to CHECKWINSIZE_DEFAULT
 
+                                  3/13
+                                  ----
+doc/bashref.texi
+       - change text referring to the copying restrictions to that
+         recommended by the FSF (no Front-Cover Texts and no Back-Cover
+         Texts)
+
+lib/readline/doc/{history,rlman,rluserman}.texi
+       - change text referring to the copying restrictions to that
+         recommended by the FSF (no Front-Cover Texts and no Back-Cover
+         Texts)
+
+                                  3/15
+                                  ----
+array.c
+       - LASTREF_START: new macro to set the starting position for an array
+         traversal to `lastref' if that's valid, and to the start of the array
+         if not.  Used in array_reference, array_insert, array_remove
+       - array_remove: try to be a little smarter with lastref instead of
+         unconditionally invalidating it
+
+                                  3/16
+                                  ----
+array.c
+       - array_insert: fix memory leak by deleting element to be added in the
+         case of an error
+
+                                  3/18
+                                  ----
+lib/sh/mbschr.c
+       - mbschr: don't call mbrlen unless is_basic is false; devolves to a
+         straight character-by-character run through the string
index 3aa3f622a3dbc65719aba2e2a1837c0152c05928..e5c9e604d7df36e97bac3261692aa3a48b10ca00 100644 (file)
@@ -13578,3 +13578,32 @@ config-top.h
          check_window_size (shopt checkwinsize): 0 for off, 1 for on.
          Default is 0
 
+{jobs,nojobs}.c
+       - check_window_size: default initial value to CHECKWINSIZE_DEFAULT
+
+                                  3/13
+                                  ----
+doc/bashref.texi
+       - change text referring to the copying restrictions to that
+         recommended by the FSF (no Front-Cover Texts and no Back-Cover
+         Texts)
+
+lib/readline/doc/{history,rlman,rluserman}.texi
+       - change text referring to the copying restrictions to that
+         recommended by the FSF (no Front-Cover Texts and no Back-Cover
+         Texts)
+
+                                  3/15
+                                  ----
+array.c
+       - LASTREF_START: new macro to set the starting position for an array
+         traversal to `lastref' if that's valid, and to the start of the array
+         if not.  Used in array_reference, array_insert, array_remove
+       - array_remove: try to be a little smarter with lastref instead of
+         unconditionally invalidating it
+
+                                  3/16
+                                  ----
+array.c
+       - array_insert: fix memory leak by deleting element to be added in the
+         case of an error
diff --git a/array.c b/array.c
index a6cca015a3f5b68ac1827819fe886ad694f8df8f..b601d58648b5c09c8a2e29786c6f383631581a48 100644 (file)
--- a/array.c
+++ b/array.c
@@ -58,7 +58,11 @@ static char *array_to_string_internal __P((ARRAY_ELEMENT *, ARRAY_ELEMENT *, cha
 static ARRAY *lastarray = 0;
 static ARRAY_ELEMENT *lastref = 0;
 
-#define IS_LASTREF(a)  ((a) == lastarray)
+#define IS_LASTREF(a)  (lastarray && (a) == lastarray)
+
+#define LASTREF_START(a, i) \
+       (IS_LASTREF(a) && i >= element_index(lastref)) ? lastref \
+                                                      : element_forw(a->head)
 
 #define INVALIDATE_LASTREF(a) \
 do { \
@@ -610,7 +614,7 @@ ARRAY       *a;
 arrayind_t     i;
 char   *v;
 {
-       register ARRAY_ELEMENT *new, *ae;
+       register ARRAY_ELEMENT *new, *ae, *start;
 
        if (a == 0)
                return(-1);
@@ -628,9 +632,12 @@ char       *v;
                return(0);
        }
        /*
-        * Otherwise we search for the spot to insert it.
+        * Otherwise we search for the spot to insert it.  The lastref
+        * handle optimizes the case of sequential or almost-sequential
+        * assignments that are not at the end of the array.
         */
-       for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae)) {
+       start = LASTREF_START(a, i);
+       for (ae = start; ae != a->head; ae = element_forw(ae)) {
                if (element_index(ae) == i) {
                        /*
                         * Replacing an existing element.
@@ -647,6 +654,7 @@ char        *v;
                        return(0);
                }
        }
+       array_dispose_element(new);
        INVALIDATE_LASTREF(a);
        return (-1);            /* problem */
 }
@@ -660,18 +668,28 @@ array_remove(a, i)
 ARRAY  *a;
 arrayind_t     i;
 {
-       register ARRAY_ELEMENT *ae;
+       register ARRAY_ELEMENT *ae, *start;
 
        if (a == 0 || array_empty(a))
                return((ARRAY_ELEMENT *) NULL);
-       for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae))
+       start = LASTREF_START(a, i);
+       for (ae = start; ae != a->head; ae = element_forw(ae))
                if (element_index(ae) == i) {
                        ae->next->prev = ae->prev;
                        ae->prev->next = ae->next;
                        a->num_elements--;
                        if (i == array_max_index(a))
                                a->max_index = element_index(ae->prev);
+#if 0
                        INVALIDATE_LASTREF(a);
+#else
+                       if (ae->next != a->head)
+                               SET_LASTREF(a, ae->next);
+                       else if (ae->prev != a->head)
+                               SET_LASTREF(a, ae->prev);
+                       else
+                               INVALIDATE_LASTREF(a);
+#endif
                        return(ae);
                }
        return((ARRAY_ELEMENT *) NULL);
@@ -685,18 +703,14 @@ array_reference(a, i)
 ARRAY  *a;
 arrayind_t     i;
 {
-       register ARRAY_ELEMENT *ae;
+       register ARRAY_ELEMENT *ae, *start;
 
        if (a == 0 || array_empty(a))
                return((char *) NULL);
        if (i > array_max_index(a))
-               return((char *)NULL);
-       /* Keep roving pointer into array to optimize sequential access */
-       if (lastref && IS_LASTREF(a))
-               ae = (i >= element_index(lastref)) ? lastref : element_forw(a->head);
-       else
-               ae = element_forw(a->head);
-       for ( ; ae != a->head; ae = element_forw(ae))
+               return((char *)NULL);   /* Keep roving pointer into array to optimize sequential access */
+       start = LASTREF_START(a, i);
+       for (ae = start; ae != a->head; ae = element_forw(ae))
                if (element_index(ae) == i) {
                        SET_LASTREF(a, ae);
                        return(element_value(ae));
@@ -816,7 +830,7 @@ int quoted;
                                                rsize, rsize);
                        strcpy(result + rlen, t);
                        rlen += reg;
-                       if (quoted)
+                       if (quoted && t)
                                free(t);
                        /*
                         * Add a separator only after non-null elements.
index 9eeb58c9328b820a2568411e985bf0f964efddf2..2c9fd21fabc6a2713ab87ad5d716928bd0a8f4ec 100644 (file)
@@ -26,13 +26,9 @@ are preserved on all copies.
 Permission is granted to copy, distribute and/or modify this document
 under the terms of the GNU Free Documentation License, Version 1.3 or
 any later version published by the Free Software Foundation; with no
-Invariant Sections, with the Front-Cover texts being ``A GNU Manual'',
-and with the Back-Cover Texts as in (a) below.  A copy of the license is
-included in the section entitled ``GNU Free Documentation License''.
-
-(a) The FSF's Back-Cover Text is: You are free to copy and modify
-this GNU manual.  Buying copies from GNU Press supports the FSF in
-developing GNU and promoting software freedom.''
+Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
+A copy of the license is included in the section entitled
+``GNU Free Documentation License''.
 
 @end quotation
 @end copying
index 64945d86e4d3ac38f666a9935691f9a69643a9ad..4d4d94da1e158166a824165f42cd1b3b0fdef696 100644 (file)
@@ -12,7 +12,7 @@ This document describes the GNU History library
 a programming tool that provides a consistent user interface for
 recalling lines of previously typed input.
 
-Copyright @copyright{} 1988--2011 Free Software Foundation, Inc.
+Copyright @copyright{} 1988--2012 Free Software Foundation, Inc.
 
 Permission is granted to make and distribute verbatim copies of
 this manual provided the copyright notice and this permission notice
@@ -22,13 +22,9 @@ are preserved on all copies.
 Permission is granted to copy, distribute and/or modify this document
 under the terms of the GNU Free Documentation License, Version 1.3 or
 any later version published by the Free Software Foundation; with no
-Invariant Sections, with the Front-Cover texts being ``A GNU Manual'',
-and with the Back-Cover Texts as in (a) below.  A copy of the license is
-included in the section entitled ``GNU Free Documentation License''.
-
-(a) The FSF's Back-Cover Text is: You are free to copy and modify
-this GNU manual.  Buying copies from GNU Press supports the FSF in
-developing GNU and promoting software freedom.''
+Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
+A copy of the license is included in the section entitled
+``GNU Free Documentation License''.
 
 @end quotation
 @end copying
index 1c9ac13338bde459bf1fb2567604d3462b622bda..84fbf23b68c3a0b43cbd787fc11ecc66797aeee3 100644 (file)
@@ -13,7 +13,7 @@ This manual describes the GNU Readline Library
 consistency of user interface across discrete programs which provide
 a command line interface.
 
-Copyright @copyright{} 1988--2011 Free Software Foundation, Inc.
+Copyright @copyright{} 1988--2012 Free Software Foundation, Inc.
 
 Permission is granted to make and distribute verbatim copies of
 this manual provided the copyright notice and this permission notice
@@ -23,13 +23,9 @@ are preserved on all copies.
 Permission is granted to copy, distribute and/or modify this document
 under the terms of the GNU Free Documentation License, Version 1.3 or
 any later version published by the Free Software Foundation; with no
-Invariant Sections, with the Front-Cover texts being ``A GNU Manual'',
-and with the Back-Cover Texts as in (a) below.  A copy of the license is
-included in the section entitled ``GNU Free Documentation License''.
-
-(a) The FSF's Back-Cover Text is: You are free to copy and modify
-this GNU manual.  Buying copies from GNU Press supports the FSF in
-developing GNU and promoting software freedom.''
+Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
+A copy of the license is included in the section entitled
+``GNU Free Documentation License''.
 
 @end quotation
 @end copying
index 5e175d172c5b45791bc6bf67a73316563c2fb580..71c7c3629da80960717fc651489bab643e028ad9 100644 (file)
@@ -29,7 +29,7 @@ Permission is granted to copy and distribute translations of this manual
 into another language, under the above conditions for modified versions,
 except that this permission notice may be stated in a translation approved
 by the Foundation.
-end ifinfo
+@end ifinfo
 
 @node Programming with GNU Readline
 @chapter Programming with GNU Readline
index 3d5452045356583b68b65e7720131fb15edcb1d9..5c55357530eee0578310d4aa13cc9c06c7ddd8de 100644 (file)
@@ -12,7 +12,7 @@ This manual describes the end user interface of the GNU Readline Library
 consistency of user interface across discrete programs which provide
 a command line interface.
 
-Copyright @copyright{} 1988--2011 Free Software Foundation, Inc.
+Copyright @copyright{} 1988--2012 Free Software Foundation, Inc.
 
 Permission is granted to make and distribute verbatim copies of
 this manual provided the copyright notice and this permission notice
@@ -22,13 +22,9 @@ are preserved on all copies.
 Permission is granted to copy, distribute and/or modify this document
 under the terms of the GNU Free Documentation License, Version 1.3 or
 any later version published by the Free Software Foundation; with no
-Invariant Sections, with the Front-Cover texts being ``A GNU Manual'',
-and with the Back-Cover Texts as in (a) below.  A copy of the license is
-included in the section entitled ``GNU Free Documentation License''.
-
-(a) The FSF's Back-Cover Text is: You are free to copy and modify
-this GNU manual.  Buying copies from GNU Press supports the FSF in
-developing GNU and promoting software freedom.''
+Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
+A copy of the license is included in the section entitled
+``GNU Free Documentation License''.
 
 @end quotation
 @end copying
index 5143d64e3ab57ff520c10b592b4cb2c23146d891..7730e46eea2a441e04fbb60dc385630f7fa26a68 100644 (file)
@@ -27,6 +27,8 @@
 #include "bashansi.h"
 #include "shmbutil.h"
 
+extern int locale_mb_cur_max;
+
 #undef mbschr
 
 /* In some locales, the non-first byte of some multibyte characters have
@@ -51,7 +53,7 @@ mbschr (s, c)
      GBK, GB18030, SHIFT_JIS, and JOHAB.  They exhibit the problem only
      when c >= 0x30.  We can therefore use the faster bytewise search if
      c <= 0x30. */
-  if ((unsigned char)c >= '0' && MB_CUR_MAX > 1)
+  if ((unsigned char)c >= '0' && locale_mb_cur_max > 1)
     {
       pos = (char *)s;
       memset (&state, '\0', sizeof(mbstate_t));
@@ -59,9 +61,14 @@ mbschr (s, c)
 
       while (strlength > 0)
        {
-         mblength = mbrlen (pos, strlength, &state);
-         if (mblength == (size_t)-2 || mblength == (size_t)-1 || mblength == (size_t)0)
+         if (is_basic (*pos))
            mblength = 1;
+         else
+           {
+             mblength = mbrlen (pos, strlength, &state);
+             if (mblength == (size_t)-2 || mblength == (size_t)-1 || mblength == (size_t)0)
+               mblength = 1;
+           }
 
          if (mblength == 1 && c == (unsigned char)*pos)
            return pos;
diff --git a/lib/sh/mbschr.c~ b/lib/sh/mbschr.c~
new file mode 100644 (file)
index 0000000..5143d64
--- /dev/null
@@ -0,0 +1,78 @@
+/* mbschr.c - strchr(3) that handles multibyte characters. */
+
+/* Copyright (C) 2002 Free Software Foundation, Inc.
+
+   This file is part of GNU Bash, the Bourne Again SHell.
+
+   Bash is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   Bash is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with Bash.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#ifdef HAVE_STDLIB_H
+#  include <stdlib.h>
+#endif
+
+#include "bashansi.h"
+#include "shmbutil.h"
+
+#undef mbschr
+
+/* In some locales, the non-first byte of some multibyte characters have
+   the same value as some ascii character.  Faced with these strings, a
+   legacy strchr() might return the wrong value. */
+
+char *
+#if defined (PROTOTYPES)
+mbschr (const char *s, int c)
+#else
+mbschr (s, c)
+     const char *s;
+     int c;
+#endif
+{
+#if HANDLE_MULTIBYTE
+  char *pos;
+  mbstate_t state;
+  size_t strlength, mblength;
+
+  /* The locale encodings with said weird property are BIG5, BIG5-HKSCS,
+     GBK, GB18030, SHIFT_JIS, and JOHAB.  They exhibit the problem only
+     when c >= 0x30.  We can therefore use the faster bytewise search if
+     c <= 0x30. */
+  if ((unsigned char)c >= '0' && MB_CUR_MAX > 1)
+    {
+      pos = (char *)s;
+      memset (&state, '\0', sizeof(mbstate_t));
+      strlength = strlen (s);
+
+      while (strlength > 0)
+       {
+         mblength = mbrlen (pos, strlength, &state);
+         if (mblength == (size_t)-2 || mblength == (size_t)-1 || mblength == (size_t)0)
+           mblength = 1;
+
+         if (mblength == 1 && c == (unsigned char)*pos)
+           return pos;
+
+         strlength -= mblength;
+         pos += mblength;
+       }
+
+      return ((char *)NULL);
+    }
+  else
+#endif
+  return (strchr (s, c));
+}
index d85641dcf52ab31bd828f180f5b3b77cde0fb5d8..a241d2f19a3eb8c835bb6bf480e94d024a2a0487 100644 (file)
@@ -25,6 +25,6 @@
    regexp `^#define[   ]*PATCHLEVEL', since that's what support/mkversion.sh
    looks for to find the patch level (for the sccs version string). */
 
-#define PATCHLEVEL 20
+#define PATCHLEVEL 24
 
 #endif /* _PATCHLEVEL_H_ */
index ffd1da925b9dbcb4783dec54ef89335dc0141dce..fd4be1cb055619fe88772ab36a1689e5fcf0b885 100644 (file)
@@ -37,6 +37,8 @@
 #  include <varargs.h>
 #endif
 
+#include <sys/time.h>
+
 #include <stdio.h>
 #include "bashansi.h"
 #include "bashintl.h"
@@ -880,7 +882,10 @@ gen_wordlist_matches (cs, text)
   /* This used to be a simple expand_string(cs->words, 0), but that won't
      do -- there's no way to split a simple list into individual words
      that way, since the shell semantics say that word splitting is done
-     only on the results of expansion. */
+     only on the results of expansion.  split_at_delims also handles embedded
+     quoted strings and preserves the quotes for the expand_words_shellexp
+     function call that follows. */
+  /* XXX - this is where this function spends most of its time */
   l = split_at_delims (cs->words, strlen (cs->words), (char *)NULL, -1, 0, (int *)NULL, (int *)NULL);
   if (l == 0)
     return ((STRINGLIST *)NULL);
diff --git a/pcomplete.c~ b/pcomplete.c~
new file mode 100644 (file)
index 0000000..74773c7
--- /dev/null
@@ -0,0 +1,1634 @@
+/* pcomplete.c - functions to generate lists of matches for programmable completion. */
+
+/* Copyright (C) 1999-2012 Free Software Foundation, Inc.
+
+   This file is part of GNU Bash, the Bourne Again SHell.
+
+   Bash is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   Bash is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with Bash.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#if defined (PROGRAMMABLE_COMPLETION)
+
+#include "bashtypes.h"
+#include "posixstat.h"
+
+#if defined (HAVE_UNISTD_H)
+#  include <unistd.h>
+#endif
+
+#include <signal.h>
+
+#if defined (PREFER_STDARG)
+#  include <stdarg.h>
+#else
+#  include <varargs.h>
+#endif
+
+#include <sys/time.h>
+
+#include <stdio.h>
+#include "bashansi.h"
+#include "bashintl.h"
+
+#include "shell.h"
+#include "pcomplete.h"
+#include "alias.h"
+#include "bashline.h"
+#include "execute_cmd.h"
+#include "pathexp.h"
+
+#if defined (JOB_CONTROL)
+#  include "jobs.h"
+#endif
+
+#if !defined (NSIG)
+#  include "trap.h"
+#endif
+
+#include "builtins.h"
+#include "builtins/common.h"
+#include "builtins/builtext.h"
+
+#include <glob/glob.h>
+#include <glob/strmatch.h>
+
+#include <readline/rlconf.h>
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#define PCOMP_RETRYFAIL        256
+
+#ifdef STRDUP
+#  undef STRDUP
+#endif
+#define STRDUP(x)      ((x) ? savestring (x) : (char *)NULL)
+
+typedef SHELL_VAR **SVFUNC ();
+
+#ifndef HAVE_STRPBRK
+extern char *strpbrk __P((char *, char *));
+#endif
+
+extern int array_needs_making;
+extern STRING_INT_ALIST word_token_alist[];
+extern char *signal_names[];
+extern sh_builtin_func_t *last_shell_builtin, *this_shell_builtin;
+
+#if defined (DEBUG)
+#if defined (PREFER_STDARG)
+static void debug_printf (const char *, ...)  __attribute__((__format__ (printf, 1, 2)));
+#endif
+#endif /* DEBUG */
+
+static int it_init_joblist __P((ITEMLIST *, int));
+
+static int it_init_aliases __P((ITEMLIST *));
+static int it_init_arrayvars __P((ITEMLIST *));
+static int it_init_bindings __P((ITEMLIST *));
+static int it_init_builtins __P((ITEMLIST *));
+static int it_init_disabled __P((ITEMLIST *));
+static int it_init_enabled __P((ITEMLIST *));
+static int it_init_exported __P((ITEMLIST *));
+static int it_init_functions __P((ITEMLIST *));
+static int it_init_helptopics __P((ITEMLIST *));
+static int it_init_hostnames __P((ITEMLIST *));
+static int it_init_jobs __P((ITEMLIST *));
+static int it_init_running __P((ITEMLIST *));
+static int it_init_stopped __P((ITEMLIST *));
+static int it_init_keywords __P((ITEMLIST *));
+static int it_init_signals __P((ITEMLIST *));
+static int it_init_variables __P((ITEMLIST *));
+static int it_init_setopts __P((ITEMLIST *));
+static int it_init_shopts __P((ITEMLIST *));
+
+static int shouldexp_filterpat __P((char *));
+static char *preproc_filterpat __P((char *, char *));
+
+static void init_itemlist_from_varlist __P((ITEMLIST *, SVFUNC *));
+
+static STRINGLIST *gen_matches_from_itemlist __P((ITEMLIST *, const char *));
+static STRINGLIST *gen_action_completions __P((COMPSPEC *, const char *));
+static STRINGLIST *gen_globpat_matches __P((COMPSPEC *, const char *));
+static STRINGLIST *gen_wordlist_matches __P((COMPSPEC *, const char *));
+static STRINGLIST *gen_shell_function_matches __P((COMPSPEC *, const char *,
+                                                  const char *,
+                                                  char *, int, WORD_LIST *,
+                                                  int, int, int *));
+static STRINGLIST *gen_command_matches __P((COMPSPEC *, const char *,
+                                           const char *,
+                                           char *, int, WORD_LIST *,
+                                           int, int));
+
+static STRINGLIST *gen_progcomp_completions __P((const char *, const char *,
+                                                const char *,
+                                                int, int, int *, int *,
+                                                COMPSPEC **));
+
+static char *pcomp_filename_completion_function __P((const char *, int));
+
+#if defined (ARRAY_VARS)
+static SHELL_VAR *bind_comp_words __P((WORD_LIST *));
+#endif
+static void bind_compfunc_variables __P((char *, int, WORD_LIST *, int, int));
+static void unbind_compfunc_variables __P((int));
+static WORD_LIST *build_arg_list __P((char *, const char *, const char *, WORD_LIST *, int));
+static WORD_LIST *command_line_to_word_list __P((char *, int, int, int *, int *));
+
+#ifdef DEBUG
+static int progcomp_debug = 0;
+#endif
+
+int prog_completion_enabled = 1;
+
+/* These are used to manage the arrays of strings for possible completions. */
+ITEMLIST it_aliases = { 0, it_init_aliases, (STRINGLIST *)0 };
+ITEMLIST it_arrayvars  = { LIST_DYNAMIC, it_init_arrayvars, (STRINGLIST *)0 };
+ITEMLIST it_bindings  = { 0, it_init_bindings, (STRINGLIST *)0 };
+ITEMLIST it_builtins  = { 0, it_init_builtins, (STRINGLIST *)0 };
+ITEMLIST it_commands = { LIST_DYNAMIC };       /* unused */
+ITEMLIST it_directories = { LIST_DYNAMIC };    /* unused */
+ITEMLIST it_disabled = { 0, it_init_disabled, (STRINGLIST *)0 };
+ITEMLIST it_enabled = { 0, it_init_enabled, (STRINGLIST *)0 };
+ITEMLIST it_exports  = { LIST_DYNAMIC, it_init_exported, (STRINGLIST *)0 };
+ITEMLIST it_files = { LIST_DYNAMIC };          /* unused */
+ITEMLIST it_functions  = { 0, it_init_functions, (STRINGLIST *)0 };
+ITEMLIST it_helptopics  = { 0, it_init_helptopics, (STRINGLIST *)0 };
+ITEMLIST it_hostnames  = { LIST_DYNAMIC, it_init_hostnames, (STRINGLIST *)0 };
+ITEMLIST it_groups = { LIST_DYNAMIC };         /* unused */
+ITEMLIST it_jobs = { LIST_DYNAMIC, it_init_jobs, (STRINGLIST *)0 };
+ITEMLIST it_keywords = { 0, it_init_keywords, (STRINGLIST *)0 };
+ITEMLIST it_running = { LIST_DYNAMIC, it_init_running, (STRINGLIST *)0 };
+ITEMLIST it_services = { LIST_DYNAMIC };       /* unused */
+ITEMLIST it_setopts = { 0, it_init_setopts, (STRINGLIST *)0 };
+ITEMLIST it_shopts = { 0, it_init_shopts, (STRINGLIST *)0 };
+ITEMLIST it_signals = { 0, it_init_signals, (STRINGLIST *)0 };
+ITEMLIST it_stopped = { LIST_DYNAMIC, it_init_stopped, (STRINGLIST *)0 };
+ITEMLIST it_users = { LIST_DYNAMIC };          /* unused */
+ITEMLIST it_variables = { LIST_DYNAMIC, it_init_variables, (STRINGLIST *)0 };
+
+COMPSPEC *pcomp_curcs;
+const char *pcomp_curcmd;
+
+#ifdef DEBUG
+/* Debugging code */
+static void
+#if defined (PREFER_STDARG)
+debug_printf (const char *format, ...)
+#else
+debug_printf (format, va_alist)
+     const char *format;
+     va_dcl
+#endif
+{
+  va_list args;
+
+  if (progcomp_debug == 0)
+    return;
+
+  SH_VA_START (args, format);
+
+  fprintf (stdout, "DEBUG: ");
+  vfprintf (stdout, format, args);
+  fprintf (stdout, "\n");
+
+  rl_on_new_line ();
+
+  va_end (args);
+}
+#endif
+
+/* Functions to manage the item lists */
+
+void
+set_itemlist_dirty (it)
+     ITEMLIST *it;
+{
+  it->flags |= LIST_DIRTY;
+}
+
+void
+initialize_itemlist (itp)
+     ITEMLIST *itp;
+{
+  (*itp->list_getter) (itp);
+  itp->flags |= LIST_INITIALIZED;
+  itp->flags &= ~LIST_DIRTY;
+}
+
+void
+clean_itemlist (itp)
+     ITEMLIST *itp;
+{
+  STRINGLIST *sl;
+
+  sl = itp->slist;
+  if (sl)
+    {
+      if ((itp->flags & (LIST_DONTFREEMEMBERS|LIST_DONTFREE)) == 0)
+       strvec_flush (sl->list);
+      if ((itp->flags & LIST_DONTFREE) == 0)
+       free (sl->list);
+      free (sl);
+    }
+  itp->slist = (STRINGLIST *)NULL;
+  itp->flags &= ~(LIST_DONTFREE|LIST_DONTFREEMEMBERS|LIST_INITIALIZED|LIST_DIRTY);
+}
+
+
+static int
+shouldexp_filterpat (s)
+     char *s;
+{
+  register char *p;
+
+  for (p = s; p && *p; p++)
+    {
+      if (*p == '\\')
+       p++;
+      else if (*p == '&')
+       return 1;
+    }
+  return 0;
+}
+
+/* Replace any instance of `&' in PAT with TEXT.  Backslash may be used to
+   quote a `&' and inhibit substitution.  Returns a new string.  This just
+   calls stringlib.c:strcreplace(). */
+static char *
+preproc_filterpat (pat, text)
+     char *pat;
+     char *text;
+{
+  char *ret;
+
+  ret = strcreplace (pat, '&', text, 1);
+  return ret;
+}
+       
+/* Remove any match of FILTERPAT from SL.  A `&' in FILTERPAT is replaced by
+   TEXT.  A leading `!' in FILTERPAT negates the pattern; in this case
+   any member of SL->list that does *not* match will be removed.  This returns
+   a new STRINGLIST with the matching members of SL *copied*.  Any
+   non-matching members of SL->list are *freed*. */   
+STRINGLIST *
+filter_stringlist (sl, filterpat, text)
+     STRINGLIST *sl;
+     char *filterpat, *text;
+{
+  int i, m, not;
+  STRINGLIST *ret;
+  char *npat, *t;
+
+  if (sl == 0 || sl->list == 0 || sl->list_len == 0)
+    return sl;
+
+  npat = shouldexp_filterpat (filterpat) ? preproc_filterpat (filterpat, text) : filterpat;
+
+  not = (npat[0] == '!');
+  t = not ? npat + 1 : npat;
+
+  ret = strlist_create (sl->list_size);
+  for (i = 0; i < sl->list_len; i++)
+    {
+      m = strmatch (t, sl->list[i], FNMATCH_EXTFLAG);
+      if ((not && m == FNM_NOMATCH) || (not == 0 && m != FNM_NOMATCH))
+       free (sl->list[i]);
+      else
+       ret->list[ret->list_len++] = sl->list[i];
+    }
+
+  ret->list[ret->list_len] = (char *)NULL;
+  if (npat != filterpat)
+    free (npat);
+
+  return ret;
+}
+
+/* Turn an array of strings returned by rl_completion_matches into a STRINGLIST.
+   This understands how rl_completion_matches sets matches[0] (the lcd of the
+   strings in the list, unless it's the only match). */
+STRINGLIST *
+completions_to_stringlist (matches)
+     char **matches;
+{
+  STRINGLIST *sl;
+  int mlen, i, n;
+
+  mlen = (matches == 0) ? 0 : strvec_len (matches);
+  sl = strlist_create (mlen + 1);
+
+  if (matches == 0 || matches[0] == 0)
+    return sl;
+
+  if (matches[1] == 0)
+    {
+      sl->list[0] = STRDUP (matches[0]);
+      sl->list[sl->list_len = 1] = (char *)NULL;
+      return sl;
+    }
+
+  for (i = 1, n = 0; i < mlen; i++, n++)
+    sl->list[n] = STRDUP (matches[i]);
+  sl->list_len = n;
+  sl->list[n] = (char *)NULL;
+
+  return sl;
+}
+
+/* Functions to manage the various ITEMLISTs that we populate internally.
+   The caller is responsible for setting ITP->flags correctly. */
+
+static int
+it_init_aliases (itp)
+     ITEMLIST *itp;
+{
+#ifdef ALIAS
+  alias_t **alias_list;
+  register int i, n;
+  STRINGLIST *sl;
+
+  alias_list = all_aliases ();
+  if (alias_list == 0)
+    {
+      itp->slist = (STRINGLIST *)NULL;
+      return 0;
+    }
+  for (n = 0; alias_list[n]; n++)
+    ;
+  sl = strlist_create (n+1);
+  for (i = 0; i < n; i++)
+    sl->list[i] = STRDUP (alias_list[i]->name);
+  sl->list[n] = (char *)NULL;
+  sl->list_size = sl->list_len = n;
+  itp->slist = sl;
+#else
+  itp->slist = (STRINGLIST *)NULL;
+#endif
+  free (alias_list);
+  return 1;
+}
+
+static void
+init_itemlist_from_varlist (itp, svfunc)
+     ITEMLIST *itp;
+     SVFUNC *svfunc;
+{
+  SHELL_VAR **vlist;
+  STRINGLIST *sl;
+  register int i, n;
+
+  vlist = (*svfunc) ();
+  if (vlist == 0)
+    {
+      itp->slist = (STRINGLIST *)NULL;
+      return;
+    }    
+  for (n = 0; vlist[n]; n++)
+    ;
+  sl = strlist_create (n+1);
+  for (i = 0; i < n; i++)
+    sl->list[i] = savestring (vlist[i]->name);
+  sl->list[sl->list_len = n] = (char *)NULL;
+  itp->slist = sl;
+}
+
+static int
+it_init_arrayvars (itp)
+     ITEMLIST *itp;
+{
+#if defined (ARRAY_VARS)
+  init_itemlist_from_varlist (itp, all_array_variables);
+  return 1;
+#else
+  return 0;
+#endif
+}
+
+static int
+it_init_bindings (itp)
+     ITEMLIST *itp;
+{
+  char **blist;
+  STRINGLIST *sl;
+
+  /* rl_funmap_names allocates blist, but not its members */
+  blist = (char **)rl_funmap_names (); /* XXX fix const later */
+  sl = strlist_create (0);
+  sl->list = blist;
+  sl->list_size = 0;
+  sl->list_len = strvec_len (sl->list);
+  itp->flags |= LIST_DONTFREEMEMBERS;
+  itp->slist = sl;
+
+  return 0;
+}
+
+static int
+it_init_builtins (itp)
+     ITEMLIST *itp;
+{
+  STRINGLIST *sl;
+  register int i, n;
+
+  sl = strlist_create (num_shell_builtins);
+  for (i = n = 0; i < num_shell_builtins; i++)
+    if (shell_builtins[i].function)
+      sl->list[n++] = shell_builtins[i].name;
+  sl->list[sl->list_len = n] = (char *)NULL;
+  itp->flags |= LIST_DONTFREEMEMBERS;
+  itp->slist = sl;
+  return 0;
+}
+
+static int
+it_init_enabled (itp)
+     ITEMLIST *itp;
+{
+  STRINGLIST *sl;
+  register int i, n;
+
+  sl = strlist_create (num_shell_builtins);
+  for (i = n = 0; i < num_shell_builtins; i++)
+    {
+      if (shell_builtins[i].function && (shell_builtins[i].flags & BUILTIN_ENABLED))
+       sl->list[n++] = shell_builtins[i].name;
+    }
+  sl->list[sl->list_len = n] = (char *)NULL;
+  itp->flags |= LIST_DONTFREEMEMBERS;
+  itp->slist = sl;
+  return 0;
+}
+
+static int
+it_init_disabled (itp)
+     ITEMLIST *itp;
+{
+  STRINGLIST *sl;
+  register int i, n;
+
+  sl = strlist_create (num_shell_builtins);
+  for (i = n = 0; i < num_shell_builtins; i++)
+    {
+      if (shell_builtins[i].function && ((shell_builtins[i].flags & BUILTIN_ENABLED) == 0))
+       sl->list[n++] = shell_builtins[i].name;
+    }
+  sl->list[sl->list_len = n] = (char *)NULL;
+  itp->flags |= LIST_DONTFREEMEMBERS;
+  itp->slist = sl;
+  return 0;
+}
+
+static int
+it_init_exported (itp)
+     ITEMLIST *itp;
+{
+  init_itemlist_from_varlist (itp, all_exported_variables);
+  return 0;
+}
+
+static int
+it_init_functions (itp)
+     ITEMLIST *itp;
+{
+  init_itemlist_from_varlist (itp, all_visible_functions);
+  return 0;
+}
+
+/* Like it_init_builtins, but includes everything the help builtin looks at,
+   not just builtins with an active implementing function. */
+static int
+it_init_helptopics (itp)
+     ITEMLIST *itp;
+{
+  STRINGLIST *sl;
+  register int i, n;
+
+  sl = strlist_create (num_shell_builtins);
+  for (i = n = 0; i < num_shell_builtins; i++)
+    sl->list[n++] = shell_builtins[i].name;
+  sl->list[sl->list_len = n] = (char *)NULL;
+  itp->flags |= LIST_DONTFREEMEMBERS;
+  itp->slist = sl;
+  return 0;
+}
+
+static int
+it_init_hostnames (itp)
+     ITEMLIST *itp;
+{
+  STRINGLIST *sl;
+
+  sl = strlist_create (0);
+  sl->list = get_hostname_list ();
+  sl->list_len = sl->list ? strvec_len (sl->list) : 0;
+  sl->list_size = sl->list_len;
+  itp->slist = sl;
+  itp->flags |= LIST_DONTFREEMEMBERS|LIST_DONTFREE;
+  return 0;
+}
+
+static int
+it_init_joblist (itp, jstate)
+     ITEMLIST *itp;
+     int jstate;
+{
+#if defined (JOB_CONTROL)
+  STRINGLIST *sl;
+  register int i;
+  register PROCESS *p;
+  char *s, *t;
+  JOB *j;
+  JOB_STATE ws;                /* wanted state */
+
+  ws = JNONE;
+  if (jstate == 0)
+    ws = JRUNNING;
+  else if (jstate == 1)
+    ws = JSTOPPED;
+
+  sl = strlist_create (js.j_jobslots);
+  for (i = js.j_jobslots - 1; i >= 0; i--)
+    {
+      j = get_job_by_jid (i);
+      if (j == 0)
+       continue;
+      p = j->pipe;
+      if (jstate == -1 || JOBSTATE(i) == ws)
+       {
+         s = savestring (p->command);
+         t = strpbrk (s, " \t\n");
+         if (t)
+           *t = '\0';
+         sl->list[sl->list_len++] = s;
+       }
+    }
+  itp->slist = sl;
+#else
+  itp->slist = (STRINGLIST *)NULL;
+#endif
+  return 0;
+}
+
+static int
+it_init_jobs (itp)
+     ITEMLIST *itp;
+{
+  return (it_init_joblist (itp, -1));
+}
+
+static int
+it_init_running (itp)
+     ITEMLIST *itp;
+{
+  return (it_init_joblist (itp, 0));
+}
+
+static int
+it_init_stopped (itp)
+     ITEMLIST *itp;
+{
+  return (it_init_joblist (itp, 1));
+}
+
+static int
+it_init_keywords (itp)
+     ITEMLIST *itp;
+{
+  STRINGLIST *sl;
+  register int i, n;
+
+  for (n = 0; word_token_alist[n].word; n++)
+    ;
+  sl = strlist_create (n);
+  for (i = 0; i < n; i++)
+    sl->list[i] = word_token_alist[i].word;
+  sl->list[sl->list_len = i] = (char *)NULL;
+  itp->flags |= LIST_DONTFREEMEMBERS;
+  itp->slist = sl;
+  return 0;
+}
+
+static int
+it_init_signals (itp)
+     ITEMLIST *itp;
+{
+  STRINGLIST *sl;
+
+  sl = strlist_create (0);
+  sl->list = signal_names;
+  sl->list_len = strvec_len (sl->list);
+  itp->flags |= LIST_DONTFREE;
+  itp->slist = sl;
+  return 0;
+}
+
+static int
+it_init_variables (itp)
+     ITEMLIST *itp;
+{
+  init_itemlist_from_varlist (itp, all_visible_variables);
+  return 0;
+}
+
+static int
+it_init_setopts (itp)
+     ITEMLIST *itp;
+{
+  STRINGLIST *sl;
+
+  sl = strlist_create (0);
+  sl->list = get_minus_o_opts ();
+  sl->list_len = strvec_len (sl->list);
+  itp->slist = sl;
+  itp->flags |= LIST_DONTFREEMEMBERS;
+  return 0;
+}
+
+static int
+it_init_shopts (itp)
+     ITEMLIST *itp;
+{
+  STRINGLIST *sl;
+
+  sl = strlist_create (0);
+  sl->list = get_shopt_options ();
+  sl->list_len = strvec_len (sl->list);
+  itp->slist = sl;
+  itp->flags |= LIST_DONTFREEMEMBERS;
+  return 0;
+}
+
+/* Generate a list of all matches for TEXT using the STRINGLIST in itp->slist
+   as the list of possibilities.  If the itemlist has been marked dirty or
+   it should be regenerated every time, destroy the old STRINGLIST and make a
+   new one before trying the match.  TEXT is dequoted before attempting a
+   match. */
+static STRINGLIST *
+gen_matches_from_itemlist (itp, text)
+     ITEMLIST *itp;
+     const char *text;
+{
+  STRINGLIST *ret, *sl;
+  int tlen, i, n;
+  char *ntxt;
+
+  if ((itp->flags & (LIST_DIRTY|LIST_DYNAMIC)) ||
+      (itp->flags & LIST_INITIALIZED) == 0)
+    {
+      if (itp->flags & (LIST_DIRTY|LIST_DYNAMIC))
+       clean_itemlist (itp);
+      if ((itp->flags & LIST_INITIALIZED) == 0)
+       initialize_itemlist (itp);
+    }
+  if (itp->slist == 0)
+    return ((STRINGLIST *)NULL);
+  ret = strlist_create (itp->slist->list_len+1);
+  sl = itp->slist;
+
+  ntxt = bash_dequote_text (text);
+  tlen = STRLEN (ntxt);
+
+  for (i = n = 0; i < sl->list_len; i++)
+    {
+      if (tlen == 0 || STREQN (sl->list[i], ntxt, tlen))
+       ret->list[n++] = STRDUP (sl->list[i]);
+    }
+  ret->list[ret->list_len = n] = (char *)NULL;
+
+  FREE (ntxt);
+  return ret;
+}
+
+/* A wrapper for rl_filename_completion_function that dequotes the filename
+   before attempting completions. */
+static char *
+pcomp_filename_completion_function (text, state)
+     const char *text;
+     int state;
+{
+  static char *dfn;    /* dequoted filename */
+  int qc;
+  int iscompgen, iscompleting;
+
+  if (state == 0)
+    {
+      FREE (dfn);
+      /* remove backslashes quoting special characters in filenames. */
+      /* There are roughtly three paths we can follow to get here:
+               1.  complete -f
+               2.  compgen -f "$word" from a completion function
+               3.  compgen -f "$word" from the command line
+        They all need to be handled.
+
+        In the first two cases, readline will run the filename dequoting
+        function in rl_filename_completion_function if it found a filename
+        quoting character in the word to be completed
+        (rl_completion_found_quote).  We run the dequoting function here
+        if we're running compgen, we're not completing, and the
+        rl_filename_completion_function won't dequote the filename
+        (rl_completion_found_quote == 0). */
+      iscompgen = this_shell_builtin == compgen_builtin;
+      iscompleting = RL_ISSTATE (RL_STATE_COMPLETING);
+      if (iscompgen && iscompleting == 0 && rl_completion_found_quote == 0)
+       {
+         /* Use rl_completion_quote_character because any single or
+            double quotes have been removed by the time TEXT makes it
+            here, and we don't want to remove backslashes inside
+            quoted strings. */
+         dfn = (*rl_filename_dequoting_function) ((char *)text, rl_completion_quote_character);
+       }
+      else
+       dfn = savestring (text);
+    }
+
+  return (rl_filename_completion_function (dfn, state));
+}
+
+#define GEN_COMPS(bmap, flag, it, text, glist, tlist) \
+  do { \
+    if (bmap & flag) \
+      { \
+       tlist = gen_matches_from_itemlist (it, text); \
+       if (tlist) \
+         { \
+           glist = strlist_append (glist, tlist); \
+           strlist_dispose (tlist); \
+         } \
+      } \
+  } while (0)
+
+#define GEN_XCOMPS(bmap, flag, text, func, cmatches, glist, tlist) \
+  do { \
+    if (bmap & flag) \
+      { \
+       cmatches = rl_completion_matches (text, func); \
+       tlist = completions_to_stringlist (cmatches); \
+       glist = strlist_append (glist, tlist); \
+       strvec_dispose (cmatches); \
+       strlist_dispose (tlist); \
+      } \
+  } while (0)
+
+/* Functions to generate lists of matches from the actions member of CS. */
+
+static STRINGLIST *
+gen_action_completions (cs, text)
+     COMPSPEC *cs;
+     const char *text;
+{
+  STRINGLIST *ret, *tmatches;
+  char **cmatches;     /* from rl_completion_matches ... */
+  unsigned long flags;
+  int t;
+
+  ret = tmatches = (STRINGLIST *)NULL;
+  flags = cs->actions;
+
+  GEN_COMPS (flags, CA_ALIAS, &it_aliases, text, ret, tmatches);
+  GEN_COMPS (flags, CA_ARRAYVAR, &it_arrayvars, text, ret, tmatches);
+  GEN_COMPS (flags, CA_BINDING, &it_bindings, text, ret, tmatches);
+  GEN_COMPS (flags, CA_BUILTIN, &it_builtins, text, ret, tmatches);
+  GEN_COMPS (flags, CA_DISABLED, &it_disabled, text, ret, tmatches);
+  GEN_COMPS (flags, CA_ENABLED, &it_enabled, text, ret, tmatches);
+  GEN_COMPS (flags, CA_EXPORT, &it_exports, text, ret, tmatches);
+  GEN_COMPS (flags, CA_FUNCTION, &it_functions, text, ret, tmatches);
+  GEN_COMPS (flags, CA_HELPTOPIC, &it_helptopics, text, ret, tmatches);
+  GEN_COMPS (flags, CA_HOSTNAME, &it_hostnames, text, ret, tmatches);
+  GEN_COMPS (flags, CA_JOB, &it_jobs, text, ret, tmatches);
+  GEN_COMPS (flags, CA_KEYWORD, &it_keywords, text, ret, tmatches);
+  GEN_COMPS (flags, CA_RUNNING, &it_running, text, ret, tmatches);
+  GEN_COMPS (flags, CA_SETOPT, &it_setopts, text, ret, tmatches);
+  GEN_COMPS (flags, CA_SHOPT, &it_shopts, text, ret, tmatches);
+  GEN_COMPS (flags, CA_SIGNAL, &it_signals, text, ret, tmatches);
+  GEN_COMPS (flags, CA_STOPPED, &it_stopped, text, ret, tmatches);
+  GEN_COMPS (flags, CA_VARIABLE, &it_variables, text, ret, tmatches);
+
+  GEN_XCOMPS(flags, CA_COMMAND, text, command_word_completion_function, cmatches, ret, tmatches);
+  GEN_XCOMPS(flags, CA_FILE, text, pcomp_filename_completion_function, cmatches, ret, tmatches);
+  GEN_XCOMPS(flags, CA_USER, text, rl_username_completion_function, cmatches, ret, tmatches);
+  GEN_XCOMPS(flags, CA_GROUP, text, bash_groupname_completion_function, cmatches, ret, tmatches);
+  GEN_XCOMPS(flags, CA_SERVICE, text, bash_servicename_completion_function, cmatches, ret, tmatches);
+
+  /* And lastly, the special case for directories */
+  if (flags & CA_DIRECTORY)
+    {
+      t = rl_filename_completion_desired;
+      rl_completion_mark_symlink_dirs = 1;     /* override user preference */
+      cmatches = bash_directory_completion_matches (text);
+      /* If we did not want filename completion before this, and there are
+        no matches, turn off rl_filename_completion_desired so whatever
+        matches we get are not treated as filenames (it gets turned on by
+        rl_filename_completion_function unconditionally). */
+      if (t == 0 && cmatches == 0 && rl_filename_completion_desired == 1)
+        rl_filename_completion_desired = 0;
+      tmatches = completions_to_stringlist (cmatches);
+      ret = strlist_append (ret, tmatches);
+      strvec_dispose (cmatches);
+      strlist_dispose (tmatches);
+    }
+
+  return ret;
+}
+
+/* Generate a list of matches for CS->globpat.  Unresolved: should this use
+   TEXT as a match prefix, or just go without?  Currently, the code does not
+   use TEXT, just globs CS->globpat and returns the results.  If we do decide
+   to use TEXT, we should call quote_string_for_globbing before the call to
+   glob_filename. */
+static STRINGLIST *
+gen_globpat_matches (cs, text)
+      COMPSPEC *cs;
+      const char *text;
+{
+  STRINGLIST *sl;
+
+  sl = strlist_create (0);
+  sl->list = glob_filename (cs->globpat, 0);
+  if (GLOB_FAILED (sl->list))
+    sl->list = (char **)NULL;
+  if (sl->list)
+    sl->list_len = sl->list_size = strvec_len (sl->list);
+  return sl;
+}
+
+/* Perform the shell word expansions on CS->words and return the results.
+   Again, this ignores TEXT. */
+static STRINGLIST *
+gen_wordlist_matches (cs, text)
+     COMPSPEC *cs;
+     const char *text;
+{
+  WORD_LIST *l, *l2;
+  STRINGLIST *sl;
+  int nw, tlen;
+  char *ntxt;          /* dequoted TEXT to use in comparisons */
+  struct timeval d1, d2, t1, t2, t3;
+
+  if (cs->words == 0 || cs->words[0] == '\0')
+    return ((STRINGLIST *)NULL);
+
+gettimeofday(&t1, 0);
+  /* This used to be a simple expand_string(cs->words, 0), but that won't
+     do -- there's no way to split a simple list into individual words
+     that way, since the shell semantics say that word splitting is done
+     only on the results of expansion. */
+  l = split_at_delims (cs->words, strlen (cs->words), (char *)NULL, -1, 0, (int *)NULL, (int *)NULL);
+  if (l == 0)
+    return ((STRINGLIST *)NULL);
+  /* This will jump back to the top level if the expansion fails... */
+  l2 = expand_words_shellexp (l);
+  dispose_words (l);
+
+gettimeofday(&t2, 0);
+
+  nw = list_length (l2);
+  sl = strlist_create (nw + 1);
+
+  ntxt = bash_dequote_text (text);
+  tlen = STRLEN (ntxt);
+
+  for (nw = 0, l = l2; l; l = l->next)
+    {
+      if (tlen == 0 || STREQN (l->word->word, ntxt, tlen))
+       sl->list[nw++] = STRDUP (l->word->word);
+    }
+  sl->list[sl->list_len = nw] = (char *)NULL;
+
+gettimeofday (&t3, 0);
+difftimeval(&d1, &t1, &t2);
+difftimeval(&d2, &t2, &t3);
+
+itrace("word splitting with split_at_delims and quote removal:");
+print_timeval(stderr, &d1);
+fprintf(stderr, "\n");
+itrace("matching and new list population:");
+print_timeval(stderr, &d2);
+fprintf(stderr, "\n");
+
+  dispose_words (l2);
+  FREE (ntxt);
+  return sl;
+}
+
+#ifdef ARRAY_VARS
+
+static SHELL_VAR *
+bind_comp_words (lwords)
+     WORD_LIST *lwords;
+{
+  SHELL_VAR *v;
+
+  v = find_variable ("COMP_WORDS");
+  if (v == 0)
+    v = make_new_array_variable ("COMP_WORDS");
+  if (readonly_p (v))
+    VUNSETATTR (v, att_readonly);
+  if (array_p (v) == 0)
+    v = convert_var_to_array (v);
+  v = assign_array_var_from_word_list (v, lwords, 0);
+
+  VUNSETATTR (v, att_invisible);
+  return v;
+}
+#endif /* ARRAY_VARS */
+
+static void
+bind_compfunc_variables (line, ind, lwords, cw, exported)
+     char *line;
+     int ind;
+     WORD_LIST *lwords;
+     int cw, exported;
+{
+  char ibuf[INT_STRLEN_BOUND(int) + 1];
+  char *value;
+  SHELL_VAR *v;
+
+  /* Set the variables that the function expects while it executes.  Maybe
+     these should be in the function environment (temporary_env). */
+  v = bind_variable ("COMP_LINE", line, 0);
+  if (v && exported)
+    VSETATTR(v, att_exported);
+
+  value = inttostr (ind, ibuf, sizeof(ibuf));
+  v = bind_int_variable ("COMP_POINT", value);
+  if (v && exported)
+    VSETATTR(v, att_exported);
+
+  value = inttostr (rl_completion_type, ibuf, sizeof (ibuf));
+  v = bind_int_variable ("COMP_TYPE", value);
+  if (v && exported)
+    VSETATTR(v, att_exported);
+
+  value = inttostr (rl_completion_invoking_key, ibuf, sizeof (ibuf));
+  v = bind_int_variable ("COMP_KEY", value);
+  if (v && exported)
+    VSETATTR(v, att_exported);
+
+  /* Since array variables can't be exported, we don't bother making the
+     array of words. */
+  if (exported == 0)
+    {
+#ifdef ARRAY_VARS
+      v = bind_comp_words (lwords);
+      value = inttostr (cw, ibuf, sizeof(ibuf));
+      bind_int_variable ("COMP_CWORD", value);
+#endif
+    }
+  else
+    array_needs_making = 1;
+}
+
+static void
+unbind_compfunc_variables (exported)
+     int exported;
+{
+  unbind_variable ("COMP_LINE");
+  unbind_variable ("COMP_POINT");
+  unbind_variable ("COMP_TYPE");
+  unbind_variable ("COMP_KEY");
+#ifdef ARRAY_VARS
+  unbind_variable ("COMP_WORDS");
+  unbind_variable ("COMP_CWORD");
+#endif
+  if (exported)
+    array_needs_making = 1;
+}
+
+/* Build the list of words to pass to a function or external command
+   as arguments.  When the function or command is invoked,
+
+       $0 == function or command being invoked
+       $1 == command name
+       $2 == word to be completed (possibly null)
+       $3 == previous word
+
+   Functions can access all of the words in the current command line
+   with the COMP_WORDS array.  External commands cannot; they have to
+   make do with the COMP_LINE and COMP_POINT variables. */
+
+static WORD_LIST *
+build_arg_list (cmd, cname, text, lwords, ind)
+     char *cmd;
+     const char *cname;
+     const char *text;
+     WORD_LIST *lwords;
+     int ind;
+{
+  WORD_LIST *ret, *cl, *l;
+  WORD_DESC *w;
+  int i;
+
+  ret = (WORD_LIST *)NULL;
+  w = make_word (cmd);
+  ret = make_word_list (w, (WORD_LIST *)NULL); /* $0 */
+
+  w = make_word (cname);                       /* $1 */
+  cl = ret->next = make_word_list (w, (WORD_LIST *)NULL);
+
+  w = make_word (text);
+  cl->next = make_word_list (w, (WORD_LIST *)NULL);    /* $2 */
+  cl = cl->next;
+
+  /* Search lwords for current word */
+  for (l = lwords, i = 1; l && i < ind-1; l = l->next, i++)
+    ;
+  w = (l && l->word) ? copy_word (l->word) : make_word ("");
+  cl->next = make_word_list (w, (WORD_LIST *)NULL);
+
+  return ret;
+}
+
+/* Build a command string with
+       $0 == cs->funcname      (function to execute for completion list)
+       $1 == command name      (command being completed)
+       $2 = word to be completed (possibly null)
+       $3 = previous word
+   and run in the current shell.  The function should put its completion
+   list into the array variable COMPREPLY.  We build a STRINGLIST
+   from the results and return it.
+
+   Since the shell function should return its list of matches in an array
+   variable, this does nothing if arrays are not compiled into the shell. */
+
+static STRINGLIST *
+gen_shell_function_matches (cs, cmd, text, line, ind, lwords, nw, cw, foundp)
+     COMPSPEC *cs;
+     const char *cmd;
+     const char *text;
+     char *line;
+     int ind;
+     WORD_LIST *lwords;
+     int nw, cw;
+     int *foundp;
+{
+  char *funcname;
+  STRINGLIST *sl;
+  SHELL_VAR *f, *v;
+  WORD_LIST *cmdlist;
+  int fval, found;
+  sh_parser_state_t ps;
+  sh_parser_state_t * restrict pps;
+#if defined (ARRAY_VARS)
+  ARRAY *a;
+#endif
+
+  found = 0;
+  if (foundp)
+    *foundp = found;
+
+  funcname = cs->funcname;
+  f = find_function (funcname);
+  if (f == 0)
+    {
+      internal_error (_("completion: function `%s' not found"), funcname);
+      rl_ding ();
+      rl_on_new_line ();
+      return ((STRINGLIST *)NULL);
+    }
+
+#if !defined (ARRAY_VARS)
+  return ((STRINGLIST *)NULL);
+#else
+
+  /* We pass cw - 1 because command_line_to_word_list returns indices that are
+     1-based, while bash arrays are 0-based. */
+  bind_compfunc_variables (line, ind, lwords, cw - 1, 0);
+
+  cmdlist = build_arg_list (funcname, cmd, text, lwords, cw);
+
+  pps = &ps;
+  save_parser_state (pps);
+  begin_unwind_frame ("gen-shell-function-matches");
+  add_unwind_protect (restore_parser_state, (char *)pps);
+  add_unwind_protect (dispose_words, (char *)cmdlist);
+  add_unwind_protect (unbind_compfunc_variables, (char *)0);
+
+  fval = execute_shell_function (f, cmdlist);  
+
+  discard_unwind_frame ("gen-shell-function-matches");
+  restore_parser_state (pps);
+
+  found = fval != EX_NOTFOUND;
+  if (fval == EX_RETRYFAIL)
+    found |= PCOMP_RETRYFAIL;
+  if (foundp)
+    *foundp = found;
+
+  /* Now clean up and destroy everything. */
+  dispose_words (cmdlist);
+  unbind_compfunc_variables (0);
+
+  /* The list of completions is returned in the array variable COMPREPLY. */
+  v = find_variable ("COMPREPLY");
+  if (v == 0)
+    return ((STRINGLIST *)NULL);
+  if (array_p (v) == 0)
+    v = convert_var_to_array (v);
+
+  VUNSETATTR (v, att_invisible);
+
+  a = array_cell (v);
+  if (found == 0 || (found & PCOMP_RETRYFAIL) || a == 0 || array_empty (a))
+    sl = (STRINGLIST *)NULL;
+  else
+    {
+      /* XXX - should we filter the list of completions so only those matching
+        TEXT are returned?  Right now, we do not. */
+      sl = strlist_create (0);
+      sl->list = array_to_argv (a);
+      sl->list_len = sl->list_size = array_num_elements (a);
+    }
+
+  /* XXX - should we unbind COMPREPLY here? */
+  unbind_variable ("COMPREPLY");
+
+  return (sl);
+#endif
+}
+
+/* Build a command string with
+       $0 == cs->command       (command to execute for completion list)
+       $1 == command name      (command being completed)
+       $2 = word to be completed (possibly null)
+       $3 = previous word
+   and run in with command substitution.  Parse the results, one word
+   per line, with backslashes allowed to escape newlines.  Build a
+   STRINGLIST from the results and return it. */
+
+static STRINGLIST *
+gen_command_matches (cs, cmd, text, line, ind, lwords, nw, cw)
+     COMPSPEC *cs;
+     const char *cmd;
+     const char *text;
+     char *line;
+     int ind;
+     WORD_LIST *lwords;
+     int nw, cw;
+{
+  char *csbuf, *cscmd, *t;
+  int cmdlen, cmdsize, n, ws, we;
+  WORD_LIST *cmdlist, *cl;
+  WORD_DESC *tw;
+  STRINGLIST *sl;
+
+  bind_compfunc_variables (line, ind, lwords, cw, 1);
+  cmdlist = build_arg_list (cs->command, cmd, text, lwords, cw);
+
+  /* Estimate the size needed for the buffer. */
+  n = strlen (cs->command);
+  cmdsize = n + 1;
+  for (cl = cmdlist->next; cl; cl = cl->next)
+    cmdsize += STRLEN (cl->word->word) + 3;
+  cmdsize += 2;
+
+  /* allocate the string for the command and fill it in. */
+  cscmd = (char *)xmalloc (cmdsize + 1);
+
+  strcpy (cscmd, cs->command);                 /* $0 */
+  cmdlen = n;
+  cscmd[cmdlen++] = ' ';
+  for (cl = cmdlist->next; cl; cl = cl->next)   /* $1, $2, $3, ... */
+    {
+      t = sh_single_quote (cl->word->word ? cl->word->word : "");
+      n = strlen (t);
+      RESIZE_MALLOCED_BUFFER (cscmd, cmdlen, n + 2, cmdsize, 64);
+      strcpy (cscmd + cmdlen, t);
+      cmdlen += n;
+      if (cl->next)
+       cscmd[cmdlen++] = ' ';
+      free (t);
+    }
+  cscmd[cmdlen] = '\0';
+
+  tw = command_substitute (cscmd, 0);
+  csbuf = tw ? tw->word : (char *)NULL;
+  if (tw)
+    dispose_word_desc (tw);
+
+  /* Now clean up and destroy everything. */
+  dispose_words (cmdlist);
+  free (cscmd);
+  unbind_compfunc_variables (1);
+
+  if (csbuf == 0 || *csbuf == '\0')
+    {
+      FREE (csbuf);
+      return ((STRINGLIST *)NULL);
+    }
+
+  /* Now break CSBUF up at newlines, with backslash allowed to escape a
+     newline, and put the individual words into a STRINGLIST. */
+  sl = strlist_create (16);
+  for (ws = 0; csbuf[ws]; )
+    {
+      we = ws;
+      while (csbuf[we] && csbuf[we] != '\n')
+       {
+         if (csbuf[we] == '\\' && csbuf[we+1] == '\n')
+           we++;
+         we++;
+       }
+      t = substring (csbuf, ws, we);
+      if (sl->list_len >= sl->list_size - 1)
+       strlist_resize (sl, sl->list_size + 16);
+      sl->list[sl->list_len++] = t;
+      while (csbuf[we] == '\n') we++;
+      ws = we;
+    }
+  sl->list[sl->list_len] = (char *)NULL;
+
+  free (csbuf);
+  return (sl);
+}
+
+static WORD_LIST *
+command_line_to_word_list (line, llen, sentinel, nwp, cwp)
+     char *line;
+     int llen, sentinel, *nwp, *cwp;
+{
+  WORD_LIST *ret;
+  char *delims;
+
+#if 0
+  delims = "()<>;&| \t\n";     /* shell metacharacters break words */
+#else
+  delims = rl_completer_word_break_characters;
+#endif
+  ret = split_at_delims (line, llen, delims, sentinel, SD_NOQUOTEDELIM, nwp, cwp);
+  return (ret);
+}
+
+/* Evaluate COMPSPEC *cs and return all matches for WORD. */
+
+STRINGLIST *
+gen_compspec_completions (cs, cmd, word, start, end, foundp)
+     COMPSPEC *cs;
+     const char *cmd;
+     const char *word;
+     int start, end;
+     int *foundp;
+{
+  STRINGLIST *ret, *tmatches;
+  char *line;
+  int llen, nw, cw, found, foundf;
+  WORD_LIST *lwords;
+  WORD_DESC *lw;
+  COMPSPEC *tcs;
+
+  found = 1;
+
+#ifdef DEBUG
+  debug_printf ("gen_compspec_completions (%s, %s, %d, %d)", cmd, word, start, end);
+  debug_printf ("gen_compspec_completions: %s -> %p", cmd, cs);
+#endif
+  ret = gen_action_completions (cs, word);
+#ifdef DEBUG
+  if (ret && progcomp_debug)
+    {
+      debug_printf ("gen_action_completions (%p, %s) -->", cs, word);
+      strlist_print (ret, "\t");
+      rl_on_new_line ();
+    }
+#endif
+
+  /* Now we start generating completions based on the other members of CS. */
+  if (cs->globpat)
+    {
+      tmatches = gen_globpat_matches (cs, word);
+      if (tmatches)
+       {
+#ifdef DEBUG
+         if (progcomp_debug)
+           {
+             debug_printf ("gen_globpat_matches (%p, %s) -->", cs, word);
+             strlist_print (tmatches, "\t");
+             rl_on_new_line ();
+           }
+#endif
+         ret = strlist_append (ret, tmatches);
+         strlist_dispose (tmatches);
+         rl_filename_completion_desired = 1;
+       }
+    }
+
+  if (cs->words)
+    {
+      tmatches = gen_wordlist_matches (cs, word);
+      if (tmatches)
+       {
+#ifdef DEBUG
+         if (progcomp_debug)
+           {
+             debug_printf ("gen_wordlist_matches (%p, %s) -->", cs, word);
+             strlist_print (tmatches, "\t");
+             rl_on_new_line ();
+           }
+#endif
+         ret = strlist_append (ret, tmatches);
+         strlist_dispose (tmatches);
+       }
+    }
+
+  lwords = (WORD_LIST *)NULL;
+  line = (char *)NULL;
+  if (cs->command || cs->funcname)
+    {
+      /* If we have a command or function to execute, we need to first break
+        the command line into individual words, find the number of words,
+        and find the word in the list containing the word to be completed. */
+      line = substring (rl_line_buffer, start, end);
+      llen = end - start;
+
+#ifdef DEBUG
+      debug_printf ("command_line_to_word_list (%s, %d, %d, %p, %p)",
+               line, llen, rl_point - start, &nw, &cw);
+#endif
+      lwords = command_line_to_word_list (line, llen, rl_point - start, &nw, &cw);
+      /* If we skipped a NULL word at the beginning of the line, add it back */
+      if (lwords && lwords->word && cmd[0] == 0 && lwords->word->word[0] != 0)
+       {
+         lw = make_bare_word (cmd);
+         lwords = make_word_list (lw, lwords);
+         nw++;
+         cw++;
+       }
+#ifdef DEBUG
+      if (lwords == 0 && llen > 0)
+       debug_printf ("ERROR: command_line_to_word_list returns NULL");
+      else if (progcomp_debug)
+       {
+         debug_printf ("command_line_to_word_list -->");
+         printf ("\t");
+         print_word_list (lwords, "!");
+         printf ("\n");
+         fflush(stdout);
+         rl_on_new_line ();
+       }
+#endif
+    }
+
+  if (cs->funcname)
+    {
+      foundf = 0;
+      tmatches = gen_shell_function_matches (cs, cmd, word, line, rl_point - start, lwords, nw, cw, &foundf);
+      if (foundf != 0)
+       found = foundf;
+      if (tmatches)
+       {
+#ifdef DEBUG
+         if (progcomp_debug)
+           {
+             debug_printf ("gen_shell_function_matches (%p, %s, %s, %p, %d, %d) -->", cs, cmd, word, lwords, nw, cw);
+             strlist_print (tmatches, "\t");
+             rl_on_new_line ();
+           }
+#endif
+         ret = strlist_append (ret, tmatches);
+         strlist_dispose (tmatches);
+       }
+    }
+
+  if (cs->command)
+    {
+      tmatches = gen_command_matches (cs, cmd, word, line, rl_point - start, lwords, nw, cw);
+      if (tmatches)
+       {
+#ifdef DEBUG
+         if (progcomp_debug)
+           {
+             debug_printf ("gen_command_matches (%p, %s, %s, %p, %d, %d) -->", cs, cmd, word, lwords, nw, cw);
+             strlist_print (tmatches, "\t");
+             rl_on_new_line ();
+           }
+#endif
+         ret = strlist_append (ret, tmatches);
+         strlist_dispose (tmatches);
+       }
+    }
+
+  if (cs->command || cs->funcname)
+    {
+      if (lwords)
+       dispose_words (lwords);
+      FREE (line);
+    }
+
+  if (foundp)
+    *foundp = found;
+
+  if (found == 0 || (found & PCOMP_RETRYFAIL))
+    {
+      strlist_dispose (ret);
+      return NULL;
+    }
+
+  if (cs->filterpat)
+    {
+      tmatches = filter_stringlist (ret, cs->filterpat, word);
+#ifdef DEBUG
+      if (progcomp_debug)
+       {
+         debug_printf ("filter_stringlist (%p, %s, %s) -->", ret, cs->filterpat, word);
+         strlist_print (tmatches, "\t");
+         rl_on_new_line ();
+       }
+#endif
+      if (ret && ret != tmatches)
+       {
+         FREE (ret->list);
+         free (ret);
+       }
+      ret = tmatches;
+    }
+
+  if (cs->prefix || cs->suffix)
+    ret = strlist_prefix_suffix (ret, cs->prefix, cs->suffix);
+
+  /* If no matches have been generated and the user has specified that
+      directory completion should be done as a default, call
+      gen_action_completions again to generate a list of matching directory
+      names. */
+  if ((ret == 0 || ret->list_len == 0) && (cs->options & COPT_DIRNAMES))
+    {
+      tcs = compspec_create ();
+      tcs->actions = CA_DIRECTORY;
+      FREE (ret);
+      ret = gen_action_completions (tcs, word);
+      compspec_dispose (tcs);
+    }
+  else if (cs->options & COPT_PLUSDIRS)
+    {
+      tcs = compspec_create ();
+      tcs->actions = CA_DIRECTORY;
+      tmatches = gen_action_completions (tcs, word);
+      ret = strlist_append (ret, tmatches);
+      strlist_dispose (tmatches);
+      compspec_dispose (tcs);
+    }
+
+  return (ret);
+}
+
+void
+pcomp_set_readline_variables (flags, nval)
+     int flags, nval;
+{
+  /* If the user specified that the compspec returns filenames, make
+     sure that readline knows it. */
+  if (flags & COPT_FILENAMES)
+    rl_filename_completion_desired = nval;
+  /* If the user doesn't want a space appended, tell readline. */
+  if (flags & COPT_NOSPACE)
+    rl_completion_suppress_append = nval;
+  /* The value here is inverted, since the default is on and the `noquote'
+     option is supposed to turn it off */
+  if (flags & COPT_NOQUOTE)
+    rl_filename_quoting_desired = 1 - nval;
+}
+
+/* Set or unset FLAGS in the options word of the current compspec.
+   SET_OR_UNSET is 1 for setting, 0 for unsetting. */
+void
+pcomp_set_compspec_options (cs, flags, set_or_unset)
+     COMPSPEC *cs;
+     int flags, set_or_unset;
+{
+  if (cs == 0 && ((cs = pcomp_curcs) == 0))
+    return;
+  if (set_or_unset)
+    cs->options |= flags;
+  else
+    cs->options &= ~flags;
+}
+
+static STRINGLIST *
+gen_progcomp_completions (ocmd, cmd, word, start, end, foundp, retryp, lastcs)
+     const char *ocmd;
+     const char *cmd;
+     const char *word;
+     int start, end;
+     int *foundp, *retryp;
+     COMPSPEC **lastcs;
+{
+  COMPSPEC *cs, *oldcs;
+  const char *oldcmd;
+  STRINGLIST *ret;
+
+  cs = progcomp_search (ocmd);
+
+  if (cs == 0 || cs == *lastcs)
+    {
+#if 0
+      if (foundp)
+       *foundp = 0;
+#endif
+      return (NULL);
+    }
+
+  if (*lastcs)
+    compspec_dispose (*lastcs);
+  cs->refcount++;      /* XXX */
+  *lastcs = cs;
+
+  cs = compspec_copy (cs);
+
+  oldcs = pcomp_curcs;
+  oldcmd = pcomp_curcmd;
+
+  pcomp_curcs = cs;
+  pcomp_curcmd = cmd;
+
+  ret = gen_compspec_completions (cs, cmd, word, start, end, foundp);
+
+  pcomp_curcs = oldcs;
+  pcomp_curcmd = oldcmd;
+
+  /* We need to conditionally handle setting *retryp here */
+  if (retryp)
+    *retryp = foundp && (*foundp & PCOMP_RETRYFAIL);           
+
+  if (foundp)
+    {
+      *foundp &= ~PCOMP_RETRYFAIL;
+      *foundp |= cs->options;
+    }
+
+  compspec_dispose (cs);
+  return ret;  
+}
+
+/* The driver function for the programmable completion code.  Returns a list
+   of matches for WORD, which is an argument to command CMD.  START and END
+   bound the command currently being completed in rl_line_buffer. */
+char **
+programmable_completions (cmd, word, start, end, foundp)
+     const char *cmd;
+     const char *word;
+     int start, end, *foundp;
+{
+  COMPSPEC *cs, *lastcs;
+  STRINGLIST *ret;
+  char **rmatches, *t;
+  int found, retry, count;
+
+  lastcs = 0;
+  found = count = 0;
+
+  do
+    {
+      retry = 0;
+
+      /* We look at the basename of CMD if the full command does not have
+        an associated COMPSPEC. */
+      ret = gen_progcomp_completions (cmd, cmd, word, start, end, &found, &retry, &lastcs);
+      if (found == 0)
+       {
+         t = strrchr (cmd, '/');
+         if (t && *(++t))
+           ret = gen_progcomp_completions (t, cmd, word, start, end, &found, &retry, &lastcs);
+       }
+
+      if (found == 0)
+       ret = gen_progcomp_completions (DEFAULTCMD, cmd, word, start, end, &found, &retry, &lastcs);
+
+      count++;
+
+      if (count > 32)
+       {
+         internal_warning ("programmable_completion: %s: possible retry loop", cmd);
+         break;
+       }
+    }
+  while (retry);
+
+  if (ret)
+    {
+      rmatches = ret->list;
+      free (ret);
+    }
+  else
+    rmatches = (char **)NULL;
+
+  if (foundp)
+    *foundp = found;
+
+  if (lastcs)  /* XXX - should be while? */
+    compspec_dispose (lastcs);
+
+  return (rmatches);
+}
+
+#endif /* PROGRAMMABLE_COMPLETION */
index 72ec06a2c1fd8dde92acea5e8ac773e35f1d061b..3efcf32d68e9722024b6ca9d67f9e81b2aa5ac04 100755 (executable)
@@ -1,4 +1,4 @@
-BUILD_DIR=/usr/local/build/bash/bash-current
+BUILD_DIR=/usr/local/build/chet/bash/bash-current
 THIS_SH=$BUILD_DIR/bash
 PATH=$PATH:$BUILD_DIR