summaryrefslogtreecommitdiff
path: root/dotfiles/system/.zsh/modules/Src/prompt.c
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-01-26 17:36:38 -0600
committerCraig Jennings <c@cjennings.net>2026-01-26 17:36:38 -0600
commitdada2f255daaa2fb493ec8c7d47e2a8123aea494 (patch)
tree0c0eeb84bb7b6e66a2d7f41cdfd061b25f80cc14 /dotfiles/system/.zsh/modules/Src/prompt.c
parentd50e5955837788fc69b4d5bc74cb574b859ed31a (diff)
refactor(dotfiles): rename system/ to common/ and remove unused configs
Rename dotfiles/system to dotfiles/common for clarity - indicates shared dotfiles used across all desktop environments (DWM, Hyprland). Removed config directories for uninstalled applications: - ghostty (using different terminal) - lf (using ranger instead) - mopidy (using mpd instead) - nitrogen (X11-only, obsolete for Wayland) - pychess (not installed) - JetBrains (not installed via archsetup) - youtube-dl (using yt-dlp with different config location) Kept audacious config for potential future use. Updated all references in archsetup, CLAUDE.md, todo.org, and validation.sh. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat (limited to 'dotfiles/system/.zsh/modules/Src/prompt.c')
-rw-r--r--dotfiles/system/.zsh/modules/Src/prompt.c2046
1 files changed, 0 insertions, 2046 deletions
diff --git a/dotfiles/system/.zsh/modules/Src/prompt.c b/dotfiles/system/.zsh/modules/Src/prompt.c
deleted file mode 100644
index 959ed8e..0000000
--- a/dotfiles/system/.zsh/modules/Src/prompt.c
+++ /dev/null
@@ -1,2046 +0,0 @@
-/*
- * 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();
-}