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