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