The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
diff -ruN sash-3.6/Makefile sash-3.6.readline/Makefile
--- sash-3.6/Makefile	2003-10-17 14:25:00.306848712 +0200
+++ sash-3.6.readline/Makefile	2003-10-17 14:27:34.529403320 +0200
@@ -19,10 +19,11 @@
 	-DHAVE_LINUX_ATTR=$(HAVE_LINUX_ATTR) \
 	-DHAVE_LINUX_MOUNT=$(HAVE_LINUX_MOUNT) \
 	-DHAVE_BSD_MOUNT=$(HAVE_BSD_MOUNT) \
-	-DMOUNT_TYPE=$(MOUNT_TYPE)
+	-DMOUNT_TYPE=$(MOUNT_TYPE) \
+	-DHAVE_READLINE
 
 LDFLAGS = --disable-shared -static -s
-LIBS = -lz
+LIBS = -lz -lreadline -lncurses
 
 
 BINDIR = /bin
diff -ruN sash-3.6/sash.c sash-3.6.readline/sash.c
--- sash-3.6/sash.c	2002-07-22 00:55:28.000000000 +0200
+++ sash-3.6.readline/sash.c	2003-10-17 14:26:26.184793280 +0200
@@ -384,6 +384,201 @@
 static	void	usage(void);
 static	Alias *	findAlias(const char * name);
 
+#ifdef HAVE_READLINE
+/*--- Interface of readline 4.0 by vlk@vinf.ru ---*/                    
+
+#include <readline/readline.h>
+#include <readline/history.h>
+#include <dirent.h>
+
+char * dupstr (char *s);
+void initialize_readline (void);
+char ** fileman_completion (char *text, int start, int end);
+char *command_generator (char *text, int state);
+void err_alloc(int ErrorNumber);
+extern char *xmalloc ();
+
+static char **	listBase;
+static long  	listSize;
+
+void 
+err_alloc(int ErrorNumber)
+{
+ fprintf(stderr, "Memory space failed: %d\n",ErrorNumber);
+ return;    
+}
+
+/* Add a one item to exec-command array */
+static int
+listAdd(const char * new_item)
+{
+ if ((listBase = realloc(listBase, sizeof(char *) * ( listSize + 1 ))) == 0 )
+    {
+      err_alloc(3); exit(1);
+    }
+ listBase[listSize] = strdup(new_item);
+ listSize++;
+ return(0);
+}
+
+static int
+genExecList(void)
+{
+	char *		dirName;
+	char *		path;
+	char *		endPath;
+	char *		fullName;
+	struct dirent **namelist;
+	int n;
+
+	if (getenv("PATH") == 0)
+	    return(1);
+	else
+	   {
+	     if ((path = strdup(getenv("PATH"))) == 0)
+	     {
+		err_alloc(4);
+		return(1);
+	     } 
+	   }    
+	/*
+	 * Check out each path to see if the program exists and is
+	 * executable in that path.
+	 */
+	for ( ; path; path = endPath)
+	{
+		/*
+		 * Find the end of the next path and NULL terminate
+		 * it if necessary.
+		 */
+		endPath = strchr(path, ':');
+
+		if (endPath)
+			*endPath++ = '\0';
+
+		/*
+		 * Get the directory name, defaulting it to DOT if
+		 * it is null.
+		 */
+		dirName = path;
+
+		/*
+		 * See if the program exists and is executable.
+		 */
+		if (access(dirName, F_OK) < 0)
+		{
+			if (errno != ENOENT)
+				printf("%s: %s\n", dirName, strerror(errno));
+
+			continue;
+		}
+		n = scandir(dirName, &namelist, 0, alphasort);
+		if (n < 0)
+                    perror("scandir");
+                else
+                    while(n--)
+			 {  
+			   if (strncmp(namelist[n]->d_name,".",1) != 0)
+			      {       
+			       fullName = malloc(sizeof(dirName) + 1 \
+			  		     + sizeof(namelist[n]->d_name));
+    			       strcpy(fullName, dirName);
+			       strcat(fullName, "/");
+			       strcat(fullName, namelist[n]->d_name);
+			       if (access(fullName,X_OK|F_OK) == 0)
+			    	   listAdd(namelist[n]->d_name);
+			      }	    
+			  }
+	}
+return(0);
+}
+
+/******************************************************************/
+/* BEGIN            Interface to Readline Completion              */
+/******************************************************************/
+
+char * dupstr (char *s)
+{
+  char *r;
+
+  r = xmalloc (strlen (s) + 1);
+  strcpy (r, s);
+  return (r);
+}
+
+/* Tell the GNU Readline library how to complete.  We want to try to complete
+   on command names if this is the first word in the line, or on filenames
+   if not. */
+void initialize_readline (void)
+{
+  /* Allow conditional parsing of the ~/.inputrc file. */
+  rl_readline_name = "readline";
+
+  /* Tell the completer that we want a crack first. */
+  rl_attempted_completion_function = (CPPFunction *)fileman_completion;
+}
+
+/* Attempt to complete on the contents of TEXT.  START and END bound the
+   region of rl_line_buffer that contains the word to complete.  TEXT is
+   the word to complete.  We can use the entire contents of rl_line_buffer
+   in case we want to do some simple parsing.  Return the array of matches,
+   or NULL if there aren't any. */
+char ** fileman_completion (char *text, int start, int end)
+{
+  char **matches;
+
+  matches = (char **)NULL;
+
+  /* If this word is at the start of the line, then it is a command
+     to complete.  Otherwise it is the name of a file in the current
+     directory. */
+  if (start == 0) // is it the begin of string ?
+    matches = completion_matches (text, command_generator);
+  return (matches);
+}
+
+/* Generator function for command completion.  STATE lets us know whether
+   to start from scratch; without any state (i.e. STATE == 0), then we
+   start at the top of the list. */
+char * command_generator (char *text, int state)
+{
+  static int list_index, len;
+  char *ename;
+  static long exec_count;
+      
+  /* If this is a new word to complete, initialize now.  This includes
+     saving the length of TEXT for efficiency, and initializing the index
+     variable to 0. */
+  if (!state)
+    {
+      exec_count = 0;	
+      list_index = 0;
+      len = strlen (text);
+    }
+
+  /* Return the next name which partially matches from the command list. */
+
+  while (ename = (char*)commandEntryTable[list_index].name)
+    {
+      list_index++;
+      if (strncmp (ename, text, len) == 0)
+        return (dupstr(ename));
+    }
+    
+  while (exec_count < listSize)
+     {
+      ename = listBase[exec_count];
+      exec_count++;
+      if (strncmp (ename, text, len) == 0)
+        return(dupstr(ename));
+     }
+  /* If no names matched, then return NULL. */
+  return ((char *)NULL);
+}
+/******************************************************************/
+/* END              Interface to Readline Completion              */
+/******************************************************************/
+#endif
 
 /*
  * Global interrupt flag.
@@ -405,7 +600,16 @@
 	commandFile = NULL;
 	quietFlag = FALSE;
 	aliasFlag = FALSE;
+#ifdef HAVE_READLINE
+	/*
+	 * Default our path if it is not set.
+	 */
+	if (getenv("PATH") == NULL)
+		putenv("PATH=/bin:/usr/bin:/sbin:/usr/sbin");
 
+	genExecList();
+	initialize_readline ();	/* Bind our completer. */
+#endif
 	/*
 	 * Look for options.
 	 */
@@ -554,6 +758,9 @@
 static void
 readFile(const char * name)
 {
+#ifdef HAVE_READLINE
+	char *input_line = NULL;
+#endif
 	FILE *	fp;
 	int	cc;
 	BOOL	ttyFlag;
@@ -586,9 +793,10 @@
 
 	while (TRUE)
 	{
-		if (ttyFlag)
+#ifndef HAVE_READLINE
+ 		if (ttyFlag)
 			showPrompt();
-
+#endif
 		if (intFlag && !ttyFlag && (fp != stdin))
 		{
 			fclose(fp);
@@ -805,17 +805,34 @@
 			return;
 		}
 	
-		if (fgets(buf, CMD_LEN - 1, fp) == NULL)
+#ifdef HAVE_READLINE
+		if (name != NULL)
 		{
-			if (ferror(fp) && (errno == EINTR))
+#endif
+			if (fgets(buf, CMD_LEN - 1, fp) == NULL)
 			{
-				clearerr(fp);
+				if (ferror(fp) && (errno == EINTR))
+				{
+					clearerr(fp);
+					continue;
+				}
 
-				continue;
+				break;
 			}
-
-			break;
+#ifdef HAVE_READLINE
 		}
+		if (name == NULL)
+		   {
+		       input_line = readline(prompt ? prompt : "$ ");
+		       if (input_line)
+		           { 
+		             if (*input_line)
+                                 add_history(input_line);
+		             strcpy(buf,input_line);
+			   }
+		       free(input_line);
+		    }   
+#endif
 
 		cc = strlen(buf);