diff options
Diffstat (limited to 'Src/prompt.c')
-rw-r--r-- | Src/prompt.c | 493 |
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; +} |