tc.prompt.c revision 59419
1/* $Header: /src/pub/tcsh/tc.prompt.c,v 3.38 2000/04/18 19:40:46 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
39RCSID("$Id: tc.prompt.c,v 3.38 2000/04/18 19:40:46 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
55static char   *month_list[12];
56static char   *day_list[7];
57
58void
59dateinit()
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
114void
115printprompt(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
176void
177tprintf(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 (str != 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
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 */
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, &pdirs, 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
575Char *
576expdollar(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