1 /* $Header: /src/pub/tcsh/tc.prompt.c,v 3.53 2005/01/05 18:06:43 christos Exp $ */ 2 /* 3 * tc.prompt.c: Prompt printing stuff 4 */ 5 /*- 6 * Copyright (c) 1980, 1991 The Regents of the University of California. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 #include "sh.h" 34 35 RCSID("$Id: tc.prompt.c,v 3.53 2005/01/05 18:06:43 christos Exp $") 36 37 #include "ed.h" 38 #include "tw.h" 39 40 /* 41 * kfk 21oct1983 -- add @ (time) and / ($cwd) in prompt. 42 * PWP 4/27/87 -- rearange for tcsh. 43 * mrdch@com.tau.edu.il 6/26/89 - added ~, T and .# - rearanged to switch() 44 * instead of if/elseif 45 * Luke Mewburn, <lukem@cs.rmit.edu.au> 46 * 6-Sep-91 changed date format 47 * 16-Feb-94 rewrote directory prompt code, added $ellipsis 48 * 29-Dec-96 added rprompt support 49 */ 50 51 static const char *month_list[12]; 52 static const char *day_list[7]; 53 54 void 55 dateinit() 56 { 57 #ifdef notyet 58 int i; 59 60 setlocale(LC_TIME, ""); 61 62 for (i = 0; i < 12; i++) 63 xfree((ptr_t) month_list[i]); 64 month_list[0] = strsave(_time_info->abbrev_month[0]); 65 month_list[1] = strsave(_time_info->abbrev_month[1]); 66 month_list[2] = strsave(_time_info->abbrev_month[2]); 67 month_list[3] = strsave(_time_info->abbrev_month[3]); 68 month_list[4] = strsave(_time_info->abbrev_month[4]); 69 month_list[5] = strsave(_time_info->abbrev_month[5]); 70 month_list[6] = strsave(_time_info->abbrev_month[6]); 71 month_list[7] = strsave(_time_info->abbrev_month[7]); 72 month_list[8] = strsave(_time_info->abbrev_month[8]); 73 month_list[9] = strsave(_time_info->abbrev_month[9]); 74 month_list[10] = strsave(_time_info->abbrev_month[10]); 75 month_list[11] = strsave(_time_info->abbrev_month[11]); 76 77 for (i = 0; i < 7; i++) 78 xfree((ptr_t) day_list[i]); 79 day_list[0] = strsave(_time_info->abbrev_wkday[0]); 80 day_list[1] = strsave(_time_info->abbrev_wkday[1]); 81 day_list[2] = strsave(_time_info->abbrev_wkday[2]); 82 day_list[3] = strsave(_time_info->abbrev_wkday[3]); 83 day_list[4] = strsave(_time_info->abbrev_wkday[4]); 84 day_list[5] = strsave(_time_info->abbrev_wkday[5]); 85 day_list[6] = strsave(_time_info->abbrev_wkday[6]); 86 #else 87 month_list[0] = "Jan"; 88 month_list[1] = "Feb"; 89 month_list[2] = "Mar"; 90 month_list[3] = "Apr"; 91 month_list[4] = "May"; 92 month_list[5] = "Jun"; 93 month_list[6] = "Jul"; 94 month_list[7] = "Aug"; 95 month_list[8] = "Sep"; 96 month_list[9] = "Oct"; 97 month_list[10] = "Nov"; 98 month_list[11] = "Dec"; 99 100 day_list[0] = "Sun"; 101 day_list[1] = "Mon"; 102 day_list[2] = "Tue"; 103 day_list[3] = "Wed"; 104 day_list[4] = "Thu"; 105 day_list[5] = "Fri"; 106 day_list[6] = "Sat"; 107 #endif 108 } 109 110 void 111 printprompt(promptno, str) 112 int promptno; 113 const char *str; 114 { 115 static Char *ocp = NULL; 116 static const char *ostr = NULL; 117 time_t lclock = time(NULL); 118 Char *cp; 119 120 switch (promptno) { 121 default: 122 case 0: 123 cp = varval(STRprompt); 124 break; 125 case 1: 126 cp = varval(STRprompt2); 127 break; 128 case 2: 129 cp = varval(STRprompt3); 130 break; 131 case 3: 132 if (ocp != NULL) { 133 cp = ocp; 134 str = ostr; 135 } 136 else 137 cp = varval(STRprompt); 138 break; 139 } 140 141 if (promptno < 2) { 142 ocp = cp; 143 ostr = str; 144 } 145 146 PromptBuf[0] = '\0'; 147 tprintf(FMT_PROMPT, PromptBuf, cp, 2 * INBUFSIZE - 2, str, lclock, NULL); 148 if (!editing) { 149 for (cp = PromptBuf; *cp ; ) 150 (void) putwraw(*cp++); 151 SetAttributes(0); 152 flush(); 153 } 154 155 RPromptBuf[0] = '\0'; 156 if (promptno == 0) { /* determine rprompt if using main prompt */ 157 cp = varval(STRrprompt); 158 tprintf(FMT_PROMPT, RPromptBuf, cp, INBUFSIZE - 2, NULL, lclock, NULL); 159 /* if not editing, put rprompt after prompt */ 160 if (!editing && RPromptBuf[0] != '\0') { 161 for (cp = RPromptBuf; *cp ; ) 162 (void) putwraw(*cp++); 163 SetAttributes(0); 164 putraw(' '); 165 flush(); 166 } 167 } 168 } 169 170 void 171 tprintf(what, buf, fmt, siz, str, tim, info) 172 int what; 173 Char *buf; 174 const Char *fmt; 175 size_t siz; 176 const char *str; 177 time_t tim; 178 ptr_t info; 179 { 180 Char *z, *q; 181 Char attributes = 0; 182 static int print_prompt_did_ding = 0; 183 Char buff[BUFSIZE]; 184 /* Need to be unsigned to avoid sign extension */ 185 const unsigned char *cz; 186 unsigned char cbuff[BUFSIZE]; 187 188 Char *p = buf; 189 Char *ep = &p[siz]; 190 const Char *cp = fmt; 191 Char Scp; 192 struct tm *t = localtime(&tim); 193 194 /* prompt stuff */ 195 static Char *olddir = NULL, *olduser = NULL; 196 int updirs; 197 size_t pdirs, sz; 198 int l; 199 200 for (; *cp; cp++) { 201 if (p >= ep) 202 break; 203 l = NLSSize(cp, -1); 204 if (l > 1) { 205 while (l--) 206 *p++ = attributes | *cp++; 207 cp--; 208 continue; 209 } 210 if ((*cp == '%') && ! (cp[1] == '\0')) { 211 cp++; 212 switch (*cp) { 213 case 'R': 214 if (what == FMT_HISTORY) { 215 fmthist('R', info, (char *) cbuff, sizeof(cbuff)); 216 cz = cbuff; 217 } else 218 cz = (const unsigned char *) str; 219 if (cz != NULL) 220 for (; *cz && p < ep; p++) { 221 cz += one_mbtowc(p, (const char *)cz, MB_LEN_MAX); 222 *p |= attributes; 223 } 224 break; 225 case '#': 226 *p++ = attributes | ((uid == 0) ? PRCHROOT : PRCH); 227 break; 228 case '!': 229 case 'h': 230 switch (what) { 231 case FMT_HISTORY: 232 fmthist('h', info, (char *) cbuff, sizeof(cbuff)); 233 break; 234 case FMT_SCHED: 235 (void) xsnprintf((char *) cbuff, sizeof(cbuff), "%d", 236 *(int *)info); 237 break; 238 default: 239 (void) xsnprintf((char *) cbuff, sizeof(cbuff), "%d", 240 eventno + 1); 241 break; 242 } 243 for (cz = cbuff; *cz && p < ep; p++) { 244 cz += one_mbtowc(p, (const char *)cz, MB_LEN_MAX); 245 *p |= attributes; 246 } 247 break; 248 case 'T': /* 24 hour format */ 249 case '@': 250 case 't': /* 12 hour am/pm format */ 251 case 'p': /* With seconds */ 252 case 'P': 253 { 254 char ampm = 'a'; 255 int hr = t->tm_hour; 256 257 if (p >= ep - 10) break; 258 259 /* addition by Hans J. Albertsson */ 260 /* and another adapted from Justin Bur */ 261 if (adrof(STRampm) || (*cp != 'T' && *cp != 'P')) { 262 if (hr >= 12) { 263 if (hr > 12) 264 hr -= 12; 265 ampm = 'p'; 266 } 267 else if (hr == 0) 268 hr = 12; 269 } /* else do a 24 hour clock */ 270 271 /* "DING!" stuff by Hans also */ 272 if (t->tm_min || print_prompt_did_ding || 273 what != FMT_PROMPT || adrof(STRnoding)) { 274 if (t->tm_min) 275 print_prompt_did_ding = 0; 276 p = Itoa(hr, p, 0, attributes); 277 *p++ = attributes | ':'; 278 p = Itoa(t->tm_min, p, 2, attributes); 279 if (*cp == 'p' || *cp == 'P') { 280 *p++ = attributes | ':'; 281 p = Itoa(t->tm_sec, p, 2, attributes); 282 } 283 if (adrof(STRampm) || (*cp != 'T' && *cp != 'P')) { 284 *p++ = attributes | ampm; 285 *p++ = attributes | 'm'; 286 } 287 } 288 else { /* we need to ding */ 289 int i = 0; 290 291 (void) Strcpy(buff, STRDING); 292 while (buff[i]) { 293 *p++ = attributes | buff[i++]; 294 } 295 print_prompt_did_ding = 1; 296 } 297 } 298 break; 299 300 case 'M': 301 #ifndef HAVENOUTMP 302 if (what == FMT_WHO) 303 cz = (const unsigned char *) who_info(info, 'M', 304 (char *) cbuff, sizeof(cbuff)); 305 else 306 #endif /* HAVENOUTMP */ 307 cz = (const unsigned char *) getenv("HOST"); 308 /* 309 * Bug pointed out by Laurent Dami <dami@cui.unige.ch>: don't 310 * derefrence that NULL (if HOST is not set)... 311 */ 312 if (cz != NULL) 313 for (; *cz && p < ep; p++) { 314 cz += one_mbtowc(p, (const char *)cz, MB_LEN_MAX); 315 *p |= attributes; 316 } 317 break; 318 319 case 'm': 320 #ifndef HAVENOUTMP 321 if (what == FMT_WHO) 322 cz = (const unsigned char *) who_info(info, 'm', 323 (char *) cbuff, sizeof(cbuff)); 324 else 325 #endif /* HAVENOUTMP */ 326 cz = (const unsigned char *) getenv("HOST"); 327 328 if (cz != NULL) 329 for (; *cz && (what == FMT_WHO || *cz != '.') && p < ep; 330 p++) { 331 cz += one_mbtowc(p, (const char *)cz, MB_LEN_MAX); 332 *p |= attributes; 333 } 334 break; 335 336 /* lukem: new directory prompt code */ 337 case '~': 338 case '/': 339 case '.': 340 case 'c': 341 case 'C': 342 Scp = *cp; 343 if (Scp == 'c') /* store format type (c == .) */ 344 Scp = '.'; 345 if ((z = varval(STRcwd)) == STRNULL) 346 break; /* no cwd, so don't do anything */ 347 348 /* show ~ whenever possible - a la dirs */ 349 if (Scp == '~' || Scp == '.' ) { 350 if (tlength == 0 || olddir != z) { 351 olddir = z; /* have we changed dir? */ 352 olduser = getusername(&olddir); 353 } 354 if (olduser) 355 z = olddir; 356 } 357 updirs = pdirs = 0; 358 359 /* option to determine fixed # of dirs from path */ 360 if (Scp == '.' || Scp == 'C') { 361 int skip; 362 #ifdef WINNT_NATIVE 363 Char *oldz = z; 364 if (z[1] == ':') { 365 *p++ = attributes | *z++; 366 *p++ = attributes | *z++; 367 } 368 if (*z == '/' && z[1] == '/') { 369 *p++ = attributes | *z++; 370 *p++ = attributes | *z++; 371 do { 372 *p++ = attributes | *z++; 373 }while(*z != '/'); 374 } 375 #endif /* WINNT_NATIVE */ 376 q = z; 377 while (*z) /* calc # of /'s */ 378 if (*z++ == '/') 379 updirs++; 380 381 #ifdef WINNT_NATIVE 382 /* 383 * for format type c, prompt will be following... 384 * c:/path => c:/path 385 * c:/path/to => c:to 386 * //machine/share => //machine/share 387 * //machine/share/folder => //machine:folder 388 */ 389 if (oldz[0] == '/' && oldz[1] == '/' && updirs > 1) 390 *p++ = attributes | ':'; 391 #endif /* WINNT_NATIVE */ 392 if ((Scp == 'C' && *q != '/')) 393 updirs++; 394 395 if (cp[1] == '0') { /* print <x> or ... */ 396 pdirs = 1; 397 cp++; 398 } 399 if (cp[1] >= '1' && cp[1] <= '9') { /* calc # to skip */ 400 skip = cp[1] - '0'; 401 cp++; 402 } 403 else 404 skip = 1; 405 406 updirs -= skip; 407 while (skip-- > 0) { 408 while ((z > q) && (*z != '/')) 409 z--; /* back up */ 410 if (skip && z > q) 411 z--; 412 } 413 if (*z == '/' && z != q) 414 z++; 415 } /* . || C */ 416 417 /* print ~[user] */ 418 if ((olduser) && ((Scp == '~') || 419 (Scp == '.' && (pdirs || (!pdirs && updirs <= 0))) )) { 420 *p++ = attributes | '~'; 421 if (p >= ep) break; 422 for (q = olduser; *q; *p++ = attributes | *q++) 423 if (p >= ep) break; 424 } 425 426 /* RWM - tell you how many dirs we've ignored */ 427 /* and add '/' at front of this */ 428 if (updirs > 0 && pdirs) { 429 if (p >= ep - 5) break; 430 if (adrof(STRellipsis)) { 431 *p++ = attributes | '.'; 432 *p++ = attributes | '.'; 433 *p++ = attributes | '.'; 434 } else { 435 *p++ = attributes | '/'; 436 *p++ = attributes | '<'; 437 if (updirs > 9) { 438 *p++ = attributes | '9'; 439 *p++ = attributes | '+'; 440 } else 441 *p++ = attributes | ('0' + updirs); 442 *p++ = attributes | '>'; 443 } 444 } 445 446 for (; *z ; *p++ = attributes | *z++) 447 if (p >= ep) break; 448 break; 449 /* lukem: end of new directory prompt code */ 450 451 case 'n': 452 #ifndef HAVENOUTMP 453 if (what == FMT_WHO) { 454 cz = (const unsigned char *) who_info(info, 'n', 455 (char *) cbuff, sizeof(cbuff)); 456 for (; *cz && p < ep; p++) { 457 cz += one_mbtowc(p, (const char *)cz, MB_LEN_MAX); 458 *p |= attributes; 459 } 460 } 461 else 462 #endif /* HAVENOUTMP */ 463 { 464 if ((z = varval(STRuser)) != STRNULL) 465 for (; *z; *p++ = attributes | *z++) 466 if (p >= ep) break; 467 } 468 break; 469 case 'l': 470 #ifndef HAVENOUTMP 471 if (what == FMT_WHO) { 472 cz = (const unsigned char *) who_info(info, 'l', 473 (char *) cbuff, sizeof(cbuff)); 474 for (; *cz && p < ep; p++) { 475 cz += one_mbtowc(p, (const char *)cz, MB_LEN_MAX); 476 *p |= attributes; 477 } 478 } 479 else 480 #endif /* HAVENOUTMP */ 481 { 482 if ((z = varval(STRtty)) != STRNULL) 483 for (; *z; *p++ = attributes | *z++) 484 if (p >= ep) break; 485 } 486 break; 487 case 'd': 488 for (cz = (const unsigned char *) day_list[t->tm_wday]; 489 *cz && p < ep; p++) { 490 cz += one_mbtowc(p, (const char *)cz, MB_LEN_MAX); 491 *p |= attributes; 492 } 493 break; 494 case 'D': 495 if (p >= ep - 3) break; 496 p = Itoa(t->tm_mday, p, 2, attributes); 497 break; 498 case 'w': 499 if (p >= ep - 5) break; 500 for (cz = (const unsigned char *) month_list[t->tm_mon]; 501 *cz && p < ep; p++) { 502 cz += one_mbtowc(p, (const char *)cz, MB_LEN_MAX); 503 *p |= attributes; 504 } 505 break; 506 case 'W': 507 if (p >= ep - 3) break; 508 p = Itoa(t->tm_mon + 1, p, 2, attributes); 509 break; 510 case 'y': 511 if (p >= ep - 3) break; 512 p = Itoa(t->tm_year % 100, p, 2, attributes); 513 break; 514 case 'Y': 515 if (p >= ep - 5) break; 516 p = Itoa(t->tm_year + 1900, p, 4, attributes); 517 break; 518 case 'S': /* start standout */ 519 attributes |= STANDOUT; 520 break; 521 case 'B': /* start bold */ 522 attributes |= BOLD; 523 break; 524 case 'U': /* start underline */ 525 attributes |= UNDER; 526 break; 527 case 's': /* end standout */ 528 attributes &= ~STANDOUT; 529 break; 530 case 'b': /* end bold */ 531 attributes &= ~BOLD; 532 break; 533 case 'u': /* end underline */ 534 attributes &= ~UNDER; 535 break; 536 case 'L': 537 ClearToBottom(); 538 break; 539 540 case 'j': 541 { 542 Char xbuf[128], *ebuf, *xq; 543 int njobs = -1; 544 struct process *pp; 545 for (pp = proclist.p_next; pp; pp = pp->p_next) 546 njobs++; 547 /* make sure we have space */ 548 ebuf = Itoa(njobs, buf, 1, attributes); 549 for (xq = xbuf; xq < ebuf; *p++ = *xq++) 550 if (p >= ep) break; 551 break; 552 } 553 case '?': 554 if ((z = varval(STRstatus)) != STRNULL) 555 for (; *z; *p++ = attributes | *z++) 556 if (p >= ep) break; 557 break; 558 case '$': 559 sz = ep - p; 560 (void) expdollar(&p, &cp, &sz, attributes); 561 /* cp should point the last char of currnet % sequence */ 562 cp--; 563 break; 564 case '%': 565 *p++ = attributes | '%'; 566 break; 567 case '{': /* literal characters start */ 568 #if LITERAL == 0 569 /* 570 * No literal capability, so skip all chars in the literal 571 * string 572 */ 573 while (*cp != '\0' && (*cp != '%' || cp[1] != '}')) 574 cp++; 575 #endif /* LITERAL == 0 */ 576 attributes |= LITERAL; 577 break; 578 case '}': /* literal characters end */ 579 attributes &= ~LITERAL; 580 break; 581 default: 582 #ifndef HAVENOUTMP 583 if (*cp == 'a' && what == FMT_WHO) { 584 cz = (const unsigned char *) who_info(info, 'a', 585 (char *) cbuff, sizeof(cbuff)); 586 for (; *cz && p < ep; p++) { 587 cz += one_mbtowc(p, (const char *)cz, MB_LEN_MAX); 588 *p |= attributes; 589 } 590 } 591 else 592 #endif /* HAVENOUTMP */ 593 { 594 if (p >= ep - 3) break; 595 *p++ = attributes | '%'; 596 *p++ = attributes | *cp; 597 } 598 break; 599 } 600 } 601 else if (*cp == '\\' || *cp == '^') 602 *p++ = attributes | parseescape(&cp); 603 else if (*cp == HIST) { /* EGS: handle '!'s in prompts */ 604 if (what == FMT_HISTORY) 605 fmthist('h', info, (char *) cbuff, sizeof(cbuff)); 606 else 607 (void) xsnprintf((char *) cbuff, sizeof(cbuff), "%d", eventno + 1); 608 for (cz = cbuff; *cz && p < ep; p++) { 609 cz += one_mbtowc(p, (const char *)cz, MB_LEN_MAX); 610 *p |= attributes; 611 } 612 } 613 else 614 *p++ = attributes | *cp; /* normal character */ 615 } 616 *p = '\0'; 617 } 618 619 Char * 620 expdollar(dstp, srcp, spp, attr) 621 Char **dstp; 622 const Char **srcp; 623 size_t *spp; 624 int attr; 625 { 626 struct varent *vp; 627 Char var[MAXVARLEN]; 628 const Char *src = *srcp; 629 Char *val; 630 Char *dst = *dstp; 631 int i, curly = 0; 632 633 /* found a variable, expand it */ 634 for (i = 0; i < MAXVARLEN; i++) { 635 var[i] = *++src & TRIM; 636 if (i == 0 && var[i] == '{') { 637 curly = 1; 638 var[i] = *++src & TRIM; 639 } 640 if (!alnum(var[i])) { 641 642 var[i] = '\0'; 643 break; 644 } 645 } 646 if (curly && (*src & TRIM) == '}') 647 src++; 648 649 vp = adrof(var); 650 val = (!vp) ? tgetenv(var) : NULL; 651 if (vp && vp->vec) { 652 for (i = 0; vp->vec[i] != NULL; i++) { 653 for (val = vp->vec[i]; *spp > 0 && *val; (*spp)--) 654 *dst++ = *val++ | attr; 655 if (vp->vec[i+1] && *spp > 0) { 656 *dst++ = ' ' | attr; 657 (*spp)--; 658 } 659 } 660 } 661 else if (val) { 662 for (; *spp > 0 && *val; (*spp)--) 663 *dst++ = *val++ | attr; 664 } 665 else { 666 **dstp = '\0'; 667 *srcp = src; 668 return NULL; 669 } 670 *dst = '\0'; 671 672 val = *dstp; 673 *srcp = src; 674 *dstp = dst; 675 676 return val; 677 } 678