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