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