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