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