about summary refs log tree commit diff
path: root/Src/prompt.c
diff options
context:
space:
mode:
Diffstat (limited to 'Src/prompt.c')
-rw-r--r--Src/prompt.c256
1 files changed, 153 insertions, 103 deletions
diff --git a/Src/prompt.c b/Src/prompt.c
index 8c9216f95..69823a5e3 100644
--- a/Src/prompt.c
+++ b/Src/prompt.c
@@ -71,6 +71,10 @@ static int bufspc;
 
 static char *bp;
 
+/* Position of the start of the current line in the buffer */
+
+static char *bufline;
+
 /* bp1 is an auxilliary pointer into the buffer, which when non-NULL is *
  * moved whenever the buffer is reallocated.  It is used when data is   *
  * being temporarily held in the buffer.                                */
@@ -81,11 +85,9 @@ static char *bp1;
 
 static char *fm;
 
-/* Current truncation string (metafied), the length at which truncation *
- * occurs, and the direction in which it occurs.                        */
+/* Non-zero if truncating the current segment of the buffer. */
 
-static char *truncstr;
-static int trunclen, truncatleft;
+static int trunclen;
 
 /* Current level of nesting of %{ / %} sequences. */
 
@@ -95,10 +97,6 @@ static int dontcount;
 
 static char *rstring, *Rstring;
 
-/* If non-zero, Inpar, Outpar and Nularg can be added to the buffer. */
-
-static int nonsp;
-
 /* Perform prompt expansion on a string, putting the result in a *
  * permanently-allocated string.  If ns is non-zero, this string *
  * may have embedded Inpar and Outpar, which indicate a toggling *
@@ -130,9 +128,8 @@ promptexpand(char *s, int ns, char *rs, char *Rs)
 
     rstring = rs;
     Rstring = Rs;
-    nonsp = ns;
     fm = s;
-    bp = buf = zalloc(bufspc = 256);
+    bp = bufline = buf = zalloc(bufspc = 256);
     bp1 = NULL;
     trunclen = 0;
     putpromptchar(1, '\0');
@@ -140,6 +137,15 @@ promptexpand(char *s, int ns, char *rs, char *Rs)
     if(dontcount)
 	*bp++ = Outpar;
     *bp = 0;
+    if (!ns) {
+	/* If zero, Inpar, Outpar and Nularg should be removed. */
+	for (bp = buf; *bp; bp++) {
+	    if (*bp == Meta)
+		bp++;
+	    else if (*bp == Inpar || *bp == Outpar || *bp == Nularg)
+		chuck(bp);
+	}
+    }
     return buf;
 }
 
@@ -164,7 +170,7 @@ putpromptchar(int doprint, int endchar)
 		arg = zstrtol(fm, &fm, 10);
 	    }
 	    if (*fm == '(') {
-		int tc;
+		int tc, otrunclen;
 
 		if (idigit(*++fm)) {
 		    arg = zstrtol(fm, &fm, 10);
@@ -224,6 +230,12 @@ putpromptchar(int doprint, int endchar)
 		    if (getegid() == arg)
 			test = 1;
 		    break;
+		case 'l':
+		    *bp = '\0';
+		    countprompt(bufline, &t0, 0);
+		    if (t0 >= arg)
+			test = 1;
+		    break;
 		case 'L':
 		    if (shlvl >= arg)
 			test = 1;
@@ -249,10 +261,15 @@ putpromptchar(int doprint, int endchar)
 		if (!*fm || !(sep = *++fm))
 		    return 0;
 		fm++;
+		/* Don't do the current truncation until we get back */
+		otrunclen = trunclen;
+		trunclen = 0;
 		if (!putpromptchar(test == 1 && doprint, sep) || !*++fm ||
 		    !putpromptchar(test == 0 && doprint, ')')) {
+		    trunclen = otrunclen;
 		    return 0;
 		}
+		trunclen = otrunclen;
 		continue;
 	    }
 	    if (!doprint)
@@ -377,72 +394,24 @@ putpromptchar(int doprint, int endchar)
 		tsetcap(TCUNDERLINEEND, 1);
 		break;
 	    case '[':
-                if (idigit(*++fm))
-                    trunclen = zstrtol(fm, &fm, 10);
-                else
-                    trunclen = arg;
-                if (trunclen) {
-		    truncatleft = *fm && *fm != ']' && *fm++ == '<';
-		    bp1 = bp;
-		    while (*fm && *fm != ']') {
-			if (*fm == '\\' && fm[1])
-			    ++fm;
-			addbufspc(1);
-			*bp++ = *fm++;
-		    }
-		    addbufspc(2);
-		    if (bp1 == bp)
-			*bp++ = '<';
-                    *bp = '\0';
-		    zsfree(truncstr);
-                    truncstr = ztrdup(bp = bp1);
-		    bp1 = NULL;
-                } else {
-		    while (*fm && *fm != ']') {
-			if (*fm == '\\' && fm[1])
-			    fm++;
-			fm++;
-		    }
-		}
-		if(!*fm)
-		    return 0;
+		if (idigit(*++fm))
+		    arg = zstrtol(fm, &fm, 10);
+		if (!prompttrunc(arg, ']', doprint, endchar))
+		    return *fm;
 		break;
 	    case '<':
 	    case '>':
-		if((trunclen = arg)) {
-		    char ch = *fm++;
-		    truncatleft = ch == '<';
-		    bp1 = bp;
-		    while (*fm && *fm != ch) {
-			if (*fm == '\\' && fm[1])
-			    ++fm;
-			addbufspc(1);
-			*bp++ = *fm++;
-		    }
-		    addbufspc(1);
-                    *bp = '\0';
-		    zsfree(truncstr);
-                    truncstr = ztrdup(bp = bp1);
-		    bp1 = NULL;
-		} else {
-		    char ch = *fm++;
-		    while(*fm && *fm != ch) {
-			if (*fm == '\\' && fm[1])
-			    fm++;
-			fm++;
-		    }
-		}
-		if(!*fm)
-		    return 0;
+		if (!prompttrunc(arg, *fm, doprint, endchar))
+		    return *fm;
 		break;
 	    case '{': /*}*/
-		if (!dontcount++ && nonsp) {
+		if (!dontcount++) {
 		    addbufspc(1);
 		    *bp++ = Inpar;
 		}
 		break;
 	    case /*{*/ '}':
-		if (dontcount && !--dontcount && nonsp) {
+		if (dontcount && !--dontcount) {
 		    addbufspc(1);
 		    *bp++ = Outpar;
 		}
@@ -569,7 +538,7 @@ putpromptchar(int doprint, int endchar)
 		break;
 	    }
 	} else if(*fm == '!' && isset(PROMPTBANG)) {
-	    if(doprint)
+	    if(doprint) {
 		if(fm[1] == '!') {
 		    fm++;
 		    addbufspc(1);
@@ -579,6 +548,7 @@ putpromptchar(int doprint, int endchar)
 		    sprintf(bp, "%d", curhist);
 		    bp += strlen(bp);
 		}
+	    }
 	} else {
 	    char c = *fm == Meta ? *++fm ^ 32 : *fm;
 
@@ -604,6 +574,8 @@ pputc(char c)
 	c ^= 32;
     }
     *bp++ = c;
+    if (c == '\n' && !dontcount)
+	bufline = bp;
 }
 
 /* Make sure there is room for `need' more characters in the buffer. */
@@ -627,46 +599,19 @@ addbufspc(int need)
 }
 
 /* stradd() adds a metafied string to the prompt, *
- * in a visible representation, doing truncation. */
+ * in a visible representation.                   */
 
 /**/
 void
 stradd(char *d)
 {
-    /* dlen is the full length of the string we want to add */
-    int dlen = niceztrlen(d);
-    char *ps, *pd, *pc, *t;
-    int tlen, maxlen;
-    addbufspc(dlen);
+    char *ps, *pc;
+    addbufspc(niceztrlen(d));
     /* This loop puts the nice representation of the string into the prompt *
-     * buffer.  It might be modified later.  Note that bp isn't changed.    */
-    for(ps=d, pd=bp; *ps; ps++)
+     * buffer.                                                              */
+    for(ps=d; *ps; ps++)
 	for(pc=nicechar(*ps == Meta ? STOUC(*++ps)^32 : STOUC(*ps)); *pc; pc++)
-	    *pd++ = *pc;
-    if(!trunclen || dlen <= trunclen) {
-	/* No truncation is needed, so update bp and return, *
-	 * leaving the full string in the prompt.            */
-	bp += dlen;
-	return;
-    }
-    /* We need to truncate.  t points to the truncation string -- which is *
-     * inserted literally, without nice representation.  tlen is its       *
-     * length, and maxlen is the amout of the main string that we want to  *
-     * keep.  Note that if the truncation string is longer than the        *
-     * truncation length (tlen > trunclen), the truncation string is used  *
-     * in full.                                                            */
-    addbufspc(tlen = ztrlen(t = truncstr));
-    maxlen = tlen < trunclen ? trunclen - tlen : 0;
-    if(truncatleft) {
-	memmove(bp + strlen(t), bp + dlen - maxlen, maxlen);
-	while(*t)
-	    *bp++ = *t++;
-	bp += maxlen;
-    } else {
-	bp += maxlen;
-	while(*t)
-	    *bp++ = *t++;
-    }
+	    *bp++ = *pc;
 }
 
 /* tsetcap(), among other things, can write a termcap string into the buffer. */
@@ -684,12 +629,12 @@ tsetcap(int cap, int flag)
 	    tputs(tcstr[cap], 1, putshout);
 	    break;
 	case 1:
-	    if (!dontcount && nonsp) {
+	    if (!dontcount) {
 		addbufspc(1);
 		*bp++ = Inpar;
 	    }
 	    tputs(tcstr[cap], 1, putstr);
-	    if (!dontcount && nonsp) {
+	    if (!dontcount) {
 		int glitch = 0;
 
 		if (cap == TCSTANDOUTBEG || cap == TCSTANDOUTEND)
@@ -764,3 +709,108 @@ countprompt(char *str, int *wp, int *hp)
     if(hp)
 	*hp = h;
 }
+
+/**/
+static int
+prompttrunc(int arg, int truncchar, int doprint, int endchar)
+{
+    if (arg) {
+	char ch = *fm, *ptr = bp, *truncstr;
+	int truncatleft = ch == '<';
+
+	/*
+	 * If there is already a truncation active, return so that
+	 * can be finished, backing up so that the new truncation
+	 * can be started afterwards.
+	 */
+	if (trunclen) {
+	    while (*--fm != '%')
+		;
+	    fm--;
+	    return 0;
+	}
+
+	trunclen = arg;
+	if (*fm != ']')
+	    fm++;
+	while (*fm && *fm != truncchar) {
+	    if (*fm == '\\' && fm[1])
+		++fm;
+	    addbufspc(1);
+	    *bp++ = *fm++;
+	}
+	if (!*fm)
+	    return 0;
+	if (bp == ptr && truncchar == ']') {
+	    addbufspc(1);
+	    *bp++ = '<';
+	}
+	truncstr = ztrduppfx(ptr, bp - ptr);
+
+	bp = ptr;
+	fm++;
+	putpromptchar(doprint, endchar);
+	*bp = '\0';
+	if (bp - ptr > trunclen) {
+	    /*
+	     * We need to truncate.  t points to the truncation string -- *
+	     * which is inserted literally, without nice representation.  *
+	     * tlen is its length, and maxlen is the amount of the main	  *
+	     * string that we want to keep.  Note that if the truncation  *
+	     * string is longer than the truncation length (tlen >	  *
+	     * trunclen), the truncation string is used in full.	  *
+	     */
+	    char *t = truncstr;
+	    int fullen = bp - ptr;
+	    int tlen = ztrlen(t), maxlen;
+	    if (tlen > fullen) {
+		addbufspc(tlen - fullen);
+		bp += tlen - fullen;
+	    } else
+		bp -= fullen - trunclen;
+	    maxlen = tlen < trunclen ? trunclen - tlen : 0;
+	    if (truncatleft) {
+		if (maxlen)
+		    memmove(ptr + strlen(t), ptr + fullen - maxlen,
+			    maxlen);
+		while (*t)
+		    *ptr++ = *t++;
+	    } else {
+		ptr += maxlen;
+		while (*t)
+		    *ptr++ = *t++;
+	    }
+	}
+	zsfree(truncstr);
+	trunclen = 0;
+	/*
+	 * We may have returned early from the previous putpromptchar *
+	 * because we found another truncation following this one.    *
+	 * In that case we need to do the rest now.                   *
+	 */
+	if (!*fm)
+	    return 0;
+	if (*fm != endchar) {
+	    fm++;
+	    /*
+	     * With trunclen set to zero, we always reach endchar *
+	     * (or the terminating NULL) this time round.         *
+	     */
+	    if (!putpromptchar(doprint, endchar))
+		return 0;
+	    /* Now we have to trick it into matching endchar again */
+	    fm--;
+	}
+    } else {
+	if (*fm != ']')
+	    fm++;
+	while(*fm && *fm != truncchar) {
+	    if (*fm == '\\' && fm[1])
+		fm++;
+	    fm++;
+	}
+	if (trunclen || !*fm)
+	    return 0;
+    }
+    return 1;
+}