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
dateinit(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
printprompt(int promptno,const char * str)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
tprintf_append_mbs(struct Strbuf * buf,const char * mbs,Char attributes)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 *
tprintf(int what,const Char * fmt,const char * str,time_t tim,ptr_t info)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
expdollar(struct Strbuf * buf,const Char ** srcp,Char attr)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