summaryrefslogtreecommitdiff
path: root/Src/prompt.c
diff options
context:
space:
mode:
Diffstat (limited to 'Src/prompt.c')
-rw-r--r--Src/prompt.c493
1 files changed, 331 insertions, 162 deletions
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;
+}