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