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