From e74702b467171dbdafb56dfe354794a212e020d9 Mon Sep 17 00:00:00 2001 From: Tanaka Akira Date: Thu, 15 Apr 1999 18:05:38 +0000 Subject: Initial revision --- Src/prompt.c | 766 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 766 insertions(+) create mode 100644 Src/prompt.c (limited to 'Src/prompt.c') diff --git a/Src/prompt.c b/Src/prompt.c new file mode 100644 index 000000000..8c9216f95 --- /dev/null +++ b/Src/prompt.c @@ -0,0 +1,766 @@ +/* + * prompt.c - construct zsh prompts + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1992-1997 Paul Falstad + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Paul Falstad or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Paul Falstad and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Paul Falstad and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Paul Falstad and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +#include "zsh.mdh" +#include "prompt.pro" + +/* text attribute mask */ + +/**/ +unsigned txtattrmask; + +/* text change - attribute change made by prompts */ + +/**/ +unsigned txtchange; + +/* the command stack for use with %_ in prompts */ + +/**/ +unsigned char *cmdstack; +/**/ +int cmdsp; + +/* parser states, for %_ */ + +static char *cmdnames[] = { + "for", "while", "repeat", "select", + "until", "if", "then", "else", + "elif", "math", "cond", "cmdor", + "cmdand", "pipe", "errpipe", "foreach", + "case", "function", "subsh", "cursh", + "array", "quote", "dquote", "bquote", + "cmdsubst", "mathsubst", "elif-then", "heredoc", + "heredocd", "brace", "braceparam", +}; + +/* The buffer into which an expanded and metafied prompt is being written, * + * and its size. */ + +static char *buf; +static int bufspc; + +/* bp is the pointer to the current position in the buffer, where the next * + * character will be added. */ + +static char *bp; + +/* 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. */ + +static char *bp1; + +/* The format string, for %-expansion. */ + +static char *fm; + +/* Current truncation string (metafied), the length at which truncation * + * occurs, and the direction in which it occurs. */ + +static char *truncstr; +static int trunclen, truncatleft; + +/* Current level of nesting of %{ / %} sequences. */ + +static int dontcount; + +/* Strings to use for %r and %R (for the spelling prompt). */ + +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 * + * between spacing and non-spacing parts of the prompt, and * + * Nularg, which (in a non-spacing sequence) indicates a * + * `glitch' space. */ + +/**/ +char * +promptexpand(char *s, int ns, char *rs, char *Rs) +{ + if(!s) + return ztrdup(""); + + if ((termflags & TERM_UNKNOWN) && (unset(INTERACTIVE))) + init_term(); + + if (isset(PROMPTSUBST)) { + int olderr = errflag; + + HEAPALLOC { + s = dupstring(s); + if (!parsestr(s)) + singsub(&s); + } LASTALLOC; + /* Ignore errors in prompt substitution */ + errflag = olderr; + } + + rstring = rs; + Rstring = Rs; + nonsp = ns; + fm = s; + bp = buf = zalloc(bufspc = 256); + bp1 = NULL; + trunclen = 0; + putpromptchar(1, '\0'); + addbufspc(1); + if(dontcount) + *bp++ = Outpar; + *bp = 0; + return buf; +} + +/* Perform %- and !-expansion as required on a section of the prompt. The * + * section is ended by an instance of endchar. If doprint is 0, the valid * + * % sequences are merely skipped over, and nothing is stored. */ + +/**/ +static int +putpromptchar(int doprint, int endchar) +{ + char *ss, *tmbuf = NULL; + int t0, arg, test, sep; + struct tm *tm; + time_t timet; + Nameddir nd; + + for (; *fm && *fm != endchar; fm++) { + arg = 0; + if (*fm == '%' && isset(PROMPTPERCENT)) { + if (idigit(*++fm)) { + arg = zstrtol(fm, &fm, 10); + } + if (*fm == '(') { + int tc; + + if (idigit(*++fm)) { + arg = zstrtol(fm, &fm, 10); + } + test = 0; + ss = pwd; + switch (tc = *fm) { + case 'c': + case '.': + case '~': + if ((nd = finddir(ss))) { + arg--; + ss += strlen(nd->dir); + } + case '/': + case 'C': + for (; *ss; ss++) + if (*ss == '/') + arg--; + if (arg <= 0) + test = 1; + break; + case 't': + case 'T': + case 'd': + case 'D': + case 'w': + timet = time(NULL); + tm = localtime(&timet); + switch (tc) { + case 't': + test = (arg == tm->tm_min); + break; + case 'T': + test = (arg == tm->tm_hour); + break; + case 'd': + test = (arg == tm->tm_mday); + break; + case 'D': + test = (arg == tm->tm_mon); + break; + case 'w': + test = (arg == tm->tm_wday); + break; + } + break; + case '?': + if (lastval == arg) + test = 1; + break; + case '#': + if (geteuid() == arg) + test = 1; + break; + case 'g': + if (getegid() == arg) + test = 1; + break; + case 'L': + if (shlvl >= arg) + test = 1; + break; + case 'S': + if (time(NULL) - shtimer.tv_sec >= arg) + test = 1; + break; + case 'v': + if (arrlen(psvar) >= arg) + test = 1; + break; + case '_': + test = (cmdsp >= arg); + break; + case '!': + test = privasserted(); + break; + default: + test = -1; + break; + } + if (!*fm || !(sep = *++fm)) + return 0; + fm++; + if (!putpromptchar(test == 1 && doprint, sep) || !*++fm || + !putpromptchar(test == 0 && doprint, ')')) { + return 0; + } + continue; + } + if (!doprint) + switch(*fm) { + case '[': + while(idigit(*++fm)); + while(*++fm != ']'); + continue; + case '<': + while(*++fm != '<'); + continue; + case '>': + while(*++fm != '>'); + continue; + case 'D': + if(fm[1]=='{') + while(*++fm != '}'); + continue; + default: + continue; + } + switch (*fm) { + case '~': + if ((nd = finddir(pwd))) { + char *t = tricat("~", nd->nam, pwd + strlen(nd->dir)); + stradd(t); + zsfree(t); + break; + } + case 'd': + case '/': + stradd(pwd); + 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; + } + 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); + break; + case 'h': + case '!': + addbufspc(DIGBUFSIZE); + sprintf(bp, "%d", curhist); + bp += strlen(bp); + break; + case 'M': + stradd(hostnam); + break; + case 'm': + if (!arg) + arg++; + for (ss = hostnam; *ss; ss++) + if (*ss == '.' && !--arg) + break; + t0 = *ss; + *ss = '\0'; + stradd(hostnam); + *ss = t0; + break; + case 'S': + txtchangeset(TXTSTANDOUT, TXTNOSTANDOUT); + txtset(TXTSTANDOUT); + tsetcap(TCSTANDOUTBEG, 1); + break; + case 's': + txtchangeset(TXTNOSTANDOUT, TXTSTANDOUT); + txtset(TXTDIRTY); + txtunset(TXTSTANDOUT); + tsetcap(TCSTANDOUTEND, 1); + break; + case 'B': + txtchangeset(TXTBOLDFACE, TXTNOBOLDFACE); + txtset(TXTDIRTY); + txtset(TXTBOLDFACE); + tsetcap(TCBOLDFACEBEG, 1); + break; + case 'b': + txtchangeset(TXTNOBOLDFACE, TXTBOLDFACE); + txtchangeset(TXTNOSTANDOUT, TXTSTANDOUT); + txtchangeset(TXTNOUNDERLINE, TXTUNDERLINE); + txtset(TXTDIRTY); + txtunset(TXTBOLDFACE); + tsetcap(TCALLATTRSOFF, 1); + break; + case 'U': + txtchangeset(TXTUNDERLINE, TXTNOUNDERLINE); + txtset(TXTUNDERLINE); + tsetcap(TCUNDERLINEBEG, 1); + break; + case 'u': + txtchangeset(TXTNOUNDERLINE, TXTUNDERLINE); + txtset(TXTDIRTY); + txtunset(TXTUNDERLINE); + 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; + 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; + break; + case '{': /*}*/ + if (!dontcount++ && nonsp) { + addbufspc(1); + *bp++ = Inpar; + } + break; + case /*{*/ '}': + if (dontcount && !--dontcount && nonsp) { + addbufspc(1); + *bp++ = Outpar; + } + break; + case 't': + case '@': + case 'T': + case '*': + case 'w': + case 'W': + case 'D': + { + char *tmfmt, *dd; + + switch (*fm) { + case 'T': + tmfmt = "%K:%M"; + break; + case '*': + tmfmt = "%K:%M:%S"; + break; + case 'w': + tmfmt = "%a %f"; + break; + case 'W': + tmfmt = "%m/%d/%y"; + break; + case 'D': + if (fm[1] == '{') /*}*/ { + for (ss = fm + 2; *ss && *ss != /*{*/ '}'; ss++) + if(*ss == '\\' && ss[1]) + ss++; + dd = tmfmt = tmbuf = zalloc(ss - fm); + for (ss = fm + 2; *ss && *ss != /*{*/ '}'; ss++) { + if(*ss == '\\' && ss[1]) + ss++; + *dd++ = *ss; + } + *dd = 0; + fm = ss - !*ss; + } else + tmfmt = "%y-%m-%d"; + break; + default: + tmfmt = "%l:%M%p"; + break; + } + timet = time(NULL); + tm = localtime(&timet); + for(t0=80; ; t0*=2) { + addbufspc(t0); + if(ztrftime(bp, t0, tmfmt, tm) != t0) + break; + } + bp += strlen(bp); + free(tmbuf); + tmbuf = NULL; + break; + } + case 'n': + stradd(get_username()); + break; + case 'l': + if (*ttystrname) { + ss = (strncmp(ttystrname, "/dev/tty", 8) ? + ttystrname + 5 : ttystrname + 8); + stradd(ss); + } else + stradd("()"); + break; + case 'L': + addbufspc(DIGBUFSIZE); + sprintf(bp, "%ld", (long)shlvl); + bp += strlen(bp); + break; + case '?': + addbufspc(DIGBUFSIZE); + sprintf(bp, "%ld", (long)lastval); + bp += strlen(bp); + break; + case '%': + case ')': + addbufspc(1); + *bp++ = *fm; + break; + case '#': + addbufspc(1); + *bp++ = privasserted() ? '#' : '%'; + break; + case 'v': + if (!arg) + arg = 1; + if (arrlen(psvar) >= arg) + stradd(psvar[arg - 1]); + break; + case 'E': + tsetcap(TCCLEAREOL, 1); + break; + case '_': + if (cmdsp) { + if (arg > cmdsp || arg <= 0) + arg = cmdsp; + for (t0 = cmdsp - arg; arg--; t0++) { + stradd(cmdnames[cmdstack[t0]]); + if (arg) { + addbufspc(1); + *bp++=' '; + } + } + } + break; + case 'r': + if(rstring) + stradd(rstring); + break; + case 'R': + if(Rstring) + stradd(Rstring); + break; + case '\0': + return 0; + case Meta: + fm++; + break; + } + } else if(*fm == '!' && isset(PROMPTBANG)) { + if(doprint) + if(fm[1] == '!') { + fm++; + addbufspc(1); + pputc('!'); + } else { + addbufspc(DIGBUFSIZE); + sprintf(bp, "%d", curhist); + bp += strlen(bp); + } + } else { + char c = *fm == Meta ? *++fm ^ 32 : *fm; + + if (doprint) { + addbufspc(1); + pputc(c); + } + } + } + + return *fm; +} + +/* pputc adds a character to the buffer, metafying. There must * + * already be space. */ + +/**/ +static void +pputc(char c) +{ + if(imeta(STOUC(c))) { + *bp++ = Meta; + c ^= 32; + } + *bp++ = c; +} + +/* Make sure there is room for `need' more characters in the buffer. */ + +/**/ +static void +addbufspc(int need) +{ + need *= 2; /* for metafication */ + if((bp - buf) + need > bufspc) { + int bo = bp - buf; + int bo1 = bp1 ? bp1 - buf : -1; + + if(need & 255) + need = (need | 255) + 1; + buf = realloc(buf, bufspc += need); + bp = buf + bo; + if(bo1 != -1) + bp1 = buf + bo1; + } +} + +/* stradd() adds a metafied string to the prompt, * + * in a visible representation, doing truncation. */ + +/**/ +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); + /* 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++) + 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++; + } +} + +/* tsetcap(), among other things, can write a termcap string into the buffer. */ + +/**/ +void +tsetcap(int cap, int flag) +{ + if (!(termflags & TERM_SHORT) && tcstr[cap]) { + switch(flag) { + case -1: + tputs(tcstr[cap], 1, putraw); + break; + case 0: + tputs(tcstr[cap], 1, putshout); + break; + case 1: + if (!dontcount && nonsp) { + addbufspc(1); + *bp++ = Inpar; + } + tputs(tcstr[cap], 1, putstr); + if (!dontcount && nonsp) { + int glitch = 0; + + if (cap == TCSTANDOUTBEG || cap == TCSTANDOUTEND) + glitch = tgetnum("sg"); + else if (cap == TCUNDERLINEBEG || cap == TCUNDERLINEEND) + glitch = tgetnum("ug"); + if(glitch < 0) + glitch = 0; + addbufspc(glitch + 1); + while(glitch--) + *bp++ = Nularg; + *bp++ = Outpar; + } + break; + } + + if (txtisset(TXTDIRTY)) { + txtunset(TXTDIRTY); + if (txtisset(TXTBOLDFACE) && cap != TCBOLDFACEBEG) + tsetcap(TCBOLDFACEBEG, flag); + if (txtisset(TXTSTANDOUT)) + tsetcap(TCSTANDOUTBEG, flag); + if (txtisset(TXTUNDERLINE)) + tsetcap(TCUNDERLINEBEG, flag); + } + } +} + +/**/ +int +putstr(int d) +{ + addbufspc(1); + pputc(d); + return 0; +} + +/* Count height etc. of a prompt string returned by promptexpand(). * + * This depends on the current terminal width, and tabs and * + * newlines require nontrivial processing. */ + +/**/ +void +countprompt(char *str, int *wp, int *hp) +{ + int w = 0, h = 1; + int s = 1; + for(; *str; str++) { + if(*str == Meta) + str++; + if(*str == Inpar) + s = 0; + else if(*str == Outpar) + s = 1; + else if(*str == Nularg) + w++; + else if(s) { + if(*str == '\t') + w = (w | 7) + 1; + else if(*str == '\n') + w = columns; + else + w++; + } + if(w >= columns) { + w = 0; + h++; + } + } + if(wp) + *wp = w; + if(hp) + *hp = h; +} -- cgit 1.4.1