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