about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog6
-rw-r--r--Doc/Zsh/prompt.yo20
-rw-r--r--Src/prompt.c493
3 files changed, 348 insertions, 171 deletions
diff --git a/ChangeLog b/ChangeLog
index d0a119adb..00dd78a7f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2000-07-13  Peter Stephenson  <pws@cambridgesiliconradio.com>
+
+	* 12242: Fr. Br. George (George V Kouryachy) and pws:
+	Doc/Zsh/prompt.yo, Src/prompt.c: negative integers in prompt
+	escapes count from the other end of the appropriate string.
+
 2000-07-13  Sven Wischnowsky  <wischnow@zsh.org>
 
 	* 12241: Completion/Core/_main_complete, Src/Zle/computil.c: fix
diff --git a/Doc/Zsh/prompt.yo b/Doc/Zsh/prompt.yo
index 7ba519d3e..e486b29c4 100644
--- a/Doc/Zsh/prompt.yo
+++ b/Doc/Zsh/prompt.yo
@@ -44,7 +44,8 @@ xitem(tt(%d))
 item(tt(%/))(
 Present working directory (tt($PWD)).  If an integer follows the `tt(%)',
 it specifies a number of trailing components of tt($PWD) to show; zero
-means the whole path.
+means the whole path.  A negative integer specifies leading components,
+i.e. tt(%-1d) specifies the first component.
 )
 item(tt(%~))(
 As tt(%d) and tt(%/), but if tt($PWD) has a named directory as its prefix,
@@ -64,7 +65,8 @@ The full machine hostname.
 item(tt(%m))(
 The hostname up to the first `tt(.)'.
 An integer may follow the `tt(%)' to specify
-how many components of the hostname are desired.
+how many components of the hostname are desired.  With a negative integer,
+trailing components of the hostname are shown.
 )
 item(tt(%S) LPAR()tt(%s)RPAR())(
 Start (stop) standout mode.
@@ -93,7 +95,7 @@ The name of the script, sourced file, or shell function that zsh is
 currently executing, whichever was started most recently.  If there is
 none, this is equivalent to the parameter tt($0).  An integer may follow
 the `tt(%)' to specify a number of trailing path components to show; zero
-means the full path.
+means the full path.  A negative integer specifies leading components.
 )
 item(tt(%i))(
 The line number currently being executed in the script, sourced file, or
@@ -126,7 +128,7 @@ The return code of the last command executed just before the prompt.
 item(tt(%_))(
 The status of the parser, i.e. the shell constructs (like `tt(if)' and
 `tt(for)') that have been started on the command line. If given an integer
-number that many strings will be printed; zero or no integer means
+number that many strings will be printed; zero or negative or no integer means
 print as many as there are.  This is most useful in prompts tt(PS2) for
 continuation lines and tt(PS4) for debugging with the tt(XTRACE) option; in
 the latter case it will also work non-interactively.
@@ -145,7 +147,8 @@ capability vectors.
 item(tt(%v))(
 vindex(psvar, use of)
 The value of the first element of the tt(psvar) array parameter.  Following
-the `tt(%)' with an integer gives that element of the array.
+the `tt(%)' with an integer gives that element of the array.  Negative
+integers count from the end of the array.
 )
 item(tt(%{)...tt(%}))(
 Include a string as a literal escape sequence.
@@ -163,10 +166,9 @@ var(true-text)
 and var(false-text) may both contain arbitrarily-nested escape
 sequences, including further ternary expressions.
 
-The left
-parenthesis may be preceded or followed by a positive integer var(n),
-which defaults to zero.  The test character var(x) may be any of the
-following:
+The left parenthesis may be preceded or followed by a positive integer var(n),
+which defaults to zero.  A negative integer will be multiplied by -1.
+The test character var(x) may be any of the following:
 
 startsitem()
 sxitem(tt(c))
diff --git a/Src/prompt.c b/Src/prompt.c
index 8c9216f95..7bd4ed0f1 100644
--- a/Src/prompt.c
+++ b/Src/prompt.c
@@ -38,7 +38,7 @@ unsigned txtattrmask;
 /* text change - attribute change made by prompts */
 
 /**/
-unsigned txtchange;
+mod_export unsigned txtchange;
 
 /* the command stack for use with %_ in prompts */
  
@@ -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,9 +97,48 @@ static int dontcount;
 
 static char *rstring, *Rstring;
 
-/* If non-zero, Inpar, Outpar and Nularg can be added to the buffer. */
+/*
+ * Expand path p; maximum is npath segments where 0 means the whole path.
+ * If tilde is 1, try and find a named directory to use.
+ */
 
-static int nonsp;
+static void
+promptpath(char *p, int npath, int tilde)
+{
+    char *modp = p;
+    Nameddir nd;
+
+    if (tilde && ((nd = finddir(p))))
+	modp = tricat("~", nd->nam, p + strlen(nd->dir));
+
+    if (npath) {
+	char *sptr;
+	if (npath > 0) {
+	    for (sptr = modp + strlen(modp); sptr > modp; sptr--) {
+		if (*sptr == '/' && !--npath) {
+		    sptr++;
+		    break;
+		}
+	    }
+	    if (*sptr == '/' && sptr[1] && sptr != modp)
+		sptr++;
+	    stradd(sptr);
+	} else {
+	    char cbu;
+	    for (sptr = modp+1; *sptr; sptr++)
+		if (*sptr == '/' && !++npath)
+		    break;
+	    cbu = *sptr;
+	    *sptr = 0;
+	    stradd(modp);
+	    *sptr = cbu;
+	}
+    } else
+	stradd(modp);
+
+    if (p != modp)
+	zsfree(modp);
+}
 
 /* Perform prompt expansion on a string, putting the result in a *
  * permanently-allocated string.  If ns is non-zero, this string *
@@ -107,7 +148,7 @@ static int nonsp;
  * `glitch' space.                                               */
 
 /**/
-char *
+mod_export char *
 promptexpand(char *s, int ns, char *rs, char *Rs)
 {
     if(!s)
@@ -119,20 +160,18 @@ promptexpand(char *s, int ns, char *rs, char *Rs)
     if (isset(PROMPTSUBST)) {
 	int olderr = errflag;
 
-	HEAPALLOC {
-	    s = dupstring(s);
-	    if (!parsestr(s))
-		singsub(&s);
-	} LASTALLOC;
+	s = dupstring(s);
+	if (!parsestr(s))
+	    singsub(&s);
+
 	/* Ignore errors in prompt substitution */
 	errflag = olderr;
     }
 
     rstring = rs;
     Rstring = Rs;
-    nonsp = ns;
     fm = s;
-    bp = buf = zalloc(bufspc = 256);
+    bp = bufline = buf = zcalloc(bufspc = 256);
     bp1 = NULL;
     trunclen = 0;
     putpromptchar(1, '\0');
@@ -140,6 +179,17 @@ 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; ) {
+	    if (*bp == Meta)
+		bp += 2;
+	    else if (*bp == Inpar || *bp == Outpar || *bp == Nularg)
+		chuck(bp);
+	    else
+		bp++;
+	}
+    }
     return buf;
 }
 
@@ -160,14 +210,26 @@ putpromptchar(int doprint, int endchar)
     for (; *fm && *fm != endchar; fm++) {
 	arg = 0;
 	if (*fm == '%' && isset(PROMPTPERCENT)) {
-	    if (idigit(*++fm)) {
-		arg = zstrtol(fm, &fm, 10);
+	    int minus = 0;
+	    fm++;
+	    if (*fm == '-') {
+		minus = 1;
+		fm++;
 	    }
+	    if (idigit(*fm)) {
+		arg = zstrtol(fm, &fm, 10);
+		if (minus)
+		    arg *= -1;
+	    } else if (minus)
+		arg = -1;
 	    if (*fm == '(') {
-		int tc;
+		int tc, otrunclen;
 
 		if (idigit(*++fm)) {
 		    arg = zstrtol(fm, &fm, 10);
+		} else if (arg < 0) {
+		    /* negative numbers don't make sense here */
+		    arg *= -1;
 		}
 		test = 0;
 		ss = pwd;
@@ -224,6 +286,12 @@ putpromptchar(int doprint, int endchar)
 		    if (getegid() == arg)
 			test = 1;
 		    break;
+		case 'l':
+		    *bp = '\0';
+		    countprompt(bufline, &t0, 0, 0);
+		    if (t0 >= arg)
+			test = 1;
+		    break;
 		case 'L':
 		    if (shlvl >= arg)
 			test = 1;
@@ -249,10 +317,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)
@@ -276,49 +349,21 @@ putpromptchar(int doprint, int endchar)
 		}
 	    switch (*fm) {
 	    case '~':
-		if ((nd = finddir(pwd))) {
-		    char *t = tricat("~", nd->nam, pwd + strlen(nd->dir));
-		    stradd(t);
-		    zsfree(t);
-		    break;
-		}
+		promptpath(pwd, arg, 1);
+		break;
 	    case 'd':
 	    case '/':
-		stradd(pwd);
+		promptpath(pwd, arg, 0);
 		break;
 	    case 'c':
 	    case '.':
-	        {
-		    char *t;
-
-		    if ((nd = finddir(pwd)))
-			t = tricat("~", nd->nam, pwd + strlen(nd->dir));
-		    else
-			t = ztrdup(pwd);
-		    if (!arg)
-			arg++;
-		    for (ss = t + strlen(t); ss > t; ss--)
-			if (*ss == '/' && !--arg) {
-			    ss++;
-			    break;
-			}
-		    if(*ss == '/' && ss[1] && ss != t)
-			ss++;
-		    stradd(ss);
-		    zsfree(t);
-		    break;
-		}
+		promptpath(pwd, arg ? arg : 1, 1);
+		break;
 	    case 'C':
-		if (!arg)
-		    arg++;
-		for (ss = pwd + strlen(pwd); ss > pwd; ss--)
-		    if (*ss == '/' && !--arg) {
-			ss++;
-			break;
-		    }
-		if (*ss == '/' && ss[1] && (ss != pwd))
-		    ss++;
-		stradd(ss);
+		promptpath(pwd, arg ? arg : 1, 0);
+		break;
+	    case 'N':
+		promptpath(scriptname ? scriptname : argzero, arg, 0);
 		break;
 	    case 'h':
 	    case '!':
@@ -332,13 +377,20 @@ putpromptchar(int doprint, int endchar)
 	    case 'm':
 		if (!arg)
 		    arg++;
-		for (ss = hostnam; *ss; ss++)
-		    if (*ss == '.' && !--arg)
-			break;
-		t0 = *ss;
-		*ss = '\0';
-		stradd(hostnam);
-		*ss = t0;
+		if (arg < 0) {
+		    for (ss = hostnam + strlen(hostnam); ss > hostnam; ss--)
+			if (ss[-1] == '.' && !++arg)
+			    break;
+		    stradd(ss);
+		} else {
+		    for (ss = hostnam; *ss; ss++)
+			if (*ss == '.' && !--arg)
+			    break;
+		    t0 = *ss;
+		    *ss = '\0';
+		    stradd(hostnam);
+		    *ss = t0;
+		}
 		break;
 	    case 'S':
 		txtchangeset(TXTSTANDOUT, TXTNOSTANDOUT);
@@ -377,72 +429,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;
 		}
@@ -535,6 +539,8 @@ putpromptchar(int doprint, int endchar)
 	    case 'v':
 		if (!arg)
 		    arg = 1;
+		else if (arg < 0)
+		    arg += arrlen(psvar) + 1;
 		if (arrlen(psvar) >= arg)
 		    stradd(psvar[arg - 1]);
 		break;
@@ -562,6 +568,11 @@ putpromptchar(int doprint, int endchar)
 		if(Rstring)
 		    stradd(Rstring);
 		break;
+	    case 'i':
+		addbufspc(DIGBUFSIZE);
+		sprintf(bp, "%ld", (long)lineno);
+		bp += strlen(bp);
+		break;
 	    case '\0':
 		return 0;
 	    case Meta:
@@ -569,7 +580,7 @@ putpromptchar(int doprint, int endchar)
 		break;
 	    }
 	} else if(*fm == '!' && isset(PROMPTBANG)) {
-	    if(doprint)
+	    if(doprint) {
 		if(fm[1] == '!') {
 		    fm++;
 		    addbufspc(1);
@@ -579,6 +590,7 @@ putpromptchar(int doprint, int endchar)
 		    sprintf(bp, "%d", curhist);
 		    bp += strlen(bp);
 		}
+	    }
 	} else {
 	    char c = *fm == Meta ? *++fm ^ 32 : *fm;
 
@@ -604,6 +616,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,52 +641,25 @@ 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. */
 
 /**/
-void
+mod_export void
 tsetcap(int cap, int flag)
 {
     if (!(termflags & TERM_SHORT) && tcstr[cap]) {
@@ -684,12 +671,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)
@@ -729,15 +716,20 @@ putstr(int d)
 
 /* Count height etc. of a prompt string returned by promptexpand(). *
  * This depends on the current terminal width, and tabs and         *
- * newlines require nontrivial processing.                          */
+ * newlines require nontrivial processing.                          *
+ * Passing `overf' as -1 means to ignore columns (absolute width).  */
 
 /**/
-void
-countprompt(char *str, int *wp, int *hp)
+mod_export void
+countprompt(char *str, int *wp, int *hp, int overf)
 {
     int w = 0, h = 1;
     int s = 1;
     for(; *str; str++) {
+	if(w >= columns && overf >= 0) {
+	    w = 0;
+	    h++;
+	}
 	if(*str == Meta)
 	    str++;
 	if(*str == Inpar)
@@ -749,12 +741,15 @@ countprompt(char *str, int *wp, int *hp)
 	else if(s) {
 	    if(*str == '\t')
 		w = (w | 7) + 1;
-	    else if(*str == '\n')
-		w = columns;
-	    else
+	    else if(*str == '\n') {
+		w = 0;
+		h++;
+	    } else
 		w++;
 	}
-	if(w >= columns) {
+    }
+    if(w >= columns && overf >= 0) {
+	if (!overf || w > columns) {
 	    w = 0;
 	    h++;
 	}
@@ -764,3 +759,177 @@ countprompt(char *str, int *wp, int *hp)
     if(hp)
 	*hp = h;
 }
+
+/**/
+static int
+prompttrunc(int arg, int truncchar, int doprint, int endchar)
+{
+    if (arg > 0) {
+	char ch = *fm, *ptr, *truncstr;
+	int truncatleft = ch == '<';
+	int w = bp - buf;
+
+	/*
+	 * 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 - buf == w && truncchar == ']') {
+	    addbufspc(1);
+	    *bp++ = '<';
+	}
+	ptr = buf + w;		/* addbufspc() may have realloc()'d buf */
+	truncstr = ztrduppfx(ptr, bp - ptr);
+
+	bp = ptr;
+	w = bp - buf;
+	fm++;
+	putpromptchar(doprint, endchar);
+	ptr = buf + w;		/* putpromptchar() may have realloc()'d */
+	*bp = '\0';
+
+	countprompt(ptr, &w, 0, -1);
+	if (w > 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;
+	    maxlen = tlen < trunclen ? trunclen - tlen : 0;
+	    if (w < fullen) {
+		/* Invisible substrings, lots of shuffling. */
+		int n = strlen(t);
+		char *p = ptr, *q = buf;
+		addbufspc(n);
+		ptr = buf + (p - q); /* addbufspc() may have realloc()'d */
+
+		if (truncatleft) {
+		    p = ptr + n;
+		    q = p;
+
+		    n = fullen - w;
+
+		    /* Shift the whole string right, then *
+		     * selectively copy to the left.      */
+		    memmove(p, ptr, fullen);
+		    while (w > 0 || n > 0) {
+			if (*p == Inpar)
+			    do {
+				*q++ = *p;
+				--n;
+			    } while (*p++ != Outpar && *p && n);
+			else if (w) {
+			    if (--w < maxlen)
+				*q++ = *p;
+			    ++p;
+			}
+		    }
+		    bp = q;
+		} else {
+		    /* Truncate on the right, selectively */
+		    q = ptr + fullen;
+
+		    /* First skip over as much as will "fit". */
+		    while (w > 0 && maxlen > 0) {
+			if (*ptr == Inpar)
+			    while (*ptr++ != Outpar && *ptr) {;}
+			else
+			    ++ptr, --w, --maxlen;
+		    }
+		    if (ptr < q) {
+			/* We didn't reach the end of the string. *
+			 * In case there are more invisible bits, *
+			 * insert the truncstr and keep looking.  */
+			memmove(ptr + n, ptr, q - ptr);
+			q = ptr + n;
+			while (*t)
+			    *ptr++ = *t++;
+			while (*q) {
+			    if (*q == Inpar)
+				do {
+				    *ptr++ = *q;
+				} while (*q++ != Outpar && *q);
+			    else
+				++q;
+			}
+			bp = ptr;
+			*bp = 0;
+		    } else
+			bp = ptr + n;
+		}
+	    } else {
+		/* No invisible substrings. */
+		if (tlen > fullen) {
+		    addbufspc(tlen - fullen);
+		    ptr = bp;	/* addbufspc() may have realloc()'d buf */
+		    bp += tlen - fullen;
+		} else
+		    bp -= fullen - trunclen;
+		if (truncatleft) {
+		    if (maxlen)
+			memmove(ptr + strlen(t), ptr + fullen - maxlen,
+				maxlen);
+		} else
+		    ptr += maxlen;
+	    }
+	    /* Finally, copy the truncstr into place. */
+	    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;
+}