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