diff options
| author | Craig Jennings <c@cjennings.net> | 2025-05-08 18:49:34 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2025-05-08 18:51:59 -0500 |
| commit | 000e00871830cd15de032c80e2b62946cf19445c (patch) | |
| tree | 794a7922750472bbe0e024042d6ba84f411fc3e0 /dotfiles/system/.zsh/modules/Src/prompt.c | |
| parent | fe302606931e4bad91c4ed6df81a4403523ba780 (diff) | |
adding missing dotfiles and folders
- profile.d/
- bashrc
- authinfo.gpg
- .zsh/
Diffstat (limited to 'dotfiles/system/.zsh/modules/Src/prompt.c')
| -rw-r--r-- | dotfiles/system/.zsh/modules/Src/prompt.c | 2046 |
1 files changed, 2046 insertions, 0 deletions
diff --git a/dotfiles/system/.zsh/modules/Src/prompt.c b/dotfiles/system/.zsh/modules/Src/prompt.c new file mode 100644 index 0000000..959ed8e --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/prompt.c @@ -0,0 +1,2046 @@ +/* + * 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 */ + +/**/ +mod_export unsigned txtattrmask; + +/* the command stack for use with %_ in prompts */ + +/**/ +unsigned char *cmdstack; +/**/ +int cmdsp; + +/* parser states, for %_ */ + +static char *cmdnames[CS_COUNT] = { + "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", "always", +}; + + +struct buf_vars; + +struct buf_vars { +/* Previous set of prompt variables on the stack. */ + + struct buf_vars *last; + +/* The buffer into which an expanded and metafied prompt is being written, * + * and its size. */ + + char *buf; + int bufspc; + +/* bp is the pointer to the current position in the buffer, where the next * + * character will be added. */ + + char *bp; + +/* Position of the start of the current line in the buffer */ + + char *bufline; + +/* bp1 is an auxiliary 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. */ + + char *bp1; + +/* The format string, for %-expansion. */ + + char *fm; + +/* Non-zero if truncating the current segment of the buffer. */ + + int truncwidth; + +/* Current level of nesting of %{ / %} sequences. */ + + int dontcount; + +/* Level of %{ / %} surrounding a truncation segment. */ + + int trunccount; + +/* Strings to use for %r and %R (for the spelling prompt). */ + + char *rstring, *Rstring; +}; + +typedef struct buf_vars *Buf_vars; + +/* The currently active prompt output variables */ +static Buf_vars bv; + +/* + * 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 void +promptpath(char *p, int npath, int tilde) +{ + char *modp = p; + Nameddir nd; + + if (tilde && ((nd = finddir(p)))) + modp = tricat("~", nd->node.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 + * 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. + * + * txtchangep gives an integer controlling the attributes of + * the prompt. This is for use in zle to maintain the attributes + * consistenly. Other parts of the shell should not need to use it. + */ + +/**/ +mod_export char * +promptexpand(char *s, int ns, char *rs, char *Rs, unsigned int *txtchangep) +{ + struct buf_vars new_vars; + + if(!s) + return ztrdup(""); + + if ((termflags & TERM_UNKNOWN) && (unset(INTERACTIVE))) + init_term(); + + if (isset(PROMPTSUBST)) { + int olderr = errflag; + int oldval = lastval; + + s = dupstring(s); + if (!parsestr(&s)) + singsub(&s); + /* + * We don't need the special Nularg hack here and we're + * going to be using Nularg for other things. + */ + if (*s == Nularg && s[1] == '\0') + *s = '\0'; + + /* + * Ignore errors and status change in prompt substitution. + * However, keep any user interrupt error that occurred. + */ + errflag = olderr | (errflag & ERRFLAG_INT); + lastval = oldval; + } + + memset(&new_vars, 0, sizeof(new_vars)); + new_vars.last = bv; + bv = &new_vars; + + new_vars.rstring = rs; + new_vars.Rstring = Rs; + new_vars.fm = s; + new_vars.bufspc = 256; + new_vars.bp = new_vars.bufline = new_vars.buf = zshcalloc(new_vars.bufspc); + new_vars.bp1 = NULL; + new_vars.truncwidth = 0; + + putpromptchar(1, '\0', txtchangep); + addbufspc(2); + if (new_vars.dontcount) + *new_vars.bp++ = Outpar; + *new_vars.bp = '\0'; + if (!ns) { + /* If zero, Inpar, Outpar and Nularg should be removed. */ + for (new_vars.bp = new_vars.buf; *new_vars.bp; ) { + if (*new_vars.bp == Meta) + new_vars.bp += 2; + else if (*new_vars.bp == Inpar || *new_vars.bp == Outpar || + *new_vars.bp == Nularg) + chuck(new_vars.bp); + else + new_vars.bp++; + } + } + + bv = new_vars.last; + + return new_vars.buf; +} + +/* Parse the argument for %F and %K */ +static int +parsecolorchar(int arg, int is_fg) +{ + if (bv->fm[1] == '{') { + char *ep; + bv->fm += 2; /* skip over F{ */ + if ((ep = strchr(bv->fm, '}'))) { + char oc = *ep, *col, *coll; + *ep = '\0'; + /* expand the contents of the argument so you can use + * %v for example */ + coll = col = promptexpand(bv->fm, 0, NULL, NULL, NULL); + *ep = oc; + arg = match_colour((const char **)&coll, is_fg, 0); + free(col); + bv->fm = ep; + } else { + arg = match_colour((const char **)&bv->fm, is_fg, 0); + if (*bv->fm != '}') + bv->fm--; + } + } else + arg = match_colour(NULL, 1, arg); + return arg; +} + +/* 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, unsigned int *txtchangep) +{ + char *ss, *hostnam; + int t0, arg, test, sep, j, numjobs, len; + struct tm *tm; + struct timespec ts; + time_t timet; + Nameddir nd; + + for (; *bv->fm && *bv->fm != endchar; bv->fm++) { + arg = 0; + if (*bv->fm == '%' && isset(PROMPTPERCENT)) { + int minus = 0; + bv->fm++; + if (*bv->fm == '-') { + minus = 1; + bv->fm++; + } + if (idigit(*bv->fm)) { + arg = zstrtol(bv->fm, &bv->fm, 10); + if (minus) + arg *= -1; + } else if (minus) + arg = -1; + if (*bv->fm == '(') { + int tc, otruncwidth; + + if (idigit(*++bv->fm)) { + arg = zstrtol(bv->fm, &bv->fm, 10); + } else if (arg < 0) { + /* negative numbers don't make sense here */ + arg *= -1; + } + test = 0; + ss = pwd; + switch (tc = *bv->fm) { + case 'c': + case '.': + case '~': + if ((nd = finddir(ss))) { + arg--; + ss += strlen(nd->dir); + } /*FALLTHROUGH*/ + case '/': + case 'C': + /* `/' gives 0, `/any' gives 1, etc. */ + if (*ss && *ss++ == '/' && *ss) + arg--; + 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() == (uid_t)arg) + test = 1; + break; + case 'g': + if (getegid() == (gid_t)arg) + test = 1; + break; + case 'j': + for (numjobs = 0, j = 1; j <= maxjob; j++) + if (jobtab[j].stat && jobtab[j].procs && + !(jobtab[j].stat & STAT_NOPRINT)) numjobs++; + if (numjobs >= arg) + test = 1; + break; + case 'l': + *bv->bp = '\0'; + countprompt(bv->bufline, &t0, 0, 0); + if (minus) + t0 = zterm_columns - t0; + if (t0 >= arg) + test = 1; + break; + case 'e': + { + Funcstack fsptr = funcstack; + test = arg; + while (fsptr && test > 0) { + test--; + fsptr = fsptr->prev; + } + test = !test; + } + 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_ge(psvar, arg)) + test = 1; + break; + case 'V': + if (psvar && *psvar && arrlen_ge(psvar, arg)) { + if (*psvar[(arg ? arg : 1) - 1]) + test = 1; + } + break; + case '_': + test = (cmdsp >= arg); + break; + case '!': + test = privasserted(); + break; + default: + test = -1; + break; + } + if (!*bv->fm || !(sep = *++bv->fm)) + return 0; + bv->fm++; + /* Don't do the current truncation until we get back */ + otruncwidth = bv->truncwidth; + bv->truncwidth = 0; + if (!putpromptchar(test == 1 && doprint, sep, + txtchangep) || !*++bv->fm || + !putpromptchar(test == 0 && doprint, ')', + txtchangep)) { + bv->truncwidth = otruncwidth; + return 0; + } + bv->truncwidth = otruncwidth; + continue; + } + if (!doprint) + switch(*bv->fm) { + case '[': + while(idigit(*++bv->fm)); + while(*++bv->fm != ']'); + continue; + case '<': + while(*++bv->fm != '<'); + continue; + case '>': + while(*++bv->fm != '>'); + continue; + case 'D': + if(bv->fm[1]=='{') + while(*++bv->fm != '}'); + continue; + default: + continue; + } + switch (*bv->fm) { + case '~': + promptpath(pwd, arg, 1); + break; + case 'd': + case '/': + promptpath(pwd, arg, 0); + break; + case 'c': + case '.': + promptpath(pwd, arg ? arg : 1, 1); + break; + case 'C': + promptpath(pwd, arg ? arg : 1, 0); + break; + case 'N': + promptpath(scriptname ? scriptname : argzero, arg, 0); + break; + case 'h': + case '!': + addbufspc(DIGBUFSIZE); + convbase(bv->bp, curhist, 10); + bv->bp += strlen(bv->bp); + break; + case 'j': + for (numjobs = 0, j = 1; j <= maxjob; j++) + if (jobtab[j].stat && jobtab[j].procs && + !(jobtab[j].stat & STAT_NOPRINT)) numjobs++; + addbufspc(DIGBUFSIZE); + sprintf(bv->bp, "%d", numjobs); + bv->bp += strlen(bv->bp); + break; + case 'M': + queue_signals(); + if ((hostnam = getsparam("HOST"))) + stradd(hostnam); + unqueue_signals(); + break; + case 'm': + if (!arg) + arg++; + queue_signals(); + if (!(hostnam = getsparam("HOST"))) { + unqueue_signals(); + break; + } + 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; + stradd(*ss ? dupstrpfx(hostnam, ss - hostnam) : hostnam); + } + unqueue_signals(); + break; + case 'S': + txtchangeset(txtchangep, TXTSTANDOUT, TXTNOSTANDOUT); + txtset(TXTSTANDOUT); + tsetcap(TCSTANDOUTBEG, TSC_PROMPT); + break; + case 's': + txtchangeset(txtchangep, TXTNOSTANDOUT, TXTSTANDOUT); + txtunset(TXTSTANDOUT); + tsetcap(TCSTANDOUTEND, TSC_PROMPT|TSC_DIRTY); + break; + case 'B': + txtchangeset(txtchangep, TXTBOLDFACE, TXTNOBOLDFACE); + txtset(TXTBOLDFACE); + tsetcap(TCBOLDFACEBEG, TSC_PROMPT|TSC_DIRTY); + break; + case 'b': + txtchangeset(txtchangep, TXTNOBOLDFACE, TXTBOLDFACE); + txtunset(TXTBOLDFACE); + tsetcap(TCALLATTRSOFF, TSC_PROMPT|TSC_DIRTY); + break; + case 'U': + txtchangeset(txtchangep, TXTUNDERLINE, TXTNOUNDERLINE); + txtset(TXTUNDERLINE); + tsetcap(TCUNDERLINEBEG, TSC_PROMPT); + break; + case 'u': + txtchangeset(txtchangep, TXTNOUNDERLINE, TXTUNDERLINE); + txtunset(TXTUNDERLINE); + tsetcap(TCUNDERLINEEND, TSC_PROMPT|TSC_DIRTY); + break; + case 'F': + arg = parsecolorchar(arg, 1); + if (arg >= 0 && !(arg & TXTNOFGCOLOUR)) { + txtchangeset(txtchangep, arg & TXT_ATTR_FG_ON_MASK, + TXTNOFGCOLOUR | TXT_ATTR_FG_COL_MASK); + txtunset(TXT_ATTR_FG_COL_MASK); + txtset(arg & TXT_ATTR_FG_ON_MASK); + set_colour_attribute(arg, COL_SEQ_FG, TSC_PROMPT); + break; + } + /* else FALLTHROUGH */ + case 'f': + txtchangeset(txtchangep, TXTNOFGCOLOUR, TXT_ATTR_FG_ON_MASK); + txtunset(TXT_ATTR_FG_ON_MASK); + set_colour_attribute(TXTNOFGCOLOUR, COL_SEQ_FG, TSC_PROMPT); + break; + case 'K': + arg = parsecolorchar(arg, 0); + if (arg >= 0 && !(arg & TXTNOBGCOLOUR)) { + txtchangeset(txtchangep, arg & TXT_ATTR_BG_ON_MASK, + TXTNOBGCOLOUR | TXT_ATTR_BG_COL_MASK); + txtunset(TXT_ATTR_BG_COL_MASK); + txtset(arg & TXT_ATTR_BG_ON_MASK); + set_colour_attribute(arg, COL_SEQ_BG, TSC_PROMPT); + break; + } + /* else FALLTHROUGH */ + case 'k': + txtchangeset(txtchangep, TXTNOBGCOLOUR, TXT_ATTR_BG_ON_MASK); + txtunset(TXT_ATTR_BG_ON_MASK); + set_colour_attribute(TXTNOBGCOLOUR, COL_SEQ_BG, TSC_PROMPT); + break; + case '[': + if (idigit(*++bv->fm)) + arg = zstrtol(bv->fm, &bv->fm, 10); + if (!prompttrunc(arg, ']', doprint, endchar, txtchangep)) + return *bv->fm; + break; + case '<': + case '>': + /* Test (minus) here so -0 means "at the right margin" */ + if (minus) { + *bv->bp = '\0'; + countprompt(bv->bufline, &t0, 0, 0); + arg = zterm_columns - t0 + arg; + if (arg <= 0) + arg = 1; + } + if (!prompttrunc(arg, *bv->fm, doprint, endchar, txtchangep)) + return *bv->fm; + break; + case '{': /*}*/ + if (!bv->dontcount++) { + addbufspc(1); + *bv->bp++ = Inpar; + } + if (arg <= 0) + break; + /* else */ + /* FALLTHROUGH */ + case 'G': + if (arg > 0) { + addbufspc(arg); + while (arg--) + *bv->bp++ = Nularg; + } else { + addbufspc(1); + *bv->bp++ = Nularg; + } + break; + case /*{*/ '}': + if (bv->trunccount && bv->trunccount >= bv->dontcount) + return *bv->fm; + if (bv->dontcount && !--bv->dontcount) { + addbufspc(1); + *bv->bp++ = Outpar; + } + break; + case 't': + case '@': + case 'T': + case '*': + case 'w': + case 'W': + case 'D': + { + char *tmfmt, *dd, *tmbuf = NULL; + + switch (*bv->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 (bv->fm[1] == '{' /*}*/) { + for (ss = bv->fm + 2; *ss && *ss != /*{*/ '}'; ss++) + if(*ss == '\\' && ss[1]) + ss++; + dd = tmfmt = tmbuf = zalloc(ss - bv->fm); + for (ss = bv->fm + 2; *ss && *ss != /*{*/ '}'; + ss++) { + if(*ss == '\\' && ss[1]) + ss++; + *dd++ = *ss; + } + *dd = 0; + bv->fm = ss - !*ss; + if (!*tmfmt) { + free(tmbuf); + continue; + } + } else + tmfmt = "%y-%m-%d"; + break; + default: + tmfmt = "%l:%M%p"; + break; + } + zgettime(&ts); + tm = localtime(&ts.tv_sec); + /* + * Hack because strftime won't say how + * much space it actually needs. Try to add it + * a few times until it works. Some formats don't + * actually have a length, so we could go on for + * ever. + */ + for(j = 0, t0 = strlen(tmfmt)*8; j < 3; j++, t0*=2) { + addbufspc(t0); + if ((len = ztrftime(bv->bp, t0, tmfmt, tm, ts.tv_nsec)) + >= 0) + break; + } + /* There is enough room for this because addbufspc(t0) + * allocates room for t0 * 2 bytes. */ + if (len >= 0) + metafy(bv->bp, len, META_NOALLOC); + bv->bp += strlen(bv->bp); + zsfree(tmbuf); + 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 'y': + if (*ttystrname) { + ss = (strncmp(ttystrname, "/dev/", 5) ? + ttystrname : ttystrname + 5); + stradd(ss); + } else + stradd("()"); + break; + case 'L': + addbufspc(DIGBUFSIZE); +#if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD) + sprintf(bv->bp, "%lld", shlvl); +#else + sprintf(bv->bp, "%ld", (long)shlvl); +#endif + bv->bp += strlen(bv->bp); + break; + case '?': + addbufspc(DIGBUFSIZE); +#if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD) + sprintf(bv->bp, "%lld", lastval); +#else + sprintf(bv->bp, "%ld", (long)lastval); +#endif + bv->bp += strlen(bv->bp); + break; + case '%': + case ')': + addbufspc(1); + *bv->bp++ = *bv->fm; + break; + case '#': + addbufspc(1); + *bv->bp++ = privasserted() ? '#' : '%'; + break; + case 'v': + if (!arg) + arg = 1; + else if (arg < 0) + arg += arrlen(psvar) + 1; + if (arg > 0 && arrlen_ge(psvar, arg)) + stradd(psvar[arg - 1]); + break; + case 'E': + tsetcap(TCCLEAREOL, TSC_PROMPT); + break; + case '^': + if (cmdsp) { + if (arg >= 0) { + if (arg > cmdsp || arg == 0) + arg = cmdsp; + for (t0 = cmdsp - 1; arg--; t0--) { + stradd(cmdnames[cmdstack[t0]]); + if (arg) { + addbufspc(1); + *bv->bp++=' '; + } + } + } else { + arg = -arg; + if (arg > cmdsp) + arg = cmdsp; + for (t0 = arg - 1; arg--; t0--) { + stradd(cmdnames[cmdstack[t0]]); + if (arg) { + addbufspc(1); + *bv->bp++=' '; + } + } + } + } + break; + case '_': + if (cmdsp) { + if (arg >= 0) { + if (arg > cmdsp || arg == 0) + arg = cmdsp; + for (t0 = cmdsp - arg; arg--; t0++) { + stradd(cmdnames[cmdstack[t0]]); + if (arg) { + addbufspc(1); + *bv->bp++=' '; + } + } + } else { + arg = -arg; + if (arg > cmdsp) + arg = cmdsp; + for (t0 = 0; arg--; t0++) { + stradd(cmdnames[cmdstack[t0]]); + if (arg) { + addbufspc(1); + *bv->bp++=' '; + } + } + } + } + break; + case 'r': + if(bv->rstring) + stradd(bv->rstring); + break; + case 'R': + if(bv->Rstring) + stradd(bv->Rstring); + break; + case 'e': + { + int depth = 0; + Funcstack fsptr = funcstack; + while (fsptr) { + depth++; + fsptr = fsptr->prev; + } + addbufspc(DIGBUFSIZE); + sprintf(bv->bp, "%d", depth); + bv->bp += strlen(bv->bp); + break; + } + case 'I': + if (funcstack && funcstack->tp != FS_SOURCE && + !IN_EVAL_TRAP()) { + /* + * We're in a function or an eval with + * EVALLINENO. Calculate the line number in + * the file. + */ + zlong flineno = lineno + funcstack->flineno; + /* take account of eval line nos. starting at 1 */ + if (funcstack->tp == FS_EVAL) + lineno--; + addbufspc(DIGBUFSIZE); +#if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD) + sprintf(bv->bp, "%lld", flineno); +#else + sprintf(bv->bp, "%ld", (long)flineno); +#endif + bv->bp += strlen(bv->bp); + break; + } + /* else we're in a file and lineno is already correct */ + /* FALLTHROUGH */ + case 'i': + addbufspc(DIGBUFSIZE); +#if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD) + sprintf(bv->bp, "%lld", lineno); +#else + sprintf(bv->bp, "%ld", (long)lineno); +#endif + bv->bp += strlen(bv->bp); + break; + case 'x': + if (funcstack && funcstack->tp != FS_SOURCE && + !IN_EVAL_TRAP()) + promptpath(funcstack->filename ? funcstack->filename : "", + arg, 0); + else + promptpath(scriptfilename ? scriptfilename : argzero, + arg, 0); + break; + case '\0': + return 0; + case Meta: + bv->fm++; + break; + } + } else if(*bv->fm == '!' && isset(PROMPTBANG)) { + if(doprint) { + if(bv->fm[1] == '!') { + bv->fm++; + addbufspc(1); + pputc('!'); + } else { + addbufspc(DIGBUFSIZE); + convbase(bv->bp, curhist, 10); + bv->bp += strlen(bv->bp); + } + } + } else { + char c = *bv->fm == Meta ? *++bv->fm ^ 32 : *bv->fm; + + if (doprint) { + addbufspc(1); + pputc(c); + } + } + } + + return *bv->fm; +} + +/* pputc adds a character to the buffer, metafying. There must * + * already be space. */ + +/**/ +static void +pputc(char c) +{ + if (imeta(c)) { + *bv->bp++ = Meta; + c ^= 32; + } + *bv->bp++ = c; + if (c == '\n' && !bv->dontcount) + bv->bufline = bv->bp; +} + +/* Make sure there is room for `need' more characters in the buffer. */ + +/**/ +static void +addbufspc(int need) +{ + need *= 2; /* for metafication */ + if((bv->bp - bv->buf) + need > bv->bufspc) { + int bo = bv->bp - bv->buf; + int bo1 = bv->bp1 ? bv->bp1 - bv->buf : -1; + ptrdiff_t bufline_off = bv->bufline ? bv->bufline - bv->buf : -1; + + if(need & 255) + need = (need | 255) + 1; + bv->buf = realloc(bv->buf, bv->bufspc += need); + memset(bv->buf + bv->bufspc - need, 0, need); + bv->bp = bv->buf + bo; + if(bo1 != -1) + bv->bp1 = bv->buf + bo1; + if (bufline_off != -1) + bv->bufline = bv->buf + bufline_off; + } +} + +/* stradd() adds a metafied string to the prompt, * + * in a visible representation. */ + +/**/ +void +stradd(char *d) +{ +#ifdef MULTIBYTE_SUPPORT + char *ums, *ups; + int upslen, eol = 0; + mbstate_t mbs; + + memset(&mbs, 0, sizeof mbs); + ums = ztrdup(d); + ups = unmetafy(ums, &upslen); + + /* + * We now have a raw string of possibly multibyte characters. + * Read each character one by one. + */ + while (upslen > 0) { + wchar_t cc; + char *pc; + size_t cnt = eol ? MB_INVALID : mbrtowc(&cc, ups, upslen, &mbs); + + switch (cnt) { + case MB_INCOMPLETE: + eol = 1; + /* FALL THROUGH */ + case MB_INVALID: + /* Bad character. Take the next byte on its own. */ + pc = nicechar(*ups); + cnt = 1; + memset(&mbs, 0, sizeof mbs); + break; + case 0: + cnt = 1; + /* FALL THROUGH */ + default: + /* Take full wide character in one go */ + mb_charinit(); + pc = wcs_nicechar(cc, NULL, NULL); + break; + } + /* Keep output as metafied string. */ + addbufspc(strlen(pc)); + + upslen -= cnt; + ups += cnt; + + /* Put printed representation into the buffer */ + while (*pc) + *bv->bp++ = *pc++; + } + + free(ums); +#else + char *ps, *pc; + addbufspc(niceztrlen(d)); + /* This loop puts the nice representation of the string into the + * prompt buffer. */ + for (ps = d; *ps; ps++) { + for (pc = nicechar(*ps == Meta ? *++ps^32 : *ps); *pc; pc++) + *bv->bp++ = *pc; + } +#endif +} + +/* tsetcap(), among other things, can write a termcap string into the buffer. */ + +/**/ +mod_export void +tsetcap(int cap, int flags) +{ + if (tccan(cap) && !isset(SINGLELINEZLE) && + !(termflags & (TERM_NOUP|TERM_BAD|TERM_UNKNOWN))) { + switch (flags & TSC_OUTPUT_MASK) { + case TSC_RAW: + tputs(tcstr[cap], 1, putraw); + break; + case 0: + default: + tputs(tcstr[cap], 1, putshout); + break; + case TSC_PROMPT: + if (!bv->dontcount) { + addbufspc(1); + *bv->bp++ = Inpar; + } + tputs(tcstr[cap], 1, putstr); + if (!bv->dontcount) { + 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--) + *bv->bp++ = Nularg; + *bv->bp++ = Outpar; + } + break; + } + + if (flags & TSC_DIRTY) { + flags &= ~TSC_DIRTY; + if (txtisset(TXTBOLDFACE) && cap != TCBOLDFACEBEG) + tsetcap(TCBOLDFACEBEG, flags); + if (txtisset(TXTSTANDOUT)) + tsetcap(TCSTANDOUTBEG, flags); + if (txtisset(TXTUNDERLINE)) + tsetcap(TCUNDERLINEBEG, flags); + if (txtisset(TXTFGCOLOUR)) + set_colour_attribute(txtattrmask, COL_SEQ_FG, TSC_PROMPT); + if (txtisset(TXTBGCOLOUR)) + set_colour_attribute(txtattrmask, COL_SEQ_BG, TSC_PROMPT); + } + } +} + +/**/ +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. + * Passing `overf' as -1 means to ignore columns (absolute width). + * + * If multibyte is enabled, take account of multibyte characters + * by locating them and finding out their screen width. + */ + +/**/ +mod_export void +countprompt(char *str, int *wp, int *hp, int overf) +{ + int w = 0, h = 1; + int s = 1; +#ifdef MULTIBYTE_SUPPORT + int wcw, multi = 0; + char inchar; + mbstate_t mbs; + wchar_t wc; + + memset(&mbs, 0, sizeof(mbs)); +#endif + + for (; *str; str++) { + if (w > zterm_columns && overf >= 0) { + w = 0; + h++; + } + /* + * Input string should be metafied, so tokens in it should + * be real tokens, even if there are multibyte characters. + */ + if (*str == Inpar) + s = 0; + else if (*str == Outpar) + s = 1; + else if (*str == Nularg) + w++; + else if (s) { + if (*str == Meta) { +#ifdef MULTIBYTE_SUPPORT + inchar = *++str ^ 32; +#else + str++; +#endif + } else { +#ifdef MULTIBYTE_SUPPORT + /* + * Don't look for tab or newline in the middle + * of a multibyte character. Otherwise, we are + * relying on the character set being an extension + * of ASCII so it's safe to test a single byte. + */ + if (!multi) { +#endif + if (*str == '\t') { + w = (w | 7) + 1; + continue; + } else if (*str == '\n') { + w = 0; + h++; + continue; + } +#ifdef MULTIBYTE_SUPPORT + } + + inchar = *str; +#endif + } + +#ifdef MULTIBYTE_SUPPORT + switch (mbrtowc(&wc, &inchar, 1, &mbs)) { + case MB_INCOMPLETE: + /* Character is incomplete -- keep looking. */ + multi = 1; + break; + case MB_INVALID: + memset(&mbs, 0, sizeof mbs); + /* Invalid character: assume single width. */ + multi = 0; + w++; + break; + case 0: + multi = 0; + break; + default: + /* + * If the character isn't printable, WCWIDTH() returns + * -1. We assume width 1. + */ + wcw = WCWIDTH(wc); + if (wcw >= 0) + w += wcw; + else + w++; + multi = 0; + break; + } +#else + w++; +#endif + } + } + /* + * multi may still be set if we were in the middle of the character. + * This isn't easy to handle generally; just assume there's no + * output. + */ + if(w >= zterm_columns && overf >= 0) { + if (!overf || w > zterm_columns) { + w = 0; + h++; + } + } + if(wp) + *wp = w; + if(hp) + *hp = h; +} + +/**/ +static int +prompttrunc(int arg, int truncchar, int doprint, int endchar, + unsigned int *txtchangep) +{ + if (arg > 0) { + char ch = *bv->fm, *ptr, *truncstr; + int truncatleft = ch == '<'; + int w = bv->bp - bv->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 (bv->truncwidth) { + while (*--bv->fm != '%') + ; + bv->fm--; + return 0; + } + + bv->truncwidth = arg; + if (*bv->fm != ']') + bv->fm++; + while (*bv->fm && *bv->fm != truncchar) { + if (*bv->fm == '\\' && bv->fm[1]) + ++bv->fm; + addbufspc(1); + *bv->bp++ = *bv->fm++; + } + if (!*bv->fm) + return 0; + if (bv->bp - bv->buf == w && truncchar == ']') { + addbufspc(1); + *bv->bp++ = '<'; + } + ptr = bv->buf + w; /* addbufspc() may have realloc()'d bv->buf */ + /* + * Now: + * bv->buf is the start of the output prompt buffer + * ptr is the start of the truncation string + * bv->bp is the end of the truncation string + */ + truncstr = ztrduppfx(ptr, bv->bp - ptr); + + bv->bp = ptr; + w = bv->bp - bv->buf; + bv->fm++; + bv->trunccount = bv->dontcount; + putpromptchar(doprint, endchar, txtchangep); + bv->trunccount = 0; + ptr = bv->buf + w; /* putpromptchar() may have realloc()'d */ + *bv->bp = '\0'; + /* + * Now: + * ptr is the start of the truncation string and also + * where we need to start putting any truncated output + * bv->bp is the end of the string we have just added, which + * may need truncating. + */ + + /* + * w below is screen width if multibyte support is enabled + * (note that above it was a raw string pointer difference). + * It's the full width of the string we may need to truncate. + * + * bv->truncwidth has come from the user, so we interpret this + * as a screen width, too. + */ + countprompt(ptr, &w, 0, -1); + if (w > bv->truncwidth) { + /* + * We need to truncate. t points to the truncation string + * -- which is inserted literally, without nice + * representation. twidth is its printing width, and maxwidth + * is the amount of the main string that we want to keep. + * Note that if the truncation string is longer than the + * truncation length (twidth > bv->truncwidth), the truncation + * string is used in full. + */ + char *t = truncstr; + int fullen = bv->bp - ptr; + int twidth, maxwidth; + int ntrunc = strlen(t); + + twidth = MB_METASTRWIDTH(t); + if (twidth < bv->truncwidth) { + maxwidth = bv->truncwidth - twidth; + /* + * It's not safe to assume there are no invisible substrings + * just because the width is less than the full string + * length since there may be multibyte characters. + */ + addbufspc(ntrunc+1); + /* may have realloc'd */ + ptr = bv->bp - fullen; + + if (truncatleft) { + /* + * To truncate at the left, selectively copy + * maxwidth bytes from the main prompt, preceded + * by the truncation string in full. + * + * We're overwriting the string containing the + * text to be truncated, so copy it. We've + * just ensured there's sufficient space at the + * end of the prompt string. + * + * Pointer into text to be truncated. + */ + char *fulltextptr, *fulltext; + int remw; +#ifdef MULTIBYTE_SUPPORT + mbstate_t mbs; + memset(&mbs, 0, sizeof mbs); +#endif + + fulltextptr = fulltext = ptr + ntrunc; + memmove(fulltext, ptr, fullen); + fulltext[fullen] = '\0'; + + /* Copy the truncstr into place. */ + while (*t) + *ptr++ = *t++; + + /* + * Find the point in the text at which we should + * start copying, i.e. when the remaining width + * is less than or equal to the maximum width. + */ + remw = w; + while (remw > maxwidth && *fulltextptr) { + if (*fulltextptr == Inpar) { + /* + * Text marked as invisible: copy + * regardless, since we don't know what + * this does. It only affects the width + * if there are Nularg's present. + * However, even in that case we + * can't break the sequence down, so + * we still loop over the entire group. + */ + for (;;) { + *ptr++ = *fulltextptr; + if (*fulltextptr == '\0' || + *fulltextptr++ == Outpar) + break; + if (fulltextptr[-1] == Nularg) + remw--; + } + } else { +#ifdef MULTIBYTE_SUPPORT + /* + * Normal text: build up a multibyte character. + */ + char inchar; + wchar_t cc; + int wcw; + + /* + * careful: string is still metafied (we + * need that because we don't know a + * priori when to stop and the resulting + * string must be metafied). + */ + if (*fulltextptr == Meta) + inchar = *++fulltextptr ^ 32; + else + inchar = *fulltextptr; + fulltextptr++; + switch (mbrtowc(&cc, &inchar, 1, &mbs)) { + case MB_INCOMPLETE: + /* Incomplete multibyte character. */ + break; + case MB_INVALID: + /* Reset invalid state. */ + memset(&mbs, 0, sizeof mbs); + /* FALL THROUGH */ + case 0: + /* Assume a single-byte character. */ + remw--; + break; + default: + wcw = WCWIDTH(cc); + if (wcw >= 0) + remw -= wcw; + else + remw--; + break; + } +#else + /* Single byte character */ + if (*fulltextptr == Meta) + fulltextptr++; + fulltextptr++; + remw--; +#endif + } + } + + /* + * Now simply copy the rest of the text. Still + * metafied, so this is easy. + */ + while (*fulltextptr) + *ptr++ = *fulltextptr++; + /* Mark the end of copying */ + bv->bp = ptr; + } else { + /* + * Truncating at the right is easier: just leave + * enough characters until we have reached the + * maximum width. + */ + char *skiptext = ptr; +#ifdef MULTIBYTE_SUPPORT + mbstate_t mbs; + memset(&mbs, 0, sizeof mbs); +#endif + + while (maxwidth > 0 && *skiptext) { + if (*skiptext == Inpar) { + /* see comment on left truncation above */ + for (;;) { + if (*skiptext == '\0' || + *skiptext++ == Outpar) + break; + if (skiptext[-1] == Nularg) + maxwidth--; + } + } else { +#ifdef MULTIBYTE_SUPPORT + char inchar; + wchar_t cc; + int wcw; + + if (*skiptext == Meta) + inchar = *++skiptext ^ 32; + else + inchar = *skiptext; + skiptext++; + switch (mbrtowc(&cc, &inchar, 1, &mbs)) { + case MB_INCOMPLETE: + /* Incomplete character. */ + break; + case MB_INVALID: + /* Reset invalid state. */ + memset(&mbs, 0, sizeof mbs); + /* FALL THROUGH */ + case 0: + /* Assume a single-byte character. */ + maxwidth--; + break; + default: + wcw = WCWIDTH(cc); + if (wcw >= 0) + maxwidth -= wcw; + else + maxwidth--; + break; + } +#else + if (*skiptext == Meta) + skiptext++; + skiptext++; + maxwidth--; +#endif + } + } + /* + * We don't need the visible text from now on, + * but we'd better copy any invisible bits. + * History dictates that these go after the + * truncation string. This is sensible since + * they may, for example, turn off an effect which + * should apply to all text at this point. + * + * Copy the truncstr. + */ + ptr = skiptext; + while (*t) + *ptr++ = *t++; + bv->bp = ptr; + if (*skiptext) { + /* Move remaining text so we don't overwrite it */ + memmove(bv->bp, skiptext, strlen(skiptext)+1); + skiptext = bv->bp; + + /* + * Copy anything we want, updating bv->bp + */ + while (*skiptext) { + if (*skiptext == Inpar) { + for (;;) { + *bv->bp++ = *skiptext; + if (*skiptext == Outpar || + *skiptext == '\0') + break; + skiptext++; + } + } + else + skiptext++; + } + } + } + } else { + /* Just copy truncstr; no other text appears. */ + while (*t) + *ptr++ = *t++; + bv->bp = ptr; + } + *bv->bp = '\0'; + } + zsfree(truncstr); + bv->truncwidth = 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 (!*bv->fm) + return 0; + if (*bv->fm != endchar) { + bv->fm++; + /* + * With bv->truncwidth set to zero, we always reach endchar * + * (or the terminating NULL) this time round. * + */ + if (!putpromptchar(doprint, endchar, txtchangep)) + return 0; + } + /* Now we have to trick it into matching endchar again */ + bv->fm--; + } else { + if (*bv->fm != endchar) + bv->fm++; + while(*bv->fm && *bv->fm != truncchar) { + if (*bv->fm == '\\' && bv->fm[1]) + bv->fm++; + bv->fm++; + } + if (bv->truncwidth || !*bv->fm) + return 0; + } + return 1; +} + +/**/ +void +cmdpush(int cmdtok) +{ + if (cmdsp >= 0 && cmdsp < CMDSTACKSZ) + cmdstack[cmdsp++] = (unsigned char)cmdtok; +} + +/**/ +void +cmdpop(void) +{ + if (cmdsp <= 0) { + DPUTS(1, "BUG: cmdstack empty"); + fflush(stderr); + } else + cmdsp--; +} + + +/***************************************************************************** + * Utilities dealing with colour and other forms of highlighting. + * + * These are shared by prompts and by zle, so it's easiest to have them + * in the main shell. + *****************************************************************************/ + +/* Defines standard ANSI colour names in index order */ +static const char *ansi_colours[] = { + "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white", + "default", NULL +}; + +/* Defines the available types of highlighting */ +struct highlight { + const char *name; + int mask_on; + int mask_off; +}; + +static const struct highlight highlights[] = { + { "none", 0, TXT_ATTR_ON_MASK }, + { "bold", TXTBOLDFACE, 0 }, + { "standout", TXTSTANDOUT, 0 }, + { "underline", TXTUNDERLINE, 0 }, + { NULL, 0, 0 } +}; + +/* + * Return index of ANSI colour for which *teststrp is an abbreviation. + * Any non-alphabetic character ends the abbreviation. + * 8 is the special value for default (note this is *not* the + * right sequence for default which is typically 9). + * -1 is failure. + */ + +static int +match_named_colour(const char **teststrp) +{ + const char *teststr = *teststrp, *end, **cptr; + int len; + + for (end = teststr; ialpha(*end); end++) + ; + len = end - teststr; + *teststrp = end; + + for (cptr = ansi_colours; *cptr; cptr++) { + if (!strncmp(teststr, *cptr, len)) + return cptr - ansi_colours; + } + + return -1; +} + +/* + * Match just the colour part of a highlight specification. + * If teststrp is NULL, use the already parsed numeric colour. + * Return the attributes to set in the attribute variable. + * Return -1 for out of range. Does not check the character + * following the colour specification. + */ + +/**/ +mod_export int +match_colour(const char **teststrp, int is_fg, int colour) +{ + int shft, on, named = 0, tc; + + if (teststrp) { + if ((named = ialpha(**teststrp))) { + colour = match_named_colour(teststrp); + if (colour == 8) { + /* default */ + return is_fg ? TXTNOFGCOLOUR : TXTNOBGCOLOUR; + } + } + else + colour = (int)zstrtol(*teststrp, (char **)teststrp, 10); + } + if (colour < 0 || colour >= 256) + return -1; + if (is_fg) { + shft = TXT_ATTR_FG_COL_SHIFT; + on = TXTFGCOLOUR; + tc = TCFGCOLOUR; + } else { + shft = TXT_ATTR_BG_COL_SHIFT; + on = TXTBGCOLOUR; + tc = TCBGCOLOUR; + } + /* + * Try termcap for numbered characters if posible. + * Don't for named characters, since our best bet + * of getting the names right is with ANSI sequences. + */ + if (!named && tccan(tc)) { + if (tccolours >= 0 && colour >= tccolours) { + /* + * Out of range of termcap colours. + * Can we assume ANSI colours work? + */ + if (colour > 7) + return -1; /* No. */ + } else { + /* + * We can handle termcap colours and the number + * is in range, so use termcap. + */ + on |= is_fg ? TXT_ATTR_FG_TERMCAP : + TXT_ATTR_BG_TERMCAP; + } + } + return on | (colour << shft); +} + +/* + * Match a set of highlights in the given teststr. + * Set *on_var to reflect the values found. + */ + +/**/ +mod_export void +match_highlight(const char *teststr, int *on_var) +{ + int found = 1; + + *on_var = 0; + while (found && *teststr) { + const struct highlight *hl; + + found = 0; + if (strpfx("fg=", teststr) || strpfx("bg=", teststr)) { + int is_fg = (teststr[0] == 'f'), atr; + + teststr += 3; + atr = match_colour(&teststr, is_fg, 0); + if (*teststr == ',') + teststr++; + else if (*teststr) + break; + found = 1; + /* skip out of range colours but keep scanning attributes */ + if (atr >= 0) + *on_var |= atr; + } else { + for (hl = highlights; hl->name; hl++) { + if (strpfx(hl->name, teststr)) { + const char *val = teststr + strlen(hl->name); + + if (*val == ',') + val++; + else if (*val) + break; + + *on_var |= hl->mask_on; + *on_var &= ~hl->mask_off; + teststr = val; + found = 1; + } + } + } + } +} + +/* + * Count or output a string for colour information: used + * by output_highlight(). + */ + +static int +output_colour(int colour, int fg_bg, int use_tc, char *buf) +{ + int atrlen = 3, len; + char *ptr = buf; + if (buf) { + strcpy(ptr, fg_bg == COL_SEQ_FG ? "fg=" : "bg="); + ptr += 3; + } + /* colour should only be > 7 if using termcap but let's be safe */ + if (use_tc || colour > 7) { + char digbuf[DIGBUFSIZE]; + sprintf(digbuf, "%d", colour); + len = strlen(digbuf); + atrlen += len; + if (buf) + strcpy(ptr, digbuf); + } else { + len = strlen(ansi_colours[colour]); + atrlen += len; + if (buf) + strcpy(ptr, ansi_colours[colour]); + } + + return atrlen; +} + +/* + * Count the length needed for outputting highlighting information + * as a string based on the bits for the attributes. + * + * If buf is not NULL, output the strings into the buffer, too. + * As conventional with strings, the allocated length should be + * at least the returned value plus 1 for the NUL byte. + */ + +/**/ +mod_export int +output_highlight(int atr, char *buf) +{ + const struct highlight *hp; + int atrlen = 0, len; + char *ptr = buf; + + if (atr & TXTFGCOLOUR) { + len = output_colour(txtchangeget(atr, TXT_ATTR_FG_COL), + COL_SEQ_FG, + (atr & TXT_ATTR_FG_TERMCAP), + ptr); + atrlen += len; + if (buf) + ptr += len; + } + if (atr & TXTBGCOLOUR) { + if (atrlen) { + atrlen++; + if (buf) { + strcpy(ptr, ","); + ptr++; + } + } + len = output_colour(txtchangeget(atr, TXT_ATTR_BG_COL), + COL_SEQ_BG, + (atr & TXT_ATTR_BG_TERMCAP), + ptr); + atrlen += len; + if (buf) + ptr += len; + } + for (hp = highlights; hp->name; hp++) { + if (hp->mask_on & atr) { + if (atrlen) { + atrlen++; + if (buf) { + strcpy(ptr, ","); + ptr++; + } + } + len = strlen(hp->name); + atrlen += len; + if (buf) { + strcpy(ptr, hp->name); + ptr += len; + } + } + } + + if (atrlen == 0) { + if (buf) + strcpy(ptr, "none"); + return 4; + } + return atrlen; +} + +/* Structure and array for holding special colour terminal sequences */ + +/* Start of escape sequence for foreground colour */ +#define TC_COL_FG_START "\033[3" +/* End of escape sequence for foreground colour */ +#define TC_COL_FG_END "m" +/* Code to reset foreground colour */ +#define TC_COL_FG_DEFAULT "9" + +/* Start of escape sequence for background colour */ +#define TC_COL_BG_START "\033[4" +/* End of escape sequence for background colour */ +#define TC_COL_BG_END "m" +/* Code to reset background colour */ +#define TC_COL_BG_DEFAULT "9" + +struct colour_sequences { + char *start; /* Escape sequence start */ + char *end; /* Escape sequence terminator */ + char *def; /* Code to reset default colour */ +}; +static struct colour_sequences fg_bg_sequences[2]; + +/* + * We need a buffer for colour sequence composition. It may + * vary depending on the sequences set. However, it's inefficient + * allocating it separately every time we send a colour sequence, + * so do it once per refresh. + */ +static char *colseq_buf; + +/* + * Count how often this has been allocated, for recursive usage. + */ +static int colseq_buf_allocs; + +/**/ +void +set_default_colour_sequences(void) +{ + fg_bg_sequences[COL_SEQ_FG].start = ztrdup(TC_COL_FG_START); + fg_bg_sequences[COL_SEQ_FG].end = ztrdup(TC_COL_FG_END); + fg_bg_sequences[COL_SEQ_FG].def = ztrdup(TC_COL_FG_DEFAULT); + + fg_bg_sequences[COL_SEQ_BG].start = ztrdup(TC_COL_BG_START); + fg_bg_sequences[COL_SEQ_BG].end = ztrdup(TC_COL_BG_END); + fg_bg_sequences[COL_SEQ_BG].def = ztrdup(TC_COL_BG_DEFAULT); +} + +static void +set_colour_code(char *str, char **var) +{ + char *keyseq; + int len; + + zsfree(*var); + keyseq = getkeystring(str, &len, GETKEYS_BINDKEY, NULL); + *var = metafy(keyseq, len, META_DUP); +} + +/* Allocate buffer for colour code composition */ + +/**/ +mod_export void +allocate_colour_buffer(void) +{ + char **atrs; + int lenfg, lenbg, len; + + if (colseq_buf_allocs++) + return; + + atrs = getaparam("zle_highlight"); + if (atrs) { + for (; *atrs; atrs++) { + if (strpfx("fg_start_code:", *atrs)) { + set_colour_code(*atrs + 14, &fg_bg_sequences[COL_SEQ_FG].start); + } else if (strpfx("fg_default_code:", *atrs)) { + set_colour_code(*atrs + 16, &fg_bg_sequences[COL_SEQ_FG].def); + } else if (strpfx("fg_end_code:", *atrs)) { + set_colour_code(*atrs + 12, &fg_bg_sequences[COL_SEQ_FG].end); + } else if (strpfx("bg_start_code:", *atrs)) { + set_colour_code(*atrs + 14, &fg_bg_sequences[COL_SEQ_BG].start); + } else if (strpfx("bg_default_code:", *atrs)) { + set_colour_code(*atrs + 16, &fg_bg_sequences[COL_SEQ_BG].def); + } else if (strpfx("bg_end_code:", *atrs)) { + set_colour_code(*atrs + 12, &fg_bg_sequences[COL_SEQ_BG].end); + } + } + } + + lenfg = strlen(fg_bg_sequences[COL_SEQ_FG].def); + /* always need 1 character for non-default code */ + if (lenfg < 1) + lenfg = 1; + lenfg += strlen(fg_bg_sequences[COL_SEQ_FG].start) + + strlen(fg_bg_sequences[COL_SEQ_FG].end); + + lenbg = strlen(fg_bg_sequences[COL_SEQ_BG].def); + /* always need 1 character for non-default code */ + if (lenbg < 1) + lenbg = 1; + lenbg += strlen(fg_bg_sequences[COL_SEQ_BG].start) + + strlen(fg_bg_sequences[COL_SEQ_BG].end); + + len = lenfg > lenbg ? lenfg : lenbg; + colseq_buf = (char *)zalloc(len+1); +} + +/* Free the colour buffer previously allocated. */ + +/**/ +mod_export void +free_colour_buffer(void) +{ + if (--colseq_buf_allocs) + return; + + DPUTS(!colseq_buf, "Freeing colour sequence buffer without alloc"); + /* Free buffer for colour code composition */ + free(colseq_buf); + colseq_buf = NULL; +} + +/* + * Handle outputting of a colour for prompts or zle. + * colour is the numeric colour, 0 to 255 (or less if termcap + * says fewer are supported). + * fg_bg indicates if we're changing the foreground or background. + * tc indicates the termcap code to use, if appropriate. + * def indicates if we're resetting the default colour. + * use_termcap indicates if we should use termcap to output colours. + * flags is either 0 or TSC_PROMPT. + */ + +/**/ +mod_export void +set_colour_attribute(int atr, int fg_bg, int flags) +{ + char *ptr; + int do_free, is_prompt = (flags & TSC_PROMPT) ? 1 : 0; + int colour, tc, def, use_termcap; + + if (fg_bg == COL_SEQ_FG) { + colour = txtchangeget(atr, TXT_ATTR_FG_COL); + tc = TCFGCOLOUR; + def = txtchangeisset(atr, TXTNOFGCOLOUR); + use_termcap = txtchangeisset(atr, TXT_ATTR_FG_TERMCAP); + } else { + colour = txtchangeget(atr, TXT_ATTR_BG_COL); + tc = TCBGCOLOUR; + def = txtchangeisset(atr, TXTNOBGCOLOUR); + use_termcap = txtchangeisset(atr, TXT_ATTR_BG_TERMCAP); + } + + /* + * If we're not restoring the default, and either have a + * colour value that is too large for ANSI, or have been told + * to use the termcap sequence, try to use the termcap sequence. + * + * We have already sanitised the values we allow from the + * highlighting variables, so much of this shouldn't be + * necessary at this point, but we might as well be safe. + */ + if (!def && (colour > 7 || use_termcap)) { + /* + * We can if it's available, and either we couldn't get + * the maximum number of colours, or the colour is in range. + */ + if (tccan(tc) && (tccolours < 0 || colour < tccolours)) + { + if (is_prompt) + { + if (!bv->dontcount) { + addbufspc(1); + *bv->bp++ = Inpar; + } + tputs(tgoto(tcstr[tc], colour, colour), 1, putstr); + if (!bv->dontcount) { + addbufspc(1); + *bv->bp++ = Outpar; + } + } else { + tputs(tgoto(tcstr[tc], colour, colour), 1, putshout); + } + /* That worked. */ + return; + } + /* + * Nope, that didn't work. + * If 0 to 7, assume standard ANSI works, otherwise it won't. + */ + if (colour > 7) + return; + } + + if ((do_free = (colseq_buf == NULL))) { + /* This can happen when moving the cursor in trashzle() */ + allocate_colour_buffer(); + } + + strcpy(colseq_buf, fg_bg_sequences[fg_bg].start); + + ptr = colseq_buf + strlen(colseq_buf); + if (def) { + strcpy(ptr, fg_bg_sequences[fg_bg].def); + while (*ptr) + ptr++; + } else + *ptr++ = colour + '0'; + strcpy(ptr, fg_bg_sequences[fg_bg].end); + + if (is_prompt) { + if (!bv->dontcount) { + addbufspc(1); + *bv->bp++ = Inpar; + } + tputs(colseq_buf, 1, putstr); + if (!bv->dontcount) { + addbufspc(1); + *bv->bp++ = Outpar; + } + } else + tputs(colseq_buf, 1, putshout); + + if (do_free) + free_colour_buffer(); +} |
