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