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