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