1/*
2 * prompt.c - construct zsh prompts
3 *
4 * This file is part of zsh, the Z shell.
5 *
6 * Copyright (c) 1992-1997 Paul Falstad
7 * All rights reserved.
8 *
9 * Permission is hereby granted, without written agreement and without
10 * license or royalty fees, to use, copy, modify, and distribute this
11 * software and to distribute modified versions of this software for any
12 * purpose, provided that the above copyright notice and the following
13 * two paragraphs appear in all copies of this software.
14 *
15 * In no event shall Paul Falstad or the Zsh Development Group be liable
16 * to any party for direct, indirect, special, incidental, or consequential
17 * damages arising out of the use of this software and its documentation,
18 * even if Paul Falstad and the Zsh Development Group have been advised of
19 * the possibility of such damage.
20 *
21 * Paul Falstad and the Zsh Development Group specifically disclaim any
22 * warranties, including, but not limited to, the implied warranties of
23 * merchantability and fitness for a particular purpose.  The software
24 * provided hereunder is on an "as is" basis, and Paul Falstad and the
25 * Zsh Development Group have no obligation to provide maintenance,
26 * support, updates, enhancements, or modifications.
27 *
28 */
29
30#include "zsh.mdh"
31#include "prompt.pro"
32
33/* text attribute mask */
34
35/**/
36mod_export unsigned txtattrmask;
37
38/* the command stack for use with %_ in prompts */
39
40/**/
41unsigned char *cmdstack;
42/**/
43int cmdsp;
44
45/* parser states, for %_ */
46
47static char *cmdnames[CS_COUNT] = {
48    "for",      "while",     "repeat",    "select",
49    "until",    "if",        "then",      "else",
50    "elif",     "math",      "cond",      "cmdor",
51    "cmdand",   "pipe",      "errpipe",   "foreach",
52    "case",     "function",  "subsh",     "cursh",
53    "array",    "quote",     "dquote",    "bquote",
54    "cmdsubst", "mathsubst", "elif-then", "heredoc",
55    "heredocd", "brace",     "braceparam", "always",
56};
57
58
59struct buf_vars;
60
61struct buf_vars {
62/* Previous set of prompt variables on the stack. */
63
64    struct buf_vars *last;
65
66/* The buffer into which an expanded and metafied prompt is being written, *
67 * and its size.                                                           */
68
69    char *buf;
70    int bufspc;
71
72/* bp is the pointer to the current position in the buffer, where the next *
73 * character will be added.                                                */
74
75    char *bp;
76
77/* Position of the start of the current line in the buffer */
78
79    char *bufline;
80
81/* bp1 is an auxiliary pointer into the buffer, which when non-NULL is *
82 * moved whenever the buffer is reallocated.  It is used when data is   *
83 * being temporarily held in the buffer.                                */
84
85    char *bp1;
86
87/* The format string, for %-expansion. */
88
89    char *fm;
90
91/* Non-zero if truncating the current segment of the buffer. */
92
93    int truncwidth;
94
95/* Current level of nesting of %{ / %} sequences. */
96
97    int dontcount;
98
99/* Level of %{ / %} surrounding a truncation segment. */
100
101    int trunccount;
102
103/* Strings to use for %r and %R (for the spelling prompt). */
104
105    char *rstring, *Rstring;
106};
107
108typedef struct buf_vars *Buf_vars;
109
110/* The currently active prompt output variables */
111static Buf_vars bv;
112
113/*
114 * Expand path p; maximum is npath segments where 0 means the whole path.
115 * If tilde is 1, try and find a named directory to use.
116 */
117
118static void
119promptpath(char *p, int npath, int tilde)
120{
121    char *modp = p;
122    Nameddir nd;
123
124    if (tilde && ((nd = finddir(p))))
125	modp = tricat("~", nd->node.nam, p + strlen(nd->dir));
126
127    if (npath) {
128	char *sptr;
129	if (npath > 0) {
130	    for (sptr = modp + strlen(modp); sptr > modp; sptr--) {
131		if (*sptr == '/' && !--npath) {
132		    sptr++;
133		    break;
134		}
135	    }
136	    if (*sptr == '/' && sptr[1] && sptr != modp)
137		sptr++;
138	    stradd(sptr);
139	} else {
140	    char cbu;
141	    for (sptr = modp+1; *sptr; sptr++)
142		if (*sptr == '/' && !++npath)
143		    break;
144	    cbu = *sptr;
145	    *sptr = 0;
146	    stradd(modp);
147	    *sptr = cbu;
148	}
149    } else
150	stradd(modp);
151
152    if (p != modp)
153	zsfree(modp);
154}
155
156/*
157 * Perform prompt expansion on a string, putting the result in a
158 * permanently-allocated string.  If ns is non-zero, this string
159 * may have embedded Inpar and Outpar, which indicate a toggling
160 * between spacing and non-spacing parts of the prompt, and
161 * Nularg, which (in a non-spacing sequence) indicates a
162 * `glitch' space.
163 *
164 * txtchangep gives an integer controlling the attributes of
165 * the prompt.  This is for use in zle to maintain the attributes
166 * consistenly.  Other parts of the shell should not need to use it.
167 */
168
169/**/
170mod_export char *
171promptexpand(char *s, int ns, char *rs, char *Rs, unsigned int *txtchangep)
172{
173    struct buf_vars new_vars;
174
175    if(!s)
176	return ztrdup("");
177
178    if ((termflags & TERM_UNKNOWN) && (unset(INTERACTIVE)))
179        init_term();
180
181    if (isset(PROMPTSUBST)) {
182	int olderr = errflag;
183	int oldval = lastval;
184
185	s = dupstring(s);
186	if (!parsestr(s))
187	    singsub(&s);
188	/*
189	 * We don't need the special Nularg hack here and we're
190	 * going to be using Nularg for other things.
191	 */
192	if (*s == Nularg && s[1] == '\0')
193	    *s = '\0';
194
195	/* Ignore errors and status change in prompt substitution */
196	errflag = olderr;
197	lastval = oldval;
198    }
199
200    memset(&new_vars, 0, sizeof(new_vars));
201    new_vars.last = bv;
202    bv = &new_vars;
203
204    new_vars.rstring = rs;
205    new_vars.Rstring = Rs;
206    new_vars.fm = s;
207    new_vars.bufspc = 256;
208    new_vars.bp = new_vars.bufline = new_vars.buf = zshcalloc(new_vars.bufspc);
209    new_vars.bp1 = NULL;
210    new_vars.truncwidth = 0;
211
212    putpromptchar(1, '\0', txtchangep);
213    addbufspc(2);
214    if (new_vars.dontcount)
215	*new_vars.bp++ = Outpar;
216    *new_vars.bp = '\0';
217    if (!ns) {
218	/* If zero, Inpar, Outpar and Nularg should be removed. */
219	for (new_vars.bp = new_vars.buf; *new_vars.bp; ) {
220	    if (*new_vars.bp == Meta)
221		new_vars.bp += 2;
222	    else if (*new_vars.bp == Inpar || *new_vars.bp == Outpar ||
223		     *new_vars.bp == Nularg)
224		chuck(new_vars.bp);
225	    else
226		new_vars.bp++;
227	}
228    }
229
230    bv = new_vars.last;
231
232    return new_vars.buf;
233}
234
235/* Perform %- and !-expansion as required on a section of the prompt.  The *
236 * section is ended by an instance of endchar.  If doprint is 0, the valid *
237 * % sequences are merely skipped over, and nothing is stored.             */
238
239/**/
240static int
241putpromptchar(int doprint, int endchar, unsigned int *txtchangep)
242{
243    char *ss, *hostnam;
244    int t0, arg, test, sep, j, numjobs;
245    struct tm *tm;
246    time_t timet;
247    Nameddir nd;
248
249    for (; *bv->fm && *bv->fm != endchar; bv->fm++) {
250	arg = 0;
251	if (*bv->fm == '%' && isset(PROMPTPERCENT)) {
252	    int minus = 0;
253	    bv->fm++;
254	    if (*bv->fm == '-') {
255		minus = 1;
256		bv->fm++;
257	    }
258	    if (idigit(*bv->fm)) {
259		arg = zstrtol(bv->fm, &bv->fm, 10);
260		if (minus)
261		    arg *= -1;
262	    } else if (minus)
263		arg = -1;
264	    if (*bv->fm == '(') {
265		int tc, otruncwidth;
266
267		if (idigit(*++bv->fm)) {
268		    arg = zstrtol(bv->fm, &bv->fm, 10);
269		} else if (arg < 0) {
270		    /* negative numbers don't make sense here */
271		    arg *= -1;
272		}
273		test = 0;
274		ss = pwd;
275		switch (tc = *bv->fm) {
276		case 'c':
277		case '.':
278		case '~':
279		    if ((nd = finddir(ss))) {
280			arg--;
281			ss += strlen(nd->dir);
282		    } /*FALLTHROUGH*/
283		case '/':
284		case 'C':
285		    /* `/' gives 0, `/any' gives 1, etc. */
286		    if (*ss++ == '/' && *ss)
287			arg--;
288		    for (; *ss; ss++)
289			if (*ss == '/')
290			    arg--;
291		    if (arg <= 0)
292			test = 1;
293		    break;
294		case 't':
295		case 'T':
296		case 'd':
297		case 'D':
298		case 'w':
299		    timet = time(NULL);
300		    tm = localtime(&timet);
301		    switch (tc) {
302		    case 't':
303			test = (arg == tm->tm_min);
304			break;
305		    case 'T':
306			test = (arg == tm->tm_hour);
307			break;
308		    case 'd':
309			test = (arg == tm->tm_mday);
310			break;
311		    case 'D':
312			test = (arg == tm->tm_mon);
313			break;
314		    case 'w':
315			test = (arg == tm->tm_wday);
316			break;
317		    }
318		    break;
319		case '?':
320		    if (lastval == arg)
321			test = 1;
322		    break;
323		case '#':
324		    if (geteuid() == (uid_t)arg)
325			test = 1;
326		    break;
327		case 'g':
328		    if (getegid() == (gid_t)arg)
329			test = 1;
330		    break;
331		case 'j':
332		    for (numjobs = 0, j = 1; j <= maxjob; j++)
333			if (jobtab[j].stat && jobtab[j].procs &&
334		    	    !(jobtab[j].stat & STAT_NOPRINT)) numjobs++;
335		    if (numjobs >= arg)
336		    	test = 1;
337		    break;
338		case 'l':
339		    *bv->bp = '\0';
340		    countprompt(bv->bufline, &t0, 0, 0);
341		    if (t0 >= arg)
342			test = 1;
343		    break;
344		case 'L':
345		    if (shlvl >= arg)
346			test = 1;
347		    break;
348		case 'S':
349		    if (time(NULL) - shtimer.tv_sec >= arg)
350			test = 1;
351		    break;
352		case 'v':
353		    if (arrlen(psvar) >= arg)
354			test = 1;
355		    break;
356		case 'V':
357		    if (arrlen(psvar) >= arg) {
358			if (*psvar[(arg ? arg : 1) - 1])
359			    test = 1;
360		    }
361		    break;
362		case '_':
363		    test = (cmdsp >= arg);
364		    break;
365		case '!':
366		    test = privasserted();
367		    break;
368		default:
369		    test = -1;
370		    break;
371		}
372		if (!*bv->fm || !(sep = *++bv->fm))
373		    return 0;
374		bv->fm++;
375		/* Don't do the current truncation until we get back */
376		otruncwidth = bv->truncwidth;
377		bv->truncwidth = 0;
378		if (!putpromptchar(test == 1 && doprint, sep,
379				   txtchangep) || !*++bv->fm ||
380		    !putpromptchar(test == 0 && doprint, ')',
381				   txtchangep)) {
382		    bv->truncwidth = otruncwidth;
383		    return 0;
384		}
385		bv->truncwidth = otruncwidth;
386		continue;
387	    }
388	    if (!doprint)
389		switch(*bv->fm) {
390		  case '[':
391		    while(idigit(*++bv->fm));
392		    while(*++bv->fm != ']');
393		    continue;
394		  case '<':
395		    while(*++bv->fm != '<');
396		    continue;
397		  case '>':
398		    while(*++bv->fm != '>');
399		    continue;
400		  case 'D':
401		    if(bv->fm[1]=='{')
402			while(*++bv->fm != '}');
403		    continue;
404		  default:
405		    continue;
406		}
407	    switch (*bv->fm) {
408	    case '~':
409		promptpath(pwd, arg, 1);
410		break;
411	    case 'd':
412	    case '/':
413		promptpath(pwd, arg, 0);
414		break;
415	    case 'c':
416	    case '.':
417		promptpath(pwd, arg ? arg : 1, 1);
418		break;
419	    case 'C':
420		promptpath(pwd, arg ? arg : 1, 0);
421		break;
422	    case 'N':
423		promptpath(scriptname ? scriptname : argzero, arg, 0);
424		break;
425	    case 'h':
426	    case '!':
427		addbufspc(DIGBUFSIZE);
428		convbase(bv->bp, curhist, 10);
429		bv->bp += strlen(bv->bp);
430		break;
431	    case 'j':
432		for (numjobs = 0, j = 1; j <= maxjob; j++)
433		    if (jobtab[j].stat && jobtab[j].procs &&
434		    	!(jobtab[j].stat & STAT_NOPRINT)) numjobs++;
435		addbufspc(DIGBUFSIZE);
436		sprintf(bv->bp, "%d", numjobs);
437		bv->bp += strlen(bv->bp);
438		break;
439	    case 'M':
440		queue_signals();
441		if ((hostnam = getsparam("HOST")))
442		    stradd(hostnam);
443		unqueue_signals();
444		break;
445	    case 'm':
446		if (!arg)
447		    arg++;
448		queue_signals();
449		if (!(hostnam = getsparam("HOST")))
450		    break;
451		if (arg < 0) {
452		    for (ss = hostnam + strlen(hostnam); ss > hostnam; ss--)
453			if (ss[-1] == '.' && !++arg)
454			    break;
455		    stradd(ss);
456		} else {
457		    for (ss = hostnam; *ss; ss++)
458			if (*ss == '.' && !--arg)
459			    break;
460		    stradd(*ss ? dupstrpfx(hostnam, ss - hostnam) : hostnam);
461		}
462		unqueue_signals();
463		break;
464	    case 'S':
465		txtchangeset(txtchangep, TXTSTANDOUT, TXTNOSTANDOUT);
466		txtset(TXTSTANDOUT);
467		tsetcap(TCSTANDOUTBEG, TSC_PROMPT);
468		break;
469	    case 's':
470		txtchangeset(txtchangep, TXTNOSTANDOUT, TXTSTANDOUT);
471		txtunset(TXTSTANDOUT);
472		tsetcap(TCSTANDOUTEND, TSC_PROMPT|TSC_DIRTY);
473		break;
474	    case 'B':
475		txtchangeset(txtchangep, TXTBOLDFACE, TXTNOBOLDFACE);
476		txtset(TXTBOLDFACE);
477		tsetcap(TCBOLDFACEBEG, TSC_PROMPT|TSC_DIRTY);
478		break;
479	    case 'b':
480		txtchangeset(txtchangep, TXTNOBOLDFACE, TXTBOLDFACE);
481		txtchangeset(txtchangep, TXTNOSTANDOUT, TXTSTANDOUT);
482		txtchangeset(txtchangep, TXTNOUNDERLINE, TXTUNDERLINE);
483		txtunset(TXTBOLDFACE);
484		tsetcap(TCALLATTRSOFF, TSC_PROMPT|TSC_DIRTY);
485		break;
486	    case 'U':
487		txtchangeset(txtchangep, TXTUNDERLINE, TXTNOUNDERLINE);
488		txtset(TXTUNDERLINE);
489		tsetcap(TCUNDERLINEBEG, TSC_PROMPT);
490		break;
491	    case 'u':
492		txtchangeset(txtchangep, TXTNOUNDERLINE, TXTUNDERLINE);
493		txtunset(TXTUNDERLINE);
494		tsetcap(TCUNDERLINEEND, TSC_PROMPT|TSC_DIRTY);
495		break;
496	    case 'F':
497		if (bv->fm[1] == '{') {
498		    bv->fm += 2;
499		    arg = match_colour((const char **)&bv->fm, 1, 0);
500		    if (*bv->fm != '}')
501			bv->fm--;
502		} else
503		    arg = match_colour(NULL, 1, arg);
504		if (arg >= 0 && !(arg & TXTNOFGCOLOUR)) {
505		    txtchangeset(txtchangep, arg & TXT_ATTR_FG_ON_MASK,
506				 TXTNOFGCOLOUR);
507		    txtset(arg & TXT_ATTR_FG_ON_MASK);
508		    set_colour_attribute(arg, COL_SEQ_FG, TSC_PROMPT);
509		    break;
510		}
511		/* else FALLTHROUGH */
512	    case 'f':
513		txtchangeset(txtchangep, TXTNOFGCOLOUR, TXT_ATTR_FG_ON_MASK);
514		txtunset(TXT_ATTR_FG_ON_MASK);
515		set_colour_attribute(TXTNOFGCOLOUR, COL_SEQ_FG, TSC_PROMPT);
516		break;
517	    case 'K':
518		if (bv->fm[1] == '{') {
519		    bv->fm += 2;
520		    arg = match_colour((const char **)&bv->fm, 0, 0);
521		    if (*bv->fm != '}')
522			bv->fm--;
523		} else
524		    arg = match_colour(NULL, 0, arg);
525		if (arg >= 0 && !(arg & TXTNOBGCOLOUR)) {
526		    txtchangeset(txtchangep, arg & TXT_ATTR_BG_ON_MASK,
527				 TXTNOBGCOLOUR);
528		    txtset(arg & TXT_ATTR_BG_ON_MASK);
529		    set_colour_attribute(arg, COL_SEQ_BG, TSC_PROMPT);
530		    break;
531		}
532		/* else FALLTHROUGH */
533	    case 'k':
534		txtchangeset(txtchangep, TXTNOBGCOLOUR, TXT_ATTR_BG_ON_MASK);
535		txtunset(TXT_ATTR_BG_ON_MASK);
536		set_colour_attribute(TXTNOBGCOLOUR, COL_SEQ_BG, TSC_PROMPT);
537		break;
538	    case '[':
539		if (idigit(*++bv->fm))
540		    arg = zstrtol(bv->fm, &bv->fm, 10);
541		if (!prompttrunc(arg, ']', doprint, endchar, txtchangep))
542		    return *bv->fm;
543		break;
544	    case '<':
545	    case '>':
546		if (!prompttrunc(arg, *bv->fm, doprint, endchar, txtchangep))
547		    return *bv->fm;
548		break;
549	    case '{': /*}*/
550		if (!bv->dontcount++) {
551		    addbufspc(1);
552		    *bv->bp++ = Inpar;
553		}
554		if (arg <= 0)
555		    break;
556		/* else */
557		/* FALLTHROUGH */
558	    case 'G':
559		if (arg > 0) {
560		    addbufspc(arg);
561		    while (arg--)
562			*bv->bp++ = Nularg;
563		} else {
564		    addbufspc(1);
565		    *bv->bp++ = Nularg;
566		}
567		break;
568	    case /*{*/ '}':
569		if (bv->trunccount && bv->trunccount >= bv->dontcount)
570		    return *bv->fm;
571		if (bv->dontcount && !--bv->dontcount) {
572		    addbufspc(1);
573		    *bv->bp++ = Outpar;
574		}
575		break;
576	    case 't':
577	    case '@':
578	    case 'T':
579	    case '*':
580	    case 'w':
581	    case 'W':
582	    case 'D':
583		{
584		    char *tmfmt, *dd, *tmbuf = NULL;
585
586		    switch (*bv->fm) {
587		    case 'T':
588			tmfmt = "%K:%M";
589			break;
590		    case '*':
591			tmfmt = "%K:%M:%S";
592			break;
593		    case 'w':
594			tmfmt = "%a %f";
595			break;
596		    case 'W':
597			tmfmt = "%m/%d/%y";
598			break;
599		    case 'D':
600			if (bv->fm[1] == '{' /*}*/) {
601			    for (ss = bv->fm + 2; *ss && *ss != /*{*/ '}'; ss++)
602				if(*ss == '\\' && ss[1])
603				    ss++;
604			    dd = tmfmt = tmbuf = zalloc(ss - bv->fm);
605			    for (ss = bv->fm + 2; *ss && *ss != /*{*/ '}';
606				 ss++) {
607				if(*ss == '\\' && ss[1])
608				    ss++;
609				*dd++ = *ss;
610			    }
611			    *dd = 0;
612			    bv->fm = ss - !*ss;
613			    if (!*tmfmt) {
614				free(tmbuf);
615				continue;
616			    }
617			} else
618			    tmfmt = "%y-%m-%d";
619			break;
620		    default:
621			tmfmt = "%l:%M%p";
622			break;
623		    }
624		    timet = time(NULL);
625		    tm = localtime(&timet);
626		    /*
627		     * Hack because strftime won't say how
628		     * much space it actually needs.  Try to add it
629		     * a few times until it works.  Some formats don't
630		     * actually have a length, so we could go on for
631		     * ever.
632		     */
633		    for(j = 0, t0 = strlen(tmfmt)*8; j < 3; j++, t0*=2) {
634			addbufspc(t0);
635			if (ztrftime(bv->bp, t0, tmfmt, tm) >= 0)
636			    break;
637		    }
638		    /* There is enough room for this because addbufspc(t0)
639		     * allocates room for t0 * 2 bytes. */
640		    metafy(bv->bp, -1, META_NOALLOC);
641		    bv->bp += strlen(bv->bp);
642		    zsfree(tmbuf);
643		    break;
644		}
645	    case 'n':
646		stradd(get_username());
647		break;
648	    case 'l':
649		if (*ttystrname) {
650                   ss = (strncmp(ttystrname, "/dev/tty", 8) ?
651                           ttystrname + 5 : ttystrname + 8);
652		    stradd(ss);
653		} else
654		    stradd("()");
655		break;
656	    case 'y':
657		if (*ttystrname) {
658		    ss = (strncmp(ttystrname, "/dev/", 5) ?
659			    ttystrname : ttystrname + 5);
660		    stradd(ss);
661		} else
662		    stradd("()");
663		break;
664	    case 'L':
665		addbufspc(DIGBUFSIZE);
666#if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD)
667		sprintf(bv->bp, "%lld", shlvl);
668#else
669		sprintf(bv->bp, "%ld", (long)shlvl);
670#endif
671		bv->bp += strlen(bv->bp);
672		break;
673	    case '?':
674		addbufspc(DIGBUFSIZE);
675#if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD)
676		sprintf(bv->bp, "%lld", lastval);
677#else
678		sprintf(bv->bp, "%ld", (long)lastval);
679#endif
680		bv->bp += strlen(bv->bp);
681		break;
682	    case '%':
683	    case ')':
684		addbufspc(1);
685		*bv->bp++ = *bv->fm;
686		break;
687	    case '#':
688		addbufspc(1);
689		*bv->bp++ = privasserted() ? '#' : '%';
690		break;
691	    case 'v':
692		if (!arg)
693		    arg = 1;
694		else if (arg < 0)
695		    arg += arrlen(psvar) + 1;
696		if (arg > 0 && arrlen(psvar) >= arg)
697		    stradd(psvar[arg - 1]);
698		break;
699	    case 'E':
700                tsetcap(TCCLEAREOL, TSC_PROMPT);
701		break;
702	    case '^':
703		if (cmdsp) {
704		    if (arg >= 0) {
705			if (arg > cmdsp || arg == 0)
706			    arg = cmdsp;
707			for (t0 = cmdsp - 1; arg--; t0--) {
708			    stradd(cmdnames[cmdstack[t0]]);
709			    if (arg) {
710				addbufspc(1);
711				*bv->bp++=' ';
712			    }
713			}
714		    } else {
715			arg = -arg;
716			if (arg > cmdsp)
717			    arg = cmdsp;
718			for (t0 = arg - 1; arg--; t0--) {
719			    stradd(cmdnames[cmdstack[t0]]);
720			    if (arg) {
721				addbufspc(1);
722				*bv->bp++=' ';
723			    }
724			}
725		    }
726		}
727		break;
728	    case '_':
729		if (cmdsp) {
730		    if (arg >= 0) {
731			if (arg > cmdsp || arg == 0)
732			    arg = cmdsp;
733			for (t0 = cmdsp - arg; arg--; t0++) {
734			    stradd(cmdnames[cmdstack[t0]]);
735			    if (arg) {
736				addbufspc(1);
737				*bv->bp++=' ';
738			    }
739			}
740		    } else {
741			arg = -arg;
742			if (arg > cmdsp)
743			    arg = cmdsp;
744			for (t0 = 0; arg--; t0++) {
745			    stradd(cmdnames[cmdstack[t0]]);
746			    if (arg) {
747				addbufspc(1);
748				*bv->bp++=' ';
749			    }
750			}
751		    }
752		}
753		break;
754	    case 'r':
755		if(bv->rstring)
756		    stradd(bv->rstring);
757		break;
758	    case 'R':
759		if(bv->Rstring)
760		    stradd(bv->Rstring);
761		break;
762	    case 'I':
763		if (funcstack && funcstack->tp != FS_SOURCE &&
764		    !IN_EVAL_TRAP()) {
765		    /*
766		     * We're in a function or an eval with
767		     * EVALLINENO.  Calculate the line number in
768		     * the file.
769		     */
770		    zlong flineno = lineno + funcstack->flineno;
771		    /* take account of eval line nos. starting at 1 */
772		    if (funcstack->tp == FS_EVAL)
773			lineno--;
774		    addbufspc(DIGBUFSIZE);
775#if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD)
776		    sprintf(bv->bp, "%lld", flineno);
777#else
778		    sprintf(bv->bp, "%ld", (long)flineno);
779#endif
780		    bv->bp += strlen(bv->bp);
781		    break;
782		}
783		/* else we're in a file and lineno is already correct */
784		/* FALLTHROUGH */
785	    case 'i':
786		addbufspc(DIGBUFSIZE);
787#if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD)
788		sprintf(bv->bp, "%lld", lineno);
789#else
790		sprintf(bv->bp, "%ld", (long)lineno);
791#endif
792		bv->bp += strlen(bv->bp);
793		break;
794	    case 'x':
795		if (funcstack && funcstack->tp != FS_SOURCE &&
796		    !IN_EVAL_TRAP())
797		    promptpath(funcstack->filename ? funcstack->filename : "",
798			       arg, 0);
799		else
800		    promptpath(scriptfilename ? scriptfilename : argzero,
801			       arg, 0);
802		break;
803	    case '\0':
804		return 0;
805	    case Meta:
806		bv->fm++;
807		break;
808	    }
809	} else if(*bv->fm == '!' && isset(PROMPTBANG)) {
810	    if(doprint) {
811		if(bv->fm[1] == '!') {
812		    bv->fm++;
813		    addbufspc(1);
814		    pputc('!');
815		} else {
816		    addbufspc(DIGBUFSIZE);
817		    convbase(bv->bp, curhist, 10);
818		    bv->bp += strlen(bv->bp);
819		}
820	    }
821	} else {
822	    char c = *bv->fm == Meta ? *++bv->fm ^ 32 : *bv->fm;
823
824	    if (doprint) {
825		addbufspc(1);
826		pputc(c);
827	    }
828	}
829    }
830
831    return *bv->fm;
832}
833
834/* pputc adds a character to the buffer, metafying.  There must *
835 * already be space.                                            */
836
837/**/
838static void
839pputc(char c)
840{
841    if (imeta(c)) {
842	*bv->bp++ = Meta;
843	c ^= 32;
844    }
845    *bv->bp++ = c;
846    if (c == '\n' && !bv->dontcount)
847	bv->bufline = bv->bp;
848}
849
850/* Make sure there is room for `need' more characters in the buffer. */
851
852/**/
853static void
854addbufspc(int need)
855{
856    need *= 2;   /* for metafication */
857    if((bv->bp - bv->buf) + need > bv->bufspc) {
858	int bo = bv->bp - bv->buf;
859	int bo1 = bv->bp1 ? bv->bp1 - bv->buf : -1;
860	ptrdiff_t bufline_off = bv->bufline ? bv->bufline - bv->buf : -1;
861
862	if(need & 255)
863	    need = (need | 255) + 1;
864	bv->buf = realloc(bv->buf, bv->bufspc += need);
865	bv->bp = bv->buf + bo;
866	if(bo1 != -1)
867	    bv->bp1 = bv->buf + bo1;
868	if (bufline_off != -1)
869	    bv->bufline = bv->buf + bufline_off;
870    }
871}
872
873/* stradd() adds a metafied string to the prompt, *
874 * in a visible representation.                   */
875
876/**/
877void
878stradd(char *d)
879{
880#ifdef MULTIBYTE_SUPPORT
881    char *ums, *ups;
882    int upslen, eol = 0;
883    mbstate_t mbs;
884
885    memset(&mbs, 0, sizeof mbs);
886    ums = ztrdup(d);
887    ups = unmetafy(ums, &upslen);
888
889    /*
890     * We now have a raw string of possibly multibyte characters.
891     * Read each character one by one.
892     */
893    while (upslen > 0) {
894	wchar_t cc;
895	char *pc;
896	size_t cnt = eol ? MB_INVALID : mbrtowc(&cc, ups, upslen, &mbs);
897
898	switch (cnt) {
899	case MB_INCOMPLETE:
900	    eol = 1;
901	    /* FALL THROUGH */
902	case MB_INVALID:
903	    /* Bad character.  Take the next byte on its own. */
904	    pc = nicechar(*ups);
905	    cnt = 1;
906	    memset(&mbs, 0, sizeof mbs);
907	    break;
908	case 0:
909	    cnt = 1;
910	    /* FALL THROUGH */
911	default:
912	    /* Take full wide character in one go */
913	    mb_metacharinit();
914	    pc = wcs_nicechar(cc, NULL, NULL);
915	    break;
916	}
917	/* Keep output as metafied string. */
918	addbufspc(strlen(pc));
919
920	upslen -= cnt;
921	ups += cnt;
922
923	/* Put printed representation into the buffer */
924	while (*pc)
925	    *bv->bp++ = *pc++;
926    }
927
928    free(ums);
929#else
930    char *ps, *pc;
931    addbufspc(niceztrlen(d));
932    /* This loop puts the nice representation of the string into the
933     * prompt buffer. */
934    for (ps = d; *ps; ps++) {
935	for (pc = nicechar(*ps == Meta ? *++ps^32 : *ps); *pc; pc++)
936	    *bv->bp++ = *pc;
937    }
938#endif
939}
940
941/* tsetcap(), among other things, can write a termcap string into the buffer. */
942
943/**/
944mod_export void
945tsetcap(int cap, int flags)
946{
947    if (tccan(cap) && !isset(SINGLELINEZLE) &&
948        !(termflags & (TERM_NOUP|TERM_BAD|TERM_UNKNOWN))) {
949	switch (flags & TSC_OUTPUT_MASK) {
950	case TSC_RAW:
951	    tputs(tcstr[cap], 1, putraw);
952	    break;
953	case 0:
954	default:
955	    tputs(tcstr[cap], 1, putshout);
956	    break;
957	case TSC_PROMPT:
958	    if (!bv->dontcount) {
959		addbufspc(1);
960		*bv->bp++ = Inpar;
961	    }
962	    tputs(tcstr[cap], 1, putstr);
963	    if (!bv->dontcount) {
964		int glitch = 0;
965
966		if (cap == TCSTANDOUTBEG || cap == TCSTANDOUTEND)
967		    glitch = tgetnum("sg");
968		else if (cap == TCUNDERLINEBEG || cap == TCUNDERLINEEND)
969		    glitch = tgetnum("ug");
970		if(glitch < 0)
971		    glitch = 0;
972		addbufspc(glitch + 1);
973		while(glitch--)
974		    *bv->bp++ = Nularg;
975		*bv->bp++ = Outpar;
976	    }
977	    break;
978	}
979
980	if (flags & TSC_DIRTY) {
981	    flags &= ~TSC_DIRTY;
982	    if (txtisset(TXTBOLDFACE) && cap != TCBOLDFACEBEG)
983		tsetcap(TCBOLDFACEBEG, flags);
984	    if (txtisset(TXTSTANDOUT))
985		tsetcap(TCSTANDOUTBEG, flags);
986	    if (txtisset(TXTUNDERLINE))
987		tsetcap(TCUNDERLINEBEG, flags);
988	}
989    }
990}
991
992/**/
993int
994putstr(int d)
995{
996    addbufspc(1);
997    pputc(d);
998    return 0;
999}
1000
1001/*
1002 * Count height etc. of a prompt string returned by promptexpand().
1003 * This depends on the current terminal width, and tabs and
1004 * newlines require nontrivial processing.
1005 * Passing `overf' as -1 means to ignore columns (absolute width).
1006 *
1007 * If multibyte is enabled, take account of multibyte characters
1008 * by locating them and finding out their screen width.
1009 */
1010
1011/**/
1012mod_export void
1013countprompt(char *str, int *wp, int *hp, int overf)
1014{
1015    int w = 0, h = 1;
1016    int s = 1;
1017#ifdef MULTIBYTE_SUPPORT
1018    int wcw, multi = 0;
1019    char inchar;
1020    mbstate_t mbs;
1021    wchar_t wc;
1022
1023    memset(&mbs, 0, sizeof(mbs));
1024#endif
1025
1026    for (; *str; str++) {
1027	if (w >= zterm_columns && overf >= 0) {
1028	    w = 0;
1029	    h++;
1030	}
1031	/*
1032	 * Input string should be metafied, so tokens in it should
1033	 * be real tokens, even if there are multibyte characters.
1034	 */
1035	if (*str == Inpar)
1036	    s = 0;
1037	else if (*str == Outpar)
1038	    s = 1;
1039	else if (*str == Nularg)
1040	    w++;
1041	else if (s) {
1042	    if (*str == Meta) {
1043#ifdef MULTIBYTE_SUPPORT
1044		inchar = *++str ^ 32;
1045#else
1046		str++;
1047#endif
1048	    } else {
1049#ifdef MULTIBYTE_SUPPORT
1050		/*
1051		 * Don't look for tab or newline in the middle
1052		 * of a multibyte character.  Otherwise, we are
1053		 * relying on the character set being an extension
1054		 * of ASCII so it's safe to test a single byte.
1055		 */
1056		if (!multi) {
1057#endif
1058		    if (*str == '\t') {
1059			w = (w | 7) + 1;
1060			continue;
1061		    } else if (*str == '\n') {
1062			w = 0;
1063			h++;
1064			continue;
1065		    }
1066#ifdef MULTIBYTE_SUPPORT
1067		}
1068
1069		inchar = *str;
1070#endif
1071	    }
1072
1073#ifdef MULTIBYTE_SUPPORT
1074	    switch (mbrtowc(&wc, &inchar, 1, &mbs)) {
1075	    case MB_INCOMPLETE:
1076		/* Character is incomplete -- keep looking. */
1077		multi = 1;
1078		break;
1079	    case MB_INVALID:
1080		memset(&mbs, 0, sizeof mbs);
1081		/* Invalid character: assume single width. */
1082		multi = 0;
1083		w++;
1084		break;
1085	    case 0:
1086		multi = 0;
1087		break;
1088	    default:
1089		/*
1090		 * If the character isn't printable, WCWIDTH() returns
1091		 * -1.  We assume width 1.
1092		 */
1093		wcw = WCWIDTH(wc);
1094		if (wcw >= 0)
1095		    w += wcw;
1096		else
1097		    w++;
1098		multi = 0;
1099		break;
1100	    }
1101#else
1102	    w++;
1103#endif
1104	}
1105    }
1106    /*
1107     * multi may still be set if we were in the middle of the character.
1108     * This isn't easy to handle generally; just assume there's no
1109     * output.
1110     */
1111    if(w >= zterm_columns && overf >= 0) {
1112	if (!overf || w > zterm_columns) {
1113	    w = 0;
1114	    h++;
1115	}
1116    }
1117    if(wp)
1118	*wp = w;
1119    if(hp)
1120	*hp = h;
1121}
1122
1123/**/
1124static int
1125prompttrunc(int arg, int truncchar, int doprint, int endchar,
1126	    unsigned int *txtchangep)
1127{
1128    if (arg > 0) {
1129	char ch = *bv->fm, *ptr, *truncstr;
1130	int truncatleft = ch == '<';
1131	int w = bv->bp - bv->buf;
1132
1133	/*
1134	 * If there is already a truncation active, return so that
1135	 * can be finished, backing up so that the new truncation
1136	 * can be started afterwards.
1137	 */
1138	if (bv->truncwidth) {
1139	    while (*--bv->fm != '%')
1140		;
1141	    bv->fm--;
1142	    return 0;
1143	}
1144
1145	bv->truncwidth = arg;
1146	if (*bv->fm != ']')
1147	    bv->fm++;
1148	while (*bv->fm && *bv->fm != truncchar) {
1149	    if (*bv->fm == '\\' && bv->fm[1])
1150		++bv->fm;
1151	    addbufspc(1);
1152	    *bv->bp++ = *bv->fm++;
1153	}
1154	if (!*bv->fm)
1155	    return 0;
1156	if (bv->bp - bv->buf == w && truncchar == ']') {
1157	    addbufspc(1);
1158	    *bv->bp++ = '<';
1159	}
1160	ptr = bv->buf + w;		/* addbv->bufspc() may have realloc()'d bv->buf */
1161	/*
1162	 * Now:
1163	 *   bv->buf is the start of the output prompt buffer
1164	 *   ptr is the start of the truncation string
1165	 *   bv->bp is the end of the truncation string
1166	 */
1167	truncstr = ztrduppfx(ptr, bv->bp - ptr);
1168
1169	bv->bp = ptr;
1170	w = bv->bp - bv->buf;
1171	bv->fm++;
1172	bv->trunccount = bv->dontcount;
1173	putpromptchar(doprint, endchar, txtchangep);
1174	bv->trunccount = 0;
1175	ptr = bv->buf + w;		/* putpromptchar() may have realloc()'d */
1176	*bv->bp = '\0';
1177	/*
1178	 * Now:
1179	 *   ptr is the start of the truncation string and also
1180	 *     where we need to start putting any truncated output
1181	 *   bv->bp is the end of the string we have just added, which
1182	 *     may need truncating.
1183	 */
1184
1185	/*
1186	 * w below is screen width if multibyte support is enabled
1187	 * (note that above it was a raw string pointer difference).
1188	 * It's the full width of the string we may need to truncate.
1189	 *
1190	 * bv->truncwidth has come from the user, so we interpret this
1191	 * as a screen width, too.
1192	 */
1193	countprompt(ptr, &w, 0, -1);
1194	if (w > bv->truncwidth) {
1195	    /*
1196	     * We need to truncate.  t points to the truncation string
1197	     * -- which is inserted literally, without nice
1198	     * representation.  twidth is its printing width, and maxwidth
1199	     * is the amount of the main string that we want to keep.
1200	     * Note that if the truncation string is longer than the
1201	     * truncation length (twidth > bv->truncwidth), the truncation
1202	     * string is used in full.
1203	     */
1204	    char *t = truncstr;
1205	    int fullen = bv->bp - ptr;
1206	    int twidth, maxwidth;
1207	    int ntrunc = strlen(t);
1208
1209	    twidth = MB_METASTRWIDTH(t);
1210	    if (twidth < bv->truncwidth) {
1211		maxwidth = bv->truncwidth - twidth;
1212		/*
1213		 * It's not safe to assume there are no invisible substrings
1214		 * just because the width is less than the full string
1215		 * length since there may be multibyte characters.
1216		 */
1217		addbufspc(ntrunc+1);
1218		/* may have realloc'd */
1219		ptr = bv->bp - fullen;
1220
1221		if (truncatleft) {
1222		    /*
1223		     * To truncate at the left, selectively copy
1224		     * maxwidth bytes from the main prompt, preceded
1225		     * by the truncation string in full.
1226		     *
1227		     * We're overwriting the string containing the
1228		     * text to be truncated, so copy it.  We've
1229		     * just ensured there's sufficient space at the
1230		     * end of the prompt string.
1231		     *
1232		     * Pointer into text to be truncated.
1233		     */
1234		    char *fulltextptr, *fulltext;
1235		    int remw;
1236#ifdef MULTIBYTE_SUPPORT
1237		    mbstate_t mbs;
1238		    memset(&mbs, 0, sizeof mbs);
1239#endif
1240
1241		    fulltextptr = fulltext = ptr + ntrunc;
1242		    memmove(fulltext, ptr, fullen);
1243		    fulltext[fullen] = '\0';
1244
1245		    /* Copy the truncstr into place. */
1246		    while (*t)
1247			*ptr++ = *t++;
1248
1249		    /*
1250		     * Find the point in the text at which we should
1251		     * start copying, i.e. when the remaining width
1252		     * is less than or equal to the maximum width.
1253		     */
1254		    remw = w;
1255		    while (remw > maxwidth && *fulltextptr) {
1256			if (*fulltextptr == Inpar) {
1257			    /*
1258			     * Text marked as invisible: copy
1259			     * regardless, since we don't know what
1260			     * this does.  It only affects the width
1261			     * if there are Nularg's present.
1262			     * However, even in that case we
1263			     * can't break the sequence down, so
1264			     * we still loop over the entire group.
1265			     */
1266			    for (;;) {
1267				*ptr++ = *fulltextptr;
1268				if (*fulltextptr == Outpar ||
1269				    *fulltextptr == '\0')
1270				    break;
1271				if (*fulltextptr == Nularg)
1272				    remw--;
1273				fulltextptr++;
1274			    }
1275			} else {
1276#ifdef MULTIBYTE_SUPPORT
1277			    /*
1278			     * Normal text: build up a multibyte character.
1279			     */
1280			    char inchar;
1281			    wchar_t cc;
1282			    int wcw;
1283
1284			    /*
1285			     * careful: string is still metafied (we
1286			     * need that because we don't know a
1287			     * priori when to stop and the resulting
1288			     * string must be metafied).
1289			     */
1290			    if (*fulltextptr == Meta)
1291				inchar = *++fulltextptr ^ 32;
1292			    else
1293				inchar = *fulltextptr;
1294			    fulltextptr++;
1295			    switch (mbrtowc(&cc, &inchar, 1, &mbs)) {
1296			    case MB_INCOMPLETE:
1297				/* Incomplete multibyte character. */
1298				break;
1299			    case MB_INVALID:
1300				/* Reset invalid state. */
1301				memset(&mbs, 0, sizeof mbs);
1302				/* FALL THROUGH */
1303			    case 0:
1304				/* Assume a single-byte character. */
1305				remw--;
1306				break;
1307			    default:
1308				wcw = WCWIDTH(cc);
1309				if (wcw >= 0)
1310				    remw -= wcw;
1311				else
1312				    remw--;
1313				break;
1314			    }
1315#else
1316			    /* Single byte character */
1317			    if (*fulltextptr == Meta)
1318				fulltextptr++;
1319			    fulltextptr++;
1320			    remw--;
1321#endif
1322			}
1323		    }
1324
1325		    /*
1326		     * Now simply copy the rest of the text.  Still
1327		     * metafied, so this is easy.
1328		     */
1329		    while (*fulltextptr)
1330			*ptr++ = *fulltextptr++;
1331		    /* Mark the end of copying */
1332		    bv->bp = ptr;
1333		} else {
1334		    /*
1335		     * Truncating at the right is easier: just leave
1336		     * enough characters until we have reached the
1337		     * maximum width.
1338		     */
1339		    char *skiptext = ptr;
1340#ifdef MULTIBYTE_SUPPORT
1341		    mbstate_t mbs;
1342		    memset(&mbs, 0, sizeof mbs);
1343#endif
1344
1345		    while (maxwidth > 0 && *skiptext) {
1346			if (*skiptext == Inpar) {
1347			    /* see comment on left truncation above */
1348			    for (;;) {
1349				if (*skiptext == Outpar ||
1350				    *skiptext == '\0')
1351				    break;
1352				if (*skiptext == Nularg)
1353				    maxwidth--;
1354				skiptext++;
1355			    }
1356			} else {
1357#ifdef MULTIBYTE_SUPPORT
1358			    char inchar;
1359			    wchar_t cc;
1360			    int wcw;
1361
1362			    if (*skiptext == Meta)
1363				inchar = *++skiptext ^ 32;
1364			    else
1365				inchar = *skiptext;
1366			    skiptext++;
1367			    switch (mbrtowc(&cc, &inchar, 1, &mbs)) {
1368			    case MB_INCOMPLETE:
1369				/* Incomplete character. */
1370				break;
1371			    case MB_INVALID:
1372				/* Reset invalid state. */
1373				memset(&mbs, 0, sizeof mbs);
1374				/* FALL THROUGH */
1375			    case 0:
1376				/* Assume a single-byte character. */
1377				maxwidth--;
1378				break;
1379			    default:
1380				wcw = WCWIDTH(cc);
1381				if (wcw >= 0)
1382				    maxwidth -= wcw;
1383				else
1384				    maxwidth--;
1385				break;
1386			    }
1387#else
1388			    if (*skiptext == Meta)
1389				skiptext++;
1390			    skiptext++;
1391			    maxwidth--;
1392#endif
1393			}
1394		    }
1395		    /*
1396		     * We don't need the visible text from now on,
1397		     * but we'd better copy any invisible bits.
1398		     * History dictates that these go after the
1399		     * truncation string.  This is sensible since
1400		     * they may, for example, turn off an effect which
1401		     * should apply to all text at this point.
1402		     *
1403		     * Copy the truncstr.
1404		     */
1405		    ptr = skiptext;
1406		    while (*t)
1407			*ptr++ = *t++;
1408		    bv->bp = ptr;
1409		    if (*skiptext) {
1410			/* Move remaining text so we don't overwrite it */
1411			memmove(bv->bp, skiptext, strlen(skiptext)+1);
1412			skiptext = bv->bp;
1413
1414			/*
1415			 * Copy anything we want, updating bv->bp
1416			 */
1417			while (*skiptext) {
1418			    if (*skiptext == Inpar) {
1419				for (;;) {
1420				    *bv->bp++ = *skiptext;
1421				    if (*skiptext == Outpar ||
1422					*skiptext == '\0')
1423					break;
1424				    skiptext++;
1425				}
1426			    }
1427			    else
1428				skiptext++;
1429			}
1430		    }
1431		}
1432	    } else {
1433		/* Just copy truncstr; no other text appears. */
1434		while (*t)
1435		    *ptr++ = *t++;
1436		bv->bp = ptr;
1437	    }
1438	    *bv->bp = '\0';
1439	}
1440	zsfree(truncstr);
1441	bv->truncwidth = 0;
1442	/*
1443	 * We may have returned early from the previous putpromptchar *
1444	 * because we found another truncation following this one.    *
1445	 * In that case we need to do the rest now.                   *
1446	 */
1447	if (!*bv->fm)
1448	    return 0;
1449	if (*bv->fm != endchar) {
1450	    bv->fm++;
1451	    /*
1452	     * With bv->truncwidth set to zero, we always reach endchar *
1453	     * (or the terminating NULL) this time round.         *
1454	     */
1455	    if (!putpromptchar(doprint, endchar, txtchangep))
1456		return 0;
1457	}
1458	/* Now we have to trick it into matching endchar again */
1459	bv->fm--;
1460    } else {
1461	if (*bv->fm != ']')
1462	    bv->fm++;
1463	while(*bv->fm && *bv->fm != truncchar) {
1464	    if (*bv->fm == '\\' && bv->fm[1])
1465		bv->fm++;
1466	    bv->fm++;
1467	}
1468	if (bv->truncwidth || !*bv->fm)
1469	    return 0;
1470    }
1471    return 1;
1472}
1473
1474/**/
1475void
1476cmdpush(int cmdtok)
1477{
1478    if (cmdsp >= 0 && cmdsp < CMDSTACKSZ)
1479	cmdstack[cmdsp++] = (unsigned char)cmdtok;
1480}
1481
1482/**/
1483void
1484cmdpop(void)
1485{
1486    if (cmdsp <= 0) {
1487	DPUTS(1, "BUG: cmdstack empty");
1488	fflush(stderr);
1489    } else
1490	cmdsp--;
1491}
1492
1493
1494/*****************************************************************************
1495 * Utilities dealing with colour and other forms of highlighting.
1496 *
1497 * These are shared by prompts and by zle, so it's easiest to have them
1498 * in the main shell.
1499 *****************************************************************************/
1500
1501/* Defines standard ANSI colour names in index order */
1502static const char *ansi_colours[] = {
1503    "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white",
1504    "default", NULL
1505};
1506
1507/* Defines the available types of highlighting */
1508struct highlight {
1509    const char *name;
1510    int mask_on;
1511    int mask_off;
1512};
1513
1514static const struct highlight highlights[] = {
1515    { "none", 0, TXT_ATTR_ON_MASK },
1516    { "bold", TXTBOLDFACE, 0 },
1517    { "standout", TXTSTANDOUT, 0 },
1518    { "underline", TXTUNDERLINE, 0 },
1519    { NULL, 0, 0 }
1520};
1521
1522/*
1523 * Return index of ANSI colour for which *teststrp is an abbreviation.
1524 * Any non-alphabetic character ends the abbreviation.
1525 * 8 is the special value for default (note this is *not* the
1526 * right sequence for default which is typically 9).
1527 * -1 is failure.
1528 */
1529
1530static int
1531match_named_colour(const char **teststrp)
1532{
1533    const char *teststr = *teststrp, *end, **cptr;
1534    int len;
1535
1536    for (end = teststr; ialpha(*end); end++)
1537	;
1538    len = end - teststr;
1539    *teststrp = end;
1540
1541    for (cptr = ansi_colours; *cptr; cptr++) {
1542	if (!strncmp(teststr, *cptr, len))
1543	    return cptr - ansi_colours;
1544    }
1545
1546    return -1;
1547}
1548
1549/*
1550 * Match just the colour part of a highlight specification.
1551 * If teststrp is NULL, use the already parsed numeric colour.
1552 * Return the attributes to set in the attribute variable.
1553 * Return -1 for out of range.  Does not check the character
1554 * following the colour specification.
1555 */
1556
1557/**/
1558mod_export int
1559match_colour(const char **teststrp, int is_fg, int colour)
1560{
1561    int shft, on, named = 0, tc;
1562
1563    if (teststrp) {
1564	if ((named = ialpha(**teststrp))) {
1565	    colour = match_named_colour(teststrp);
1566	    if (colour == 8) {
1567		/* default */
1568		return is_fg ? TXTNOFGCOLOUR : TXTNOBGCOLOUR;
1569	    }
1570	}
1571	else
1572	    colour = (int)zstrtol(*teststrp, (char **)teststrp, 10);
1573    }
1574    if (colour < 0 || colour >= 256)
1575	return -1;
1576    if (is_fg) {
1577	shft = TXT_ATTR_FG_COL_SHIFT;
1578	on = TXTFGCOLOUR;
1579	tc = TCFGCOLOUR;
1580    } else {
1581	shft = TXT_ATTR_BG_COL_SHIFT;
1582	on = TXTBGCOLOUR;
1583	tc = TCBGCOLOUR;
1584    }
1585    /*
1586     * Try termcap for numbered characters if posible.
1587     * Don't for named characters, since our best bet
1588     * of getting the names right is with ANSI sequences.
1589     */
1590    if (!named && tccan(tc)) {
1591	if (tccolours >= 0 && colour >= tccolours) {
1592	    /*
1593	     * Out of range of termcap colours.
1594	     * Can we assume ANSI colours work?
1595	     */
1596	    if (colour > 7)
1597		return -1; /* No. */
1598	} else {
1599	    /*
1600	     * We can handle termcap colours and the number
1601	     * is in range, so use termcap.
1602	     */
1603	    on |= is_fg ? TXT_ATTR_FG_TERMCAP :
1604		TXT_ATTR_BG_TERMCAP;
1605	}
1606    }
1607    return on | (colour << shft);
1608}
1609
1610/*
1611 * Match a set of highlights in the given teststr.
1612 * Set *on_var to reflect the values found.
1613 */
1614
1615/**/
1616mod_export void
1617match_highlight(const char *teststr, int *on_var)
1618{
1619    int found = 1;
1620
1621    *on_var = 0;
1622    while (found && *teststr) {
1623	const struct highlight *hl;
1624
1625	found = 0;
1626	if (strpfx("fg=", teststr) || strpfx("bg=", teststr)) {
1627	    int is_fg = (teststr[0] == 'f'), atr;
1628
1629	    teststr += 3;
1630	    atr = match_colour(&teststr, is_fg, 0);
1631	    if (*teststr == ',')
1632		teststr++;
1633	    else if (*teststr)
1634		break;
1635	    found = 1;
1636	    /* skip out of range colours but keep scanning attributes */
1637	    if (atr >= 0)
1638		*on_var |= atr;
1639	} else {
1640	    for (hl = highlights; hl->name; hl++) {
1641		if (strpfx(hl->name, teststr)) {
1642		    const char *val = teststr + strlen(hl->name);
1643
1644		    if (*val == ',')
1645			val++;
1646		    else if (*val)
1647			break;
1648
1649		    *on_var |= hl->mask_on;
1650		    *on_var &= ~hl->mask_off;
1651		    teststr = val;
1652		    found = 1;
1653		}
1654	    }
1655	}
1656    }
1657}
1658
1659/*
1660 * Count or output a string for colour information: used
1661 * by output_highlight().
1662 */
1663
1664static int
1665output_colour(int colour, int fg_bg, int use_tc, char *buf)
1666{
1667    int atrlen = 3, len;
1668    char *ptr = buf;
1669    if (buf) {
1670	strcpy(ptr, fg_bg == COL_SEQ_FG ? "fg=" : "bg=");
1671	ptr += 3;
1672    }
1673    /* colour should only be > 7 if using termcap but let's be safe */
1674    if (use_tc || colour > 7) {
1675	char digbuf[DIGBUFSIZE];
1676	sprintf(digbuf, "%d", colour);
1677	len = strlen(digbuf);
1678	atrlen += len;
1679	if (buf)
1680	    strcpy(ptr, digbuf);
1681    } else {
1682	len = strlen(ansi_colours[colour]);
1683	atrlen += len;
1684	if (buf)
1685	    strcpy(ptr, ansi_colours[colour]);
1686    }
1687
1688    return atrlen;
1689}
1690
1691/*
1692 * Count the length needed for outputting highlighting information
1693 * as a string based on the bits for the attributes.
1694 *
1695 * If buf is not NULL, output the strings into the buffer, too.
1696 * As conventional with strings, the allocated length should be
1697 * at least the returned value plus 1 for the NUL byte.
1698 */
1699
1700/**/
1701mod_export int
1702output_highlight(int atr, char *buf)
1703{
1704    const struct highlight *hp;
1705    int atrlen = 0, len;
1706    char *ptr = buf;
1707
1708    if (atr & TXTFGCOLOUR) {
1709	len = output_colour(txtchangeget(atr, TXT_ATTR_FG_COL),
1710			    COL_SEQ_FG,
1711			    (atr & TXT_ATTR_FG_TERMCAP),
1712			    ptr);
1713	atrlen += len;
1714	if (buf)
1715	    ptr += len;
1716    }
1717    if (atr & TXTBGCOLOUR) {
1718	if (atrlen) {
1719	    atrlen++;
1720	    if (buf) {
1721		strcpy(ptr, ",");
1722		ptr++;
1723	    }
1724	}
1725	len = output_colour(txtchangeget(atr, TXT_ATTR_BG_COL),
1726			    COL_SEQ_BG,
1727			    (atr & TXT_ATTR_BG_TERMCAP),
1728			    ptr);
1729	atrlen += len;
1730	if (buf)
1731	    ptr += len;
1732    }
1733    for (hp = highlights; hp->name; hp++) {
1734	if (hp->mask_on & atr) {
1735	    if (atrlen) {
1736		atrlen++;
1737		if (buf) {
1738		    strcpy(ptr, ",");
1739		    ptr++;
1740		}
1741	    }
1742	    len = strlen(hp->name);
1743	    atrlen += len;
1744	    if (buf) {
1745		strcpy(ptr, hp->name);
1746		ptr += len;
1747	    }
1748	}
1749    }
1750
1751    if (atrlen == 0) {
1752	if (buf)
1753	    strcpy(ptr, "none");
1754	return 4;
1755    }
1756    return atrlen;
1757}
1758
1759/* Structure and array for holding special colour terminal sequences */
1760
1761/* Start of escape sequence for foreground colour */
1762#define TC_COL_FG_START	"\033[3"
1763/* End of escape sequence for foreground colour */
1764#define TC_COL_FG_END	"m"
1765/* Code to reset foreground colour */
1766#define TC_COL_FG_DEFAULT	"9"
1767
1768/* Start of escape sequence for background colour */
1769#define TC_COL_BG_START	"\033[4"
1770/* End of escape sequence for background colour */
1771#define TC_COL_BG_END	"m"
1772/* Code to reset background colour */
1773#define TC_COL_BG_DEFAULT	"9"
1774
1775struct colour_sequences {
1776    char *start;		/* Escape sequence start */
1777    char *end;			/* Escape sequence terminator */
1778    char *def;			/* Code to reset default colour */
1779};
1780struct colour_sequences fg_bg_sequences[2];
1781
1782/*
1783 * We need a buffer for colour sequence composition.  It may
1784 * vary depending on the sequences set.  However, it's inefficient
1785 * allocating it separately every time we send a colour sequence,
1786 * so do it once per refresh.
1787 */
1788static char *colseq_buf;
1789
1790/*
1791 * Count how often this has been allocated, for recursive usage.
1792 */
1793static int colseq_buf_allocs;
1794
1795/**/
1796void
1797set_default_colour_sequences(void)
1798{
1799    fg_bg_sequences[COL_SEQ_FG].start = ztrdup(TC_COL_FG_START);
1800    fg_bg_sequences[COL_SEQ_FG].end = ztrdup(TC_COL_FG_END);
1801    fg_bg_sequences[COL_SEQ_FG].def = ztrdup(TC_COL_FG_DEFAULT);
1802
1803    fg_bg_sequences[COL_SEQ_BG].start = ztrdup(TC_COL_BG_START);
1804    fg_bg_sequences[COL_SEQ_BG].end = ztrdup(TC_COL_BG_END);
1805    fg_bg_sequences[COL_SEQ_BG].def = ztrdup(TC_COL_BG_DEFAULT);
1806}
1807
1808static void
1809set_colour_code(char *str, char **var)
1810{
1811    char *keyseq;
1812    int len;
1813
1814    zsfree(*var);
1815    keyseq = getkeystring(str, &len, GETKEYS_BINDKEY, NULL);
1816    *var = metafy(keyseq, len, META_DUP);
1817}
1818
1819/* Allocate buffer for colour code composition */
1820
1821/**/
1822mod_export void
1823allocate_colour_buffer(void)
1824{
1825    char **atrs;
1826    int lenfg, lenbg, len;
1827
1828    if (colseq_buf_allocs++)
1829	return;
1830
1831    atrs = getaparam("zle_highlight");
1832    if (atrs) {
1833	for (; *atrs; atrs++) {
1834	    if (strpfx("fg_start_code:", *atrs)) {
1835		set_colour_code(*atrs + 14, &fg_bg_sequences[COL_SEQ_FG].start);
1836	    } else if (strpfx("fg_default_code:", *atrs)) {
1837		set_colour_code(*atrs + 16, &fg_bg_sequences[COL_SEQ_FG].def);
1838	    } else if (strpfx("fg_end_code:", *atrs)) {
1839		set_colour_code(*atrs + 12, &fg_bg_sequences[COL_SEQ_FG].end);
1840	    } else if (strpfx("bg_start_code:", *atrs)) {
1841		set_colour_code(*atrs + 14, &fg_bg_sequences[COL_SEQ_BG].start);
1842	    } else if (strpfx("bg_default_code:", *atrs)) {
1843		set_colour_code(*atrs + 16, &fg_bg_sequences[COL_SEQ_BG].def);
1844	    } else if (strpfx("bg_end_code:", *atrs)) {
1845		set_colour_code(*atrs + 12, &fg_bg_sequences[COL_SEQ_BG].end);
1846	    }
1847	}
1848    }
1849
1850    lenfg = strlen(fg_bg_sequences[COL_SEQ_FG].def);
1851    /* always need 1 character for non-default code */
1852    if (lenfg < 1)
1853	lenfg = 1;
1854    lenfg += strlen(fg_bg_sequences[COL_SEQ_FG].start) +
1855	strlen(fg_bg_sequences[COL_SEQ_FG].end);
1856
1857    lenbg = strlen(fg_bg_sequences[COL_SEQ_BG].def);
1858    /* always need 1 character for non-default code */
1859    if (lenbg < 1)
1860	lenbg = 1;
1861    lenbg += strlen(fg_bg_sequences[COL_SEQ_BG].start) +
1862	strlen(fg_bg_sequences[COL_SEQ_BG].end);
1863
1864    len = lenfg > lenbg ? lenfg : lenbg;
1865    colseq_buf = (char *)zalloc(len+1);
1866}
1867
1868/* Free the colour buffer previously allocated. */
1869
1870/**/
1871mod_export void
1872free_colour_buffer(void)
1873{
1874    if (--colseq_buf_allocs)
1875	return;
1876
1877    DPUTS(!colseq_buf, "Freeing colour sequence buffer without alloc");
1878    /* Free buffer for colour code composition */
1879    free(colseq_buf);
1880    colseq_buf = NULL;
1881}
1882
1883/*
1884 * Handle outputting of a colour for prompts or zle.
1885 * colour is the numeric colour, 0 to 255 (or less if termcap
1886 * says fewer are supported).
1887 * fg_bg indicates if we're changing the foreground or background.
1888 * tc indicates the termcap code to use, if appropriate.
1889 * def indicates if we're resetting the default colour.
1890 * use_termcap indicates if we should use termcap to output colours.
1891 * flags is either 0 or TSC_PROMPT.
1892 */
1893
1894/**/
1895mod_export void
1896set_colour_attribute(int atr, int fg_bg, int flags)
1897{
1898    char *ptr;
1899    int do_free, is_prompt = (flags & TSC_PROMPT) ? 1 : 0;
1900    int colour, tc, def, use_termcap;
1901
1902    if (fg_bg == COL_SEQ_FG) {
1903	colour = txtchangeget(atr, TXT_ATTR_FG_COL);
1904	tc = TCFGCOLOUR;
1905	def = txtchangeisset(atr, TXTNOFGCOLOUR);
1906	use_termcap = txtchangeisset(atr, TXT_ATTR_FG_TERMCAP);
1907    } else {
1908	colour = txtchangeget(atr, TXT_ATTR_BG_COL);
1909	tc = TCBGCOLOUR;
1910	def = txtchangeisset(atr, TXTNOBGCOLOUR);
1911	use_termcap = txtchangeisset(atr, TXT_ATTR_BG_TERMCAP);
1912    }
1913
1914    /*
1915     * If we're not restoring the default, and either have a
1916     * colour value that is too large for ANSI, or have been told
1917     * to use the termcap sequence, try to use the termcap sequence.
1918     *
1919     * We have already sanitised the values we allow from the
1920     * highlighting variables, so much of this shouldn't be
1921     * necessary at this point, but we might as well be safe.
1922     */
1923    if (!def && (colour > 7 || use_termcap)) {
1924	/*
1925	 * We can if it's available, and either we couldn't get
1926	 * the maximum number of colours, or the colour is in range.
1927	 */
1928	if (tccan(tc) && (tccolours < 0 || colour < tccolours))
1929	{
1930	    if (is_prompt)
1931	    {
1932		if (!bv->dontcount) {
1933		    addbufspc(1);
1934		    *bv->bp++ = Inpar;
1935		}
1936		tputs(tgoto(tcstr[tc], colour, colour), 1, putstr);
1937		if (!bv->dontcount) {
1938		    addbufspc(1);
1939		    *bv->bp++ = Outpar;
1940		}
1941	    } else {
1942		tputs(tgoto(tcstr[tc], colour, colour), 1, putshout);
1943	    }
1944	    /* That worked. */
1945	    return;
1946	}
1947	/*
1948	 * Nope, that didn't work.
1949	 * If 0 to 7, assume standard ANSI works, otherwise it won't.
1950	 */
1951	if (colour > 7)
1952	    return;
1953    }
1954
1955    if ((do_free = (colseq_buf == NULL))) {
1956	/* This can happen when moving the cursor in trashzle() */
1957	allocate_colour_buffer();
1958    }
1959
1960    strcpy(colseq_buf, fg_bg_sequences[fg_bg].start);
1961
1962    ptr = colseq_buf + strlen(colseq_buf);
1963    if (def) {
1964	strcpy(ptr, fg_bg_sequences[fg_bg].def);
1965	while (*ptr)
1966	    ptr++;
1967    } else
1968	*ptr++ = colour + '0';
1969    strcpy(ptr, fg_bg_sequences[fg_bg].end);
1970
1971    if (is_prompt) {
1972	if (!bv->dontcount) {
1973	    addbufspc(1);
1974	    *bv->bp++ = Inpar;
1975	}
1976	tputs(colseq_buf, 1, putstr);
1977	if (!bv->dontcount) {
1978	    addbufspc(1);
1979	    *bv->bp++ = Outpar;
1980	}
1981    } else
1982	tputs(colseq_buf, 1, putshout);
1983
1984    if (do_free)
1985	free_colour_buffer();
1986}
1987