Subject: Re: pkg/16226 (new patch for review)
To: None <tech-userlevel@netbsd.org>
From: Julio Merino <jmmv@menta.net>
List: tech-userlevel
Date: 01/18/2003 11:51:46
Hello,

here is a new patch to do the prompt expansion thing I submitted some time
ago. It seems better to me (now memory is only allocated once, and realloc's
are avoided when possible).

The problem I see in it is that the memory allocated for the exp string is
never free'd. Were could it be done? At sh's exit?

Anyway here is the patch, what do you think?

Index: parser.c
===================================================================
RCS file: /cvsroot/src/bin/sh/parser.c,v
retrieving revision 1.54
diff -u -u -r1.54 parser.c
--- parser.c	2002/11/24 22:35:42	1.54
+++ parser.c	2003/01/18 10:38:58
@@ -46,6 +46,7 @@
 #endif /* not lint */
 
 #include <stdlib.h>
+#include <unistd.h>
 
 #include "shell.h"
 #include "parser.h"
@@ -1608,21 +1609,89 @@
 		out2str(getprompt(NULL));
 }
 
+/* used by getprompt */
+#define STR_REALLOC(str,actual,add) \
+	if (actual < strlen(str) + add) { \
+		str = (char *) realloc(str, actual + add); \
+		actual += add; \
+	}
+
 /*
  * called by editline -- any expansions to the prompt
  *    should be added here.
  */
 const char *
 getprompt(void *unused)
-	{
+{
+	const char *unexp;			/* Unexpanded path */
+	static char *exp = NULL;		/* Expanded path */
+	static int alloclen = 0;		/* Allocated memory in exp */
+	unsigned long pos = 0;			/* Current position in exp */
+	int err;
+	char hostname[MAXHOSTNAMELEN + 1];
+	char *cwd;
+
+	/* Set unexp to the requested prompt if it requires expansion,
+	 * otherwise return its value directly. */
 	switch (whichprompt) {
 	case 0:
 		return "";
 	case 1:
-		return ps1val();
+		unexp = ps1val();
+		break;
 	case 2:
-		return ps2val();
+		unexp = ps2val();
+		break;
 	default:
 		return "<internal prompt error>";
 	}
+
+	if (alloclen == 0) {
+		alloclen = strlen(unexp) + 1;
+		exp = (char *) malloc(alloclen);
+	}
+	*exp = '\0';
+
+	while (*unexp != '\0') {
+		err = 0;
+
+		if (*unexp == '\\') {
+			unexp++;	/* Skip backslash */
+			switch (*unexp) {
+			case 'm':	/* Short hostname */
+				gethostname(hostname, MAXHOSTNAMELEN);
+				STR_REALLOC(exp, alloclen, strlen(hostname)+1);
+				strcat(exp, hostname);
+				pos += strlen(hostname);
+				break;
+			case 'w':	/* Current working directory */
+				cwd = getcwd(NULL, 0);
+				if (cwd == NULL) {
+					err = 1;
+					cwd = "<cwd disappeared>";
+				}
+				STR_REALLOC(exp, alloclen, strlen(cwd)+1);
+				strcat(exp, cwd);
+				pos += strlen(cwd);
+				if (!err)
+					free(cwd);
+				break;
+			case '#':	/* # for root, $ for user */
+				if (geteuid())
+					exp[pos++] = '$';
+				else
+					exp[pos++] = '#';
+				break;
+			default:
+				exp[pos++] = *unexp;
+				break;
+			}
+		} else
+			exp[pos++] = *unexp;
+
+		unexp++;
+		exp[pos] = '\0';
+	}
+
+	return exp;
 }
Index: sh.1
===================================================================
RCS file: /cvsroot/src/bin/sh/sh.1,v
retrieving revision 1.55
diff -u -u -r1.55 sh.1
--- sh.1	2002/12/28 05:08:27	1.55
+++ sh.1	2003/01/18 10:38:58
@@ -1031,6 +1031,30 @@
 patterns used for both Pathname Expansion and the
 .Ic case
 command.
+.Ss Prompt expansion
+Prompt variables,
+.Va PS1
+and
+.Va PS2 ,
+are subject to prompt expansion each time they are shown.
+The following sequences are recognized, and can appear any number of
+times inside prompt variables:
+.Bl -tag -width indent
+.It \em
+Machine's hostname, up to the first dot.
+.It \ew
+Full path of the current working directory.
+.It \e#
+A
+.Ql $
+sign if the shell is running as a regular user, or a
+.Ql #
+sign if running as root.
+.El
+.Pp
+Note that backslashes must be quoted with another backslash, like
+.Ql \e\e ,
+in order to avoid parsing of escape sequences.
 .Ss Shell Patterns
 A pattern consists of normal characters, which match themselves,
 and meta-characters.


Thanks

-- 
Julio M. Merino Vidal <jmmv@menta.net>
The NetBSD Project - http://www.NetBSD.org/