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