xref: /freebsd/contrib/tcsh/tc.prompt.c (revision 6b806d21d144c25f4fad714e1c0cf780f5e27d7e)
1 /* $Header: /src/pub/tcsh/tc.prompt.c,v 3.47 2002/07/25 17:14:59 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("$Id: tc.prompt.c,v 3.47 2002/07/25 17:14:59 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 char   *month_list[12];
52 static char   *day_list[7];
53 
54 void
55 dateinit()
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(promptno, str)
112     int     promptno;
113     char   *str;
114 {
115     static  Char *ocp = NULL;
116     static  char *ostr = NULL;
117     time_t  lclock = time(NULL);
118     Char   *cp;
119 
120     switch (promptno) {
121     default:
122     case 0:
123 	cp = varval(STRprompt);
124 	break;
125     case 1:
126 	cp = varval(STRprompt2);
127 	break;
128     case 2:
129 	cp = varval(STRprompt3);
130 	break;
131     case 3:
132 	if (ocp != NULL) {
133 	    cp = ocp;
134 	    str = ostr;
135 	}
136 	else
137 	    cp = varval(STRprompt);
138 	break;
139     }
140 
141     if (promptno < 2) {
142 	ocp = cp;
143 	ostr = str;
144     }
145 
146     PromptBuf[0] = '\0';
147     tprintf(FMT_PROMPT, PromptBuf, cp, 2 * INBUFSIZE - 2, str, lclock, NULL);
148 
149     if (!editing) {
150 	for (cp = PromptBuf; *cp ; )
151 	    (void) putraw(*cp++);
152 	SetAttributes(0);
153 	flush();
154     }
155 
156     RPromptBuf[0] = '\0';
157     if (promptno == 0) {	/* determine rprompt if using main prompt */
158 	cp = varval(STRrprompt);
159 	tprintf(FMT_PROMPT, RPromptBuf, cp, INBUFSIZE - 2, NULL, lclock, NULL);
160 
161 				/* if not editing, put rprompt after prompt */
162 	if (!editing && RPromptBuf[0] != '\0') {
163 	    for (cp = RPromptBuf; *cp ; )
164 		(void) putraw(*cp++);
165 	    SetAttributes(0);
166 	    putraw(' ');
167 	    flush();
168 	}
169     }
170 }
171 
172 void
173 tprintf(what, buf, fmt, siz, str, tim, info)
174     int what;
175     Char *buf;
176     const Char *fmt;
177     size_t siz;
178     char *str;
179     time_t tim;
180     ptr_t info;
181 {
182     Char   *z, *q;
183     Char    attributes = 0;
184     static int print_prompt_did_ding = 0;
185     Char    buff[BUFSIZE];
186     /* Need to be unsigned to avoid sign extension */
187     const unsigned char   *cz;
188     unsigned char    cbuff[BUFSIZE];
189 
190     Char *p  = buf;
191     Char *ep = &p[siz];
192     const Char *cp = fmt;
193     Char Scp;
194     struct tm *t = localtime(&tim);
195 
196 			/* prompt stuff */
197     static Char *olddir = NULL, *olduser = NULL;
198     extern int tlength;	/* cache cleared */
199     int updirs;
200     size_t pdirs, sz;
201 
202     for (; *cp; cp++) {
203 	if (p >= ep)
204 	    break;
205 #ifdef DSPMBYTE
206 	if (Ismbyte1(*cp) && ! (cp[1] == '\0'))
207 	{
208 	    *p++ = attributes | *cp++;	/* normal character */
209 	    *p++ = attributes | *cp;	/* normal character */
210 	}
211 	else
212 #endif /* DSPMBYTE */
213 	if ((*cp == '%') && ! (cp[1] == '\0')) {
214 	    cp++;
215 	    switch (*cp) {
216 	    case 'R':
217 		if (what == FMT_HISTORY)
218 		    fmthist('R', info, (char *) (cz = cbuff), sizeof(cbuff));
219 		else
220 		    cz = (unsigned char *) str;
221 		if (cz != NULL)
222 		    for (; *cz; *p++ = attributes | *cz++)
223 			if (p >= ep) break;
224 		break;
225 	    case '#':
226 		*p++ = attributes | ((uid == 0) ? PRCHROOT : PRCH);
227 		break;
228 	    case '!':
229 	    case 'h':
230 		switch (what) {
231 		case FMT_HISTORY:
232 		    fmthist('h', info, (char *) cbuff, sizeof(cbuff));
233 		    break;
234 		case FMT_SCHED:
235 		    (void) xsnprintf((char *) cbuff, sizeof(cbuff), "%d",
236 			*(int *)info);
237 		    break;
238 		default:
239 		    (void) xsnprintf((char *) cbuff, sizeof(cbuff), "%d",
240 			eventno + 1);
241 		    break;
242 		}
243 		for (cz = cbuff; *cz; *p++ = attributes | *cz++)
244 		    if (p >= ep) break;
245 		break;
246 	    case 'T':		/* 24 hour format	 */
247 	    case '@':
248 	    case 't':		/* 12 hour am/pm format */
249 	    case 'p':		/* With seconds	*/
250 	    case 'P':
251 		{
252 		    char    ampm = 'a';
253 		    int     hr = t->tm_hour;
254 
255 		    if (p >= ep - 10) break;
256 
257 		    /* addition by Hans J. Albertsson */
258 		    /* and another adapted from Justin Bur */
259 		    if (adrof(STRampm) || (*cp != 'T' && *cp != 'P')) {
260 			if (hr >= 12) {
261 			    if (hr > 12)
262 				hr -= 12;
263 			    ampm = 'p';
264 			}
265 			else if (hr == 0)
266 			    hr = 12;
267 		    }		/* else do a 24 hour clock */
268 
269 		    /* "DING!" stuff by Hans also */
270 		    if (t->tm_min || print_prompt_did_ding ||
271 			what != FMT_PROMPT || adrof(STRnoding)) {
272 			if (t->tm_min)
273 			    print_prompt_did_ding = 0;
274 			p = Itoa(hr, p, 0, attributes);
275 			*p++ = attributes | ':';
276 			p = Itoa(t->tm_min, p, 2, attributes);
277 			if (*cp == 'p' || *cp == 'P') {
278 			    *p++ = attributes | ':';
279 			    p = Itoa(t->tm_sec, p, 2, attributes);
280 			}
281 			if (adrof(STRampm) || (*cp != 'T' && *cp != 'P')) {
282 			    *p++ = attributes | ampm;
283 			    *p++ = attributes | 'm';
284 			}
285 		    }
286 		    else {	/* we need to ding */
287 			int     i = 0;
288 
289 			(void) Strcpy(buff, STRDING);
290 			while (buff[i]) {
291 			    *p++ = attributes | buff[i++];
292 			}
293 			print_prompt_did_ding = 1;
294 		    }
295 		}
296 		break;
297 
298 	    case 'M':
299 #ifndef HAVENOUTMP
300 		if (what == FMT_WHO)
301 		    cz = (unsigned char *) who_info(info, 'M',
302 			(char *) cbuff, sizeof(cbuff));
303 		else
304 #endif /* HAVENOUTMP */
305 		    cz = (unsigned char *) getenv("HOST");
306 		/*
307 		 * Bug pointed out by Laurent Dami <dami@cui.unige.ch>: don't
308 		 * derefrence that NULL (if HOST is not set)...
309 		 */
310 		if (cz != NULL)
311 		    for (; *cz ; *p++ = attributes | *cz++)
312 			if (p >= ep) break;
313 		break;
314 
315 	    case 'm':
316 #ifndef HAVENOUTMP
317 		if (what == FMT_WHO)
318 		    cz = (unsigned char *) who_info(info, 'm', (char *) cbuff,
319 			sizeof(cbuff));
320 		else
321 #endif /* HAVENOUTMP */
322 		    cz = (unsigned char *) getenv("HOST");
323 
324 		if (cz != NULL)
325 		    for ( ; *cz && (what == FMT_WHO || *cz != '.')
326 			  ; *p++ = attributes | *cz++ )
327 			if (p >= ep) break;
328 		break;
329 
330 			/* lukem: new directory prompt code */
331 	    case '~':
332 	    case '/':
333 	    case '.':
334 	    case 'c':
335 	    case 'C':
336 		Scp = *cp;
337 		if (Scp == 'c')		/* store format type (c == .) */
338 		    Scp = '.';
339 		if ((z = varval(STRcwd)) == STRNULL)
340 		    break;		/* no cwd, so don't do anything */
341 
342 			/* show ~ whenever possible - a la dirs */
343 		if (Scp == '~' || Scp == '.' ) {
344 		    if (tlength == 0 || olddir != z) {
345 			olddir = z;		/* have we changed dir? */
346 			olduser = getusername(&olddir);
347 		    }
348 		    if (olduser)
349 			z = olddir;
350 		}
351 		updirs = pdirs = 0;
352 
353 			/* option to determine fixed # of dirs from path */
354 		if (Scp == '.' || Scp == 'C') {
355 		    int skip;
356 #ifdef WINNT_NATIVE
357 		    if (z[1] == ':') {
358 		    	*p++ = attributes | *z++;
359 		    	*p++ = attributes | *z++;
360 		    }
361 			if (*z == '/' && z[1] == '/') {
362 				*p++ = attributes | *z++;
363 				*p++ = attributes | *z++;
364 				do {
365 					*p++ = attributes | *z++;
366 				}while(*z != '/');
367 			}
368 #endif /* WINNT_NATIVE */
369 		    q = z;
370 		    while (*z)				/* calc # of /'s */
371 			if (*z++ == '/')
372 			    updirs++;
373 		    if ((Scp == 'C' && *q != '/'))
374 			updirs++;
375 
376 		    if (cp[1] == '0') {			/* print <x> or ...  */
377 			pdirs = 1;
378 			cp++;
379 		    }
380 		    if (cp[1] >= '1' && cp[1] <= '9') {	/* calc # to skip  */
381 			skip = cp[1] - '0';
382 			cp++;
383 		    }
384 		    else
385 			skip = 1;
386 
387 		    updirs -= skip;
388 		    while (skip-- > 0) {
389 			while ((z > q) && (*z != '/'))
390 			    z--;			/* back up */
391 			if (skip && z > q)
392 			    z--;
393 		    }
394 		    if (*z == '/' && z != q)
395 			z++;
396 		} /* . || C */
397 
398 							/* print ~[user] */
399 		if ((olduser) && ((Scp == '~') ||
400 		     (Scp == '.' && (pdirs || (!pdirs && updirs <= 0))) )) {
401 		    *p++ = attributes | '~';
402 		    if (p >= ep) break;
403 		    for (q = olduser; *q; *p++ = attributes | *q++)
404 			if (p >= ep) break;
405 		}
406 
407 			/* RWM - tell you how many dirs we've ignored */
408 			/*       and add '/' at front of this         */
409 		if (updirs > 0 && pdirs) {
410 		    if (p >= ep - 5) break;
411 		    if (adrof(STRellipsis)) {
412 			*p++ = attributes | '.';
413 			*p++ = attributes | '.';
414 			*p++ = attributes | '.';
415 		    } else {
416 			*p++ = attributes | '/';
417 			*p++ = attributes | '<';
418 			if (updirs > 9) {
419 			    *p++ = attributes | '9';
420 			    *p++ = attributes | '+';
421 			} else
422 			    *p++ = attributes | ('0' + updirs);
423 			*p++ = attributes | '>';
424 		    }
425 		}
426 
427 		for (; *z ; *p++ = attributes | *z++)
428 		    if (p >= ep) break;
429 		break;
430 			/* lukem: end of new directory prompt code */
431 
432 	    case 'n':
433 #ifndef HAVENOUTMP
434 		if (what == FMT_WHO) {
435 		    cz = (unsigned char *) who_info(info, 'n',
436 			(char *) cbuff, sizeof(cbuff));
437 		    for (; cz && *cz ; *p++ = attributes | *cz++)
438 			if (p >= ep) break;
439 		}
440 		else
441 #endif /* HAVENOUTMP */
442 		{
443 		    if ((z = varval(STRuser)) != STRNULL)
444 			for (; *z; *p++ = attributes | *z++)
445 			    if (p >= ep) break;
446 		}
447 		break;
448 	    case 'l':
449 #ifndef HAVENOUTMP
450 		if (what == FMT_WHO) {
451 		    cz = (unsigned char *) who_info(info, 'l',
452 			(char *) cbuff, sizeof(cbuff));
453 		    for (; cz && *cz ; *p++ = attributes | *cz++)
454 			if (p >= ep) break;
455 		}
456 		else
457 #endif /* HAVENOUTMP */
458 		{
459 		    if ((z = varval(STRtty)) != STRNULL)
460 			for (; *z; *p++ = attributes | *z++)
461 			    if (p >= ep) break;
462 		}
463 		break;
464 	    case 'd':
465 		for (cz = (unsigned char *) day_list[t->tm_wday]; *cz;
466 		    *p++ = attributes | *cz++)
467 		    if (p >= ep) break;
468 		break;
469 	    case 'D':
470 		if (p >= ep - 3) break;
471 		p = Itoa(t->tm_mday, p, 2, attributes);
472 		break;
473 	    case 'w':
474 		if (p >= ep - 5) break;
475 		for (cz = (unsigned char *) month_list[t->tm_mon]; *cz;
476 		    *p++ = attributes | *cz++)
477 		    if (p >= ep) break;
478 		break;
479 	    case 'W':
480 		if (p >= ep - 3) break;
481 		p = Itoa(t->tm_mon + 1, p, 2, attributes);
482 		break;
483 	    case 'y':
484 		if (p >= ep - 3) break;
485 		p = Itoa(t->tm_year % 100, p, 2, attributes);
486 		break;
487 	    case 'Y':
488 		if (p >= ep - 5) break;
489 		p = Itoa(t->tm_year + 1900, p, 4, attributes);
490 		break;
491 	    case 'S':		/* start standout */
492 		attributes |= STANDOUT;
493 		break;
494 	    case 'B':		/* start bold */
495 		attributes |= BOLD;
496 		break;
497 	    case 'U':		/* start underline */
498 		attributes |= UNDER;
499 		break;
500 	    case 's':		/* end standout */
501 		attributes &= ~STANDOUT;
502 		break;
503 	    case 'b':		/* end bold */
504 		attributes &= ~BOLD;
505 		break;
506 	    case 'u':		/* end underline */
507 		attributes &= ~UNDER;
508 		break;
509 	    case 'L':
510 		ClearToBottom();
511 		break;
512 
513 	    case 'j':
514 		{
515 		    Char buf[128], *ebuf, *q;
516 		    int njobs = -1;
517 		    struct process *pp;
518 		    for (pp = proclist.p_next; pp; pp = pp->p_next)
519 			njobs++;
520 		    /* make sure we have space */
521 		    ebuf = Itoa(njobs, buf, 1, attributes);
522 		    for (q = buf; q < ebuf; *p++ = *q++)
523 			if (p >= ep) break;
524 		    break;
525 		}
526 	    case '?':
527 		if ((z = varval(STRstatus)) != STRNULL)
528 		    for (; *z; *p++ = attributes | *z++)
529 			if (p >= ep) break;
530 		break;
531 	    case '$':
532 		sz = ep - p;
533 		(void) expdollar(&p, &cp, &sz, attributes);
534 		/* cp should point the last char of currnet % sequence */
535 		cp--;
536 		break;
537 	    case '%':
538 		*p++ = attributes | '%';
539 		break;
540 	    case '{':		/* literal characters start */
541 #if LITERAL == 0
542 		/*
543 		 * No literal capability, so skip all chars in the literal
544 		 * string
545 		 */
546 		while (*cp != '\0' && (*cp != '%' || cp[1] != '}'))
547 		    cp++;
548 #endif				/* LITERAL == 0 */
549 		attributes |= LITERAL;
550 		break;
551 	    case '}':		/* literal characters end */
552 		attributes &= ~LITERAL;
553 		break;
554 	    default:
555 #ifndef HAVENOUTMP
556 		if (*cp == 'a' && what == FMT_WHO) {
557 		    cz = (unsigned char *) who_info(info, 'a', (char *) cbuff,
558 			sizeof(cbuff));
559 		    for (; cz && *cz; *p++ = attributes | *cz++)
560 			if (p >= ep) break;
561 		}
562 		else
563 #endif /* HAVENOUTMP */
564 		{
565 		    if (p >= ep - 3) break;
566 		    *p++ = attributes | '%';
567 		    *p++ = attributes | *cp;
568 		}
569 		break;
570 	    }
571 	}
572 	else if (*cp == '\\' || *cp == '^')
573 	    *p++ = attributes | parseescape(&cp);
574 	else if (*cp == HIST) {	/* EGS: handle '!'s in prompts */
575 	    if (what == FMT_HISTORY)
576 		fmthist('h', info, (char *) cbuff, sizeof(cbuff));
577 	    else
578 		(void) xsnprintf((char *) cbuff, sizeof(cbuff), "%d", eventno + 1);
579 	    for (cz = cbuff; *cz; *p++ = attributes | *cz++)
580 		if (p >= ep) break;
581 	}
582 	else
583 	    *p++ = attributes | *cp;	/* normal character */
584     }
585     *p = '\0';
586 }
587 
588 Char *
589 expdollar(dstp, srcp, spp, attr)
590     Char **dstp;
591     const Char **srcp;
592     size_t *spp;
593     int	    attr;
594 {
595     struct varent *vp;
596     Char var[MAXVARLEN];
597     const Char *src = *srcp;
598     Char *val;
599     Char *dst = *dstp;
600     int i, curly = 0;
601 
602     /* found a variable, expand it */
603     for (i = 0; i < MAXVARLEN; i++) {
604 	var[i] = *++src & TRIM;
605 	if (i == 0 && var[i] == '{') {
606 	    curly = 1;
607 	    var[i] = *++src & TRIM;
608 	}
609 	if (!alnum(var[i])) {
610 
611 	    var[i] = '\0';
612 	    break;
613 	}
614     }
615     if (curly && (*src & TRIM) == '}')
616 	src++;
617 
618     vp = adrof(var);
619     val = (!vp) ? tgetenv(var) : NULL;
620     if (vp && vp->vec) {
621 	for (i = 0; vp->vec[i] != NULL; i++) {
622 	    for (val = vp->vec[i]; *spp > 0 && *val; (*spp)--)
623 		*dst++ = *val++ | attr;
624 	    if (vp->vec[i+1] && *spp > 0) {
625 		*dst++ = ' ' | attr;
626 		(*spp)--;
627 	    }
628 	}
629     }
630     else if (val) {
631 	for (; *spp > 0 && *val; (*spp)--)
632 	    *dst++ = *val++ | attr;
633     }
634     else {
635 	**dstp = '\0';
636 	*srcp = src;
637 	return NULL;
638     }
639     *dst = '\0';
640 
641     val = *dstp;
642     *srcp = src;
643     *dstp = dst;
644 
645     return val;
646 }
647