1 /* $Header: /p/tcsh/cvsroot/tcsh/tc.prompt.c,v 3.70 2011/10/27 22:41:06 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("$tcsh: tc.prompt.c,v 3.70 2011/10/27 22:41:06 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(void) 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(int promptno, const char *str) 112 { 113 static const Char *ocp = NULL; 114 static const char *ostr = NULL; 115 time_t lclock = time(NULL); 116 const Char *cp; 117 118 switch (promptno) { 119 default: 120 case 0: 121 cp = varval(STRprompt); 122 break; 123 case 1: 124 cp = varval(STRprompt2); 125 break; 126 case 2: 127 cp = varval(STRprompt3); 128 break; 129 case 3: 130 if (ocp != NULL) { 131 cp = ocp; 132 str = ostr; 133 } 134 else 135 cp = varval(STRprompt); 136 break; 137 } 138 139 if (promptno < 2) { 140 ocp = cp; 141 ostr = str; 142 } 143 144 xfree(Prompt); 145 Prompt = NULL; 146 Prompt = tprintf(FMT_PROMPT, cp, str, lclock, NULL); 147 if (!editing) { 148 for (cp = Prompt; *cp ; ) 149 (void) putwraw(*cp++); 150 SetAttributes(0); 151 flush(); 152 } 153 154 xfree(RPrompt); 155 RPrompt = NULL; 156 if (promptno == 0) { /* determine rprompt if using main prompt */ 157 cp = varval(STRrprompt); 158 RPrompt = tprintf(FMT_PROMPT, cp, NULL, lclock, NULL); 159 /* if not editing, put rprompt after prompt */ 160 if (!editing && RPrompt[0] != '\0') { 161 for (cp = RPrompt; *cp ; ) 162 (void) putwraw(*cp++); 163 SetAttributes(0); 164 putraw(' '); 165 flush(); 166 } 167 } 168 } 169 170 static void 171 tprintf_append_mbs(struct Strbuf *buf, const char *mbs, Char attributes) 172 { 173 while (*mbs != 0) { 174 Char wc; 175 176 mbs += one_mbtowc(&wc, mbs, MB_LEN_MAX); 177 Strbuf_append1(buf, wc | attributes); 178 } 179 } 180 181 Char * 182 tprintf(int what, const Char *fmt, const char *str, time_t tim, ptr_t info) 183 { 184 struct Strbuf buf = Strbuf_INIT; 185 Char *z, *q; 186 Char attributes = 0; 187 static int print_prompt_did_ding = 0; 188 char *cz; 189 190 Char *p; 191 const Char *cp = fmt; 192 Char Scp; 193 struct tm *t = localtime(&tim); 194 195 /* prompt stuff */ 196 static Char *olduser = NULL; 197 int updirs; 198 size_t pdirs; 199 200 cleanup_push(&buf, Strbuf_cleanup); 201 for (; *cp; cp++) { 202 if ((*cp == '%') && ! (cp[1] == '\0')) { 203 cp++; 204 switch (*cp) { 205 case 'R': 206 if (what == FMT_HISTORY) { 207 cz = fmthist('R', info); 208 tprintf_append_mbs(&buf, cz, attributes); 209 xfree(cz); 210 } else { 211 if (str != NULL) 212 tprintf_append_mbs(&buf, str, attributes); 213 } 214 break; 215 case '#': 216 Scp = (uid == 0 || euid == 0) ? PRCHROOT : PRCH; 217 if (Scp != '\0') 218 Strbuf_append1(&buf, attributes | Scp); 219 break; 220 case '!': 221 case 'h': 222 switch (what) { 223 case FMT_HISTORY: 224 cz = fmthist('h', info); 225 break; 226 case FMT_SCHED: 227 cz = xasprintf("%d", *(int *)info); 228 break; 229 default: 230 cz = xasprintf("%d", eventno + 1); 231 break; 232 } 233 tprintf_append_mbs(&buf, cz, attributes); 234 xfree(cz); 235 break; 236 case 'T': /* 24 hour format */ 237 case '@': 238 case 't': /* 12 hour am/pm format */ 239 case 'p': /* With seconds */ 240 case 'P': 241 { 242 char ampm = 'a'; 243 int hr = t->tm_hour; 244 245 /* addition by Hans J. Albertsson */ 246 /* and another adapted from Justin Bur */ 247 if (adrof(STRampm) || (*cp != 'T' && *cp != 'P')) { 248 if (hr >= 12) { 249 if (hr > 12) 250 hr -= 12; 251 ampm = 'p'; 252 } 253 else if (hr == 0) 254 hr = 12; 255 } /* else do a 24 hour clock */ 256 257 /* "DING!" stuff by Hans also */ 258 if (t->tm_min || print_prompt_did_ding || 259 what != FMT_PROMPT || adrof(STRnoding)) { 260 if (t->tm_min) 261 print_prompt_did_ding = 0; 262 /* 263 * Pad hour to 2 characters if padhour is set, 264 * by ADAM David Alan Martin 265 */ 266 p = Itoa(hr, adrof(STRpadhour) ? 2 : 0, attributes); 267 Strbuf_append(&buf, p); 268 xfree(p); 269 Strbuf_append1(&buf, attributes | ':'); 270 p = Itoa(t->tm_min, 2, attributes); 271 Strbuf_append(&buf, p); 272 xfree(p); 273 if (*cp == 'p' || *cp == 'P') { 274 Strbuf_append1(&buf, attributes | ':'); 275 p = Itoa(t->tm_sec, 2, attributes); 276 Strbuf_append(&buf, p); 277 xfree(p); 278 } 279 if (adrof(STRampm) || (*cp != 'T' && *cp != 'P')) { 280 Strbuf_append1(&buf, attributes | ampm); 281 Strbuf_append1(&buf, attributes | 'm'); 282 } 283 } 284 else { /* we need to ding */ 285 size_t i; 286 287 for (i = 0; STRDING[i] != 0; i++) 288 Strbuf_append1(&buf, attributes | STRDING[i]); 289 print_prompt_did_ding = 1; 290 } 291 } 292 break; 293 294 case 'M': 295 #ifndef HAVENOUTMP 296 if (what == FMT_WHO) 297 cz = who_info(info, 'M'); 298 else 299 #endif /* HAVENOUTMP */ 300 cz = getenv("HOST"); 301 /* 302 * Bug pointed out by Laurent Dami <dami@cui.unige.ch>: don't 303 * derefrence that NULL (if HOST is not set)... 304 */ 305 if (cz != NULL) 306 tprintf_append_mbs(&buf, cz, attributes); 307 if (what == FMT_WHO) 308 xfree(cz); 309 break; 310 311 case 'm': { 312 char *scz = NULL; 313 #ifndef HAVENOUTMP 314 if (what == FMT_WHO) 315 scz = cz = who_info(info, 'm'); 316 else 317 #endif /* HAVENOUTMP */ 318 cz = getenv("HOST"); 319 320 if (cz != NULL) 321 while (*cz != 0 && (what == FMT_WHO || *cz != '.')) { 322 Char wc; 323 324 cz += one_mbtowc(&wc, cz, MB_LEN_MAX); 325 Strbuf_append1(&buf, wc | attributes); 326 } 327 if (scz) 328 xfree(scz); 329 break; 330 } 331 332 /* lukem: new directory prompt code */ 333 case '~': 334 case '/': 335 case '.': 336 case 'c': 337 case 'C': 338 Scp = *cp; 339 if (Scp == 'c') /* store format type (c == .) */ 340 Scp = '.'; 341 if ((z = varval(STRcwd)) == STRNULL) 342 break; /* no cwd, so don't do anything */ 343 344 /* show ~ whenever possible - a la dirs */ 345 if (Scp == '~' || Scp == '.' ) { 346 static Char *olddir = NULL; 347 348 if (tlength == 0 || olddir != z) { 349 olddir = z; /* have we changed dir? */ 350 olduser = getusername(&olddir); 351 } 352 if (olduser) 353 z = olddir; 354 } 355 updirs = pdirs = 0; 356 357 /* option to determine fixed # of dirs from path */ 358 if (Scp == '.' || Scp == 'C') { 359 int skip; 360 #ifdef WINNT_NATIVE 361 Char *oldz = z; 362 if (z[1] == ':') { 363 Strbuf_append1(&buf, attributes | *z++); 364 Strbuf_append1(&buf, attributes | *z++); 365 } 366 if (*z == '/' && z[1] == '/') { 367 Strbuf_append1(&buf, attributes | *z++); 368 Strbuf_append1(&buf, attributes | *z++); 369 do { 370 Strbuf_append1(&buf, attributes | *z++); 371 } while(*z != '/'); 372 } 373 #endif /* WINNT_NATIVE */ 374 q = z; 375 while (*z) /* calc # of /'s */ 376 if (*z++ == '/') 377 updirs++; 378 379 #ifdef WINNT_NATIVE 380 /* 381 * for format type c, prompt will be following... 382 * c:/path => c:/path 383 * c:/path/to => c:to 384 * //machine/share => //machine/share 385 * //machine/share/folder => //machine:folder 386 */ 387 if (oldz[0] == '/' && oldz[1] == '/' && updirs > 1) 388 Strbuf_append1(&buf, attributes | ':'); 389 #endif /* WINNT_NATIVE */ 390 if ((Scp == 'C' && *q != '/')) 391 updirs++; 392 393 if (cp[1] == '0') { /* print <x> or ... */ 394 pdirs = 1; 395 cp++; 396 } 397 if (cp[1] >= '1' && cp[1] <= '9') { /* calc # to skip */ 398 skip = cp[1] - '0'; 399 cp++; 400 } 401 else 402 skip = 1; 403 404 updirs -= skip; 405 while (skip-- > 0) { 406 while ((z > q) && (*z != '/')) 407 z--; /* back up */ 408 if (skip && z > q) 409 z--; 410 } 411 if (*z == '/' && z != q) 412 z++; 413 } /* . || C */ 414 415 /* print ~[user] */ 416 if ((olduser) && ((Scp == '~') || 417 (Scp == '.' && (pdirs || (!pdirs && updirs <= 0))) )) { 418 Strbuf_append1(&buf, attributes | '~'); 419 for (q = olduser; *q; q++) 420 Strbuf_append1(&buf, attributes | *q); 421 } 422 423 /* RWM - tell you how many dirs we've ignored */ 424 /* and add '/' at front of this */ 425 if (updirs > 0 && pdirs) { 426 if (adrof(STRellipsis)) { 427 Strbuf_append1(&buf, attributes | '.'); 428 Strbuf_append1(&buf, attributes | '.'); 429 Strbuf_append1(&buf, attributes | '.'); 430 } else { 431 Strbuf_append1(&buf, attributes | '/'); 432 Strbuf_append1(&buf, attributes | '<'); 433 if (updirs > 9) { 434 Strbuf_append1(&buf, attributes | '9'); 435 Strbuf_append1(&buf, attributes | '+'); 436 } else 437 Strbuf_append1(&buf, attributes | ('0' + updirs)); 438 Strbuf_append1(&buf, attributes | '>'); 439 } 440 } 441 442 while (*z) 443 Strbuf_append1(&buf, attributes | *z++); 444 break; 445 /* lukem: end of new directory prompt code */ 446 447 case 'n': 448 #ifndef HAVENOUTMP 449 if (what == FMT_WHO) { 450 cz = who_info(info, 'n'); 451 tprintf_append_mbs(&buf, cz, attributes); 452 xfree(cz); 453 } 454 else 455 #endif /* HAVENOUTMP */ 456 { 457 if ((z = varval(STRuser)) != STRNULL) 458 while (*z) 459 Strbuf_append1(&buf, attributes | *z++); 460 } 461 break; 462 case 'N': 463 if ((z = varval(STReuser)) != STRNULL) 464 while (*z) 465 Strbuf_append1(&buf, attributes | *z++); 466 break; 467 case 'l': 468 #ifndef HAVENOUTMP 469 if (what == FMT_WHO) { 470 cz = who_info(info, 'l'); 471 tprintf_append_mbs(&buf, cz, attributes); 472 xfree(cz); 473 } 474 else 475 #endif /* HAVENOUTMP */ 476 { 477 if ((z = varval(STRtty)) != STRNULL) 478 while (*z) 479 Strbuf_append1(&buf, attributes | *z++); 480 } 481 break; 482 case 'd': 483 tprintf_append_mbs(&buf, day_list[t->tm_wday], attributes); 484 break; 485 case 'D': 486 p = Itoa(t->tm_mday, 2, attributes); 487 Strbuf_append(&buf, p); 488 xfree(p); 489 break; 490 case 'w': 491 tprintf_append_mbs(&buf, month_list[t->tm_mon], attributes); 492 break; 493 case 'W': 494 p = Itoa(t->tm_mon + 1, 2, attributes); 495 Strbuf_append(&buf, p); 496 xfree(p); 497 break; 498 case 'y': 499 p = Itoa(t->tm_year % 100, 2, attributes); 500 Strbuf_append(&buf, p); 501 xfree(p); 502 break; 503 case 'Y': 504 p = Itoa(t->tm_year + 1900, 4, attributes); 505 Strbuf_append(&buf, p); 506 xfree(p); 507 break; 508 case 'S': /* start standout */ 509 attributes |= STANDOUT; 510 break; 511 case 'B': /* start bold */ 512 attributes |= BOLD; 513 break; 514 case 'U': /* start underline */ 515 attributes |= UNDER; 516 break; 517 case 's': /* end standout */ 518 attributes &= ~STANDOUT; 519 break; 520 case 'b': /* end bold */ 521 attributes &= ~BOLD; 522 break; 523 case 'u': /* end underline */ 524 attributes &= ~UNDER; 525 break; 526 case 'L': 527 ClearToBottom(); 528 break; 529 530 case 'j': 531 { 532 int njobs = -1; 533 struct process *pp; 534 535 for (pp = proclist.p_next; pp; pp = pp->p_next) 536 njobs++; 537 if (njobs == -1) 538 njobs++; 539 p = Itoa(njobs, 1, attributes); 540 Strbuf_append(&buf, p); 541 xfree(p); 542 break; 543 } 544 case '?': 545 if ((z = varval(STRstatus)) != STRNULL) 546 while (*z) 547 Strbuf_append1(&buf, attributes | *z++); 548 break; 549 case '$': 550 expdollar(&buf, &cp, attributes); 551 /* cp should point the last char of current % sequence */ 552 cp--; 553 break; 554 case '%': 555 Strbuf_append1(&buf, attributes | '%'); 556 break; 557 case '{': /* literal characters start */ 558 #if LITERAL == 0 559 /* 560 * No literal capability, so skip all chars in the literal 561 * string 562 */ 563 while (*cp != '\0' && (cp[-1] != '%' || *cp != '}')) 564 cp++; 565 #endif /* LITERAL == 0 */ 566 attributes |= LITERAL; 567 break; 568 case '}': /* literal characters end */ 569 attributes &= ~LITERAL; 570 break; 571 default: 572 #ifndef HAVENOUTMP 573 if (*cp == 'a' && what == FMT_WHO) { 574 cz = who_info(info, 'a'); 575 tprintf_append_mbs(&buf, cz, attributes); 576 xfree(cz); 577 } 578 else 579 #endif /* HAVENOUTMP */ 580 { 581 Strbuf_append1(&buf, attributes | '%'); 582 Strbuf_append1(&buf, attributes | *cp); 583 } 584 break; 585 } 586 } 587 else if (*cp == '\\' || *cp == '^') 588 Strbuf_append1(&buf, attributes | parseescape(&cp)); 589 else if (*cp == HIST) { /* EGS: handle '!'s in prompts */ 590 if (what == FMT_HISTORY) 591 cz = fmthist('h', info); 592 else 593 cz = xasprintf("%d", eventno + 1); 594 tprintf_append_mbs(&buf, cz, attributes); 595 xfree(cz); 596 } 597 else 598 Strbuf_append1(&buf, attributes | *cp); /* normal character */ 599 } 600 cleanup_ignore(&buf); 601 cleanup_until(&buf); 602 return Strbuf_finish(&buf); 603 } 604 605 int 606 expdollar(struct Strbuf *buf, const Char **srcp, Char attr) 607 { 608 struct varent *vp; 609 const Char *src = *srcp; 610 Char *var, *val; 611 size_t i; 612 int curly = 0; 613 614 /* found a variable, expand it */ 615 var = xmalloc((Strlen(src) + 1) * sizeof (*var)); 616 for (i = 0; ; i++) { 617 var[i] = *++src & TRIM; 618 if (i == 0 && var[i] == '{') { 619 curly = 1; 620 var[i] = *++src & TRIM; 621 } 622 if (!alnum(var[i]) && var[i] != '_') { 623 624 var[i] = '\0'; 625 break; 626 } 627 } 628 if (curly && (*src & TRIM) == '}') 629 src++; 630 631 vp = adrof(var); 632 if (vp && vp->vec) { 633 for (i = 0; vp->vec[i] != NULL; i++) { 634 for (val = vp->vec[i]; *val; val++) 635 if (*val != '\n' && *val != '\r') 636 Strbuf_append1(buf, *val | attr); 637 if (vp->vec[i+1]) 638 Strbuf_append1(buf, ' ' | attr); 639 } 640 } 641 else { 642 val = (!vp) ? tgetenv(var) : NULL; 643 if (val) { 644 for (; *val; val++) 645 if (*val != '\n' && *val != '\r') 646 Strbuf_append1(buf, *val | attr); 647 } else { 648 *srcp = src; 649 xfree(var); 650 return 0; 651 } 652 } 653 654 *srcp = src; 655 xfree(var); 656 return 1; 657 } 658