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