1232633Smp/* $Header: /p/tcsh/cvsroot/tcsh/tc.prompt.c,v 3.70 2011/10/27 22:41:06 christos Exp $ */
259243Sobrien/*
359243Sobrien * tc.prompt.c: Prompt printing stuff
459243Sobrien */
559243Sobrien/*-
659243Sobrien * Copyright (c) 1980, 1991 The Regents of the University of California.
759243Sobrien * All rights reserved.
859243Sobrien *
959243Sobrien * Redistribution and use in source and binary forms, with or without
1059243Sobrien * modification, are permitted provided that the following conditions
1159243Sobrien * are met:
1259243Sobrien * 1. Redistributions of source code must retain the above copyright
1359243Sobrien *    notice, this list of conditions and the following disclaimer.
1459243Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1559243Sobrien *    notice, this list of conditions and the following disclaimer in the
1659243Sobrien *    documentation and/or other materials provided with the distribution.
17100616Smp * 3. Neither the name of the University nor the names of its contributors
1859243Sobrien *    may be used to endorse or promote products derived from this software
1959243Sobrien *    without specific prior written permission.
2059243Sobrien *
2159243Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2259243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2359243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2459243Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2559243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2659243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2759243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2859243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2959243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3059243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3159243Sobrien * SUCH DAMAGE.
3259243Sobrien */
3359243Sobrien#include "sh.h"
3459243Sobrien
35232633SmpRCSID("$tcsh: tc.prompt.c,v 3.70 2011/10/27 22:41:06 christos Exp $")
3659243Sobrien
3759243Sobrien#include "ed.h"
3859243Sobrien#include "tw.h"
3959243Sobrien
4059243Sobrien/*
4159243Sobrien * kfk 21oct1983 -- add @ (time) and / ($cwd) in prompt.
4259243Sobrien * PWP 4/27/87 -- rearange for tcsh.
4359243Sobrien * mrdch@com.tau.edu.il 6/26/89 - added ~, T and .# - rearanged to switch()
4459243Sobrien *                 instead of if/elseif
4559243Sobrien * Luke Mewburn, <lukem@cs.rmit.edu.au>
4659243Sobrien *	6-Sep-91	changed date format
4759243Sobrien *	16-Feb-94	rewrote directory prompt code, added $ellipsis
4859243Sobrien *	29-Dec-96	added rprompt support
4959243Sobrien */
5059243Sobrien
51145479Smpstatic const char   *month_list[12];
52145479Smpstatic const char   *day_list[7];
5359243Sobrien
5459243Sobrienvoid
55167465Smpdateinit(void)
5659243Sobrien{
5759243Sobrien#ifdef notyet
5859243Sobrien  int i;
5959243Sobrien
6059243Sobrien  setlocale(LC_TIME, "");
6159243Sobrien
6259243Sobrien  for (i = 0; i < 12; i++)
6359243Sobrien      xfree((ptr_t) month_list[i]);
6459243Sobrien  month_list[0] = strsave(_time_info->abbrev_month[0]);
6559243Sobrien  month_list[1] = strsave(_time_info->abbrev_month[1]);
6659243Sobrien  month_list[2] = strsave(_time_info->abbrev_month[2]);
6759243Sobrien  month_list[3] = strsave(_time_info->abbrev_month[3]);
6859243Sobrien  month_list[4] = strsave(_time_info->abbrev_month[4]);
6959243Sobrien  month_list[5] = strsave(_time_info->abbrev_month[5]);
7059243Sobrien  month_list[6] = strsave(_time_info->abbrev_month[6]);
7159243Sobrien  month_list[7] = strsave(_time_info->abbrev_month[7]);
7259243Sobrien  month_list[8] = strsave(_time_info->abbrev_month[8]);
7359243Sobrien  month_list[9] = strsave(_time_info->abbrev_month[9]);
7459243Sobrien  month_list[10] = strsave(_time_info->abbrev_month[10]);
7559243Sobrien  month_list[11] = strsave(_time_info->abbrev_month[11]);
7659243Sobrien
7759243Sobrien  for (i = 0; i < 7; i++)
7859243Sobrien      xfree((ptr_t) day_list[i]);
7959243Sobrien  day_list[0] = strsave(_time_info->abbrev_wkday[0]);
8059243Sobrien  day_list[1] = strsave(_time_info->abbrev_wkday[1]);
8159243Sobrien  day_list[2] = strsave(_time_info->abbrev_wkday[2]);
8259243Sobrien  day_list[3] = strsave(_time_info->abbrev_wkday[3]);
8359243Sobrien  day_list[4] = strsave(_time_info->abbrev_wkday[4]);
8459243Sobrien  day_list[5] = strsave(_time_info->abbrev_wkday[5]);
8559243Sobrien  day_list[6] = strsave(_time_info->abbrev_wkday[6]);
8659243Sobrien#else
8759243Sobrien  month_list[0] = "Jan";
8859243Sobrien  month_list[1] = "Feb";
8959243Sobrien  month_list[2] = "Mar";
9059243Sobrien  month_list[3] = "Apr";
9159243Sobrien  month_list[4] = "May";
9259243Sobrien  month_list[5] = "Jun";
9359243Sobrien  month_list[6] = "Jul";
9459243Sobrien  month_list[7] = "Aug";
9559243Sobrien  month_list[8] = "Sep";
9659243Sobrien  month_list[9] = "Oct";
9759243Sobrien  month_list[10] = "Nov";
9859243Sobrien  month_list[11] = "Dec";
9959243Sobrien
10059243Sobrien  day_list[0] = "Sun";
10159243Sobrien  day_list[1] = "Mon";
10259243Sobrien  day_list[2] = "Tue";
10359243Sobrien  day_list[3] = "Wed";
10459243Sobrien  day_list[4] = "Thu";
10559243Sobrien  day_list[5] = "Fri";
10659243Sobrien  day_list[6] = "Sat";
10759243Sobrien#endif
10859243Sobrien}
10959243Sobrien
11059243Sobrienvoid
111167465Smpprintprompt(int promptno, const char *str)
11259243Sobrien{
113167465Smp    static  const Char *ocp = NULL;
114145479Smp    static  const char *ostr = NULL;
11559243Sobrien    time_t  lclock = time(NULL);
116167465Smp    const Char *cp;
11759243Sobrien
11859243Sobrien    switch (promptno) {
11959243Sobrien    default:
12059243Sobrien    case 0:
12159243Sobrien	cp = varval(STRprompt);
12259243Sobrien	break;
12359243Sobrien    case 1:
12459243Sobrien	cp = varval(STRprompt2);
12559243Sobrien	break;
12659243Sobrien    case 2:
12759243Sobrien	cp = varval(STRprompt3);
12859243Sobrien	break;
12959243Sobrien    case 3:
13059243Sobrien	if (ocp != NULL) {
13159243Sobrien	    cp = ocp;
13259243Sobrien	    str = ostr;
13359243Sobrien	}
13459243Sobrien	else
13559243Sobrien	    cp = varval(STRprompt);
13659243Sobrien	break;
13759243Sobrien    }
13859243Sobrien
13959243Sobrien    if (promptno < 2) {
14059243Sobrien	ocp = cp;
14159243Sobrien	ostr = str;
14259243Sobrien    }
14359243Sobrien
144167465Smp    xfree(Prompt);
145167465Smp    Prompt = NULL;
146167465Smp    Prompt = tprintf(FMT_PROMPT, cp, str, lclock, NULL);
14759243Sobrien    if (!editing) {
148167465Smp	for (cp = Prompt; *cp ; )
149145479Smp	    (void) putwraw(*cp++);
15059243Sobrien	SetAttributes(0);
15159243Sobrien	flush();
15259243Sobrien    }
15359243Sobrien
154167465Smp    xfree(RPrompt);
155167465Smp    RPrompt = NULL;
15659243Sobrien    if (promptno == 0) {	/* determine rprompt if using main prompt */
15759243Sobrien	cp = varval(STRrprompt);
158167465Smp	RPrompt = tprintf(FMT_PROMPT, cp, NULL, lclock, NULL);
15959243Sobrien				/* if not editing, put rprompt after prompt */
160167465Smp	if (!editing && RPrompt[0] != '\0') {
161167465Smp	    for (cp = RPrompt; *cp ; )
162145479Smp		(void) putwraw(*cp++);
16359243Sobrien	    SetAttributes(0);
16459243Sobrien	    putraw(' ');
16559243Sobrien	    flush();
16659243Sobrien	}
16759243Sobrien    }
16859243Sobrien}
16959243Sobrien
170167465Smpstatic void
171167465Smptprintf_append_mbs(struct Strbuf *buf, const char *mbs, Char attributes)
17259243Sobrien{
173167465Smp    while (*mbs != 0) {
174167465Smp	Char wc;
175167465Smp
176167465Smp	mbs += one_mbtowc(&wc, mbs, MB_LEN_MAX);
177167465Smp	Strbuf_append1(buf, wc | attributes);
178167465Smp    }
179167465Smp}
180167465Smp
181167465SmpChar *
182167465Smptprintf(int what, const Char *fmt, const char *str, time_t tim, ptr_t info)
183167465Smp{
184167465Smp    struct Strbuf buf = Strbuf_INIT;
18559243Sobrien    Char   *z, *q;
18659243Sobrien    Char    attributes = 0;
18759243Sobrien    static int print_prompt_did_ding = 0;
188167465Smp    char *cz;
18959243Sobrien
190167465Smp    Char *p;
19159243Sobrien    const Char *cp = fmt;
19259243Sobrien    Char Scp;
19359243Sobrien    struct tm *t = localtime(&tim);
19459243Sobrien
19559243Sobrien			/* prompt stuff */
196167465Smp    static Char *olduser = NULL;
197100616Smp    int updirs;
198167465Smp    size_t pdirs;
19959243Sobrien
200167465Smp    cleanup_push(&buf, Strbuf_cleanup);
20159243Sobrien    for (; *cp; cp++) {
20259243Sobrien	if ((*cp == '%') && ! (cp[1] == '\0')) {
20359243Sobrien	    cp++;
20459243Sobrien	    switch (*cp) {
20559243Sobrien	    case 'R':
206145479Smp		if (what == FMT_HISTORY) {
207167465Smp		    cz = fmthist('R', info);
208167465Smp		    tprintf_append_mbs(&buf, cz, attributes);
209167465Smp		    xfree(cz);
210167465Smp		} else {
211167465Smp		    if (str != NULL)
212167465Smp			tprintf_append_mbs(&buf, str, attributes);
213167465Smp		}
21459243Sobrien		break;
21559243Sobrien	    case '#':
216232633Smp		Scp = (uid == 0 || euid == 0) ? PRCHROOT : PRCH;
217232633Smp		if (Scp != '\0')
218232633Smp		    Strbuf_append1(&buf, attributes | Scp);
21959243Sobrien		break;
22059243Sobrien	    case '!':
22159243Sobrien	    case 'h':
22259243Sobrien		switch (what) {
22359243Sobrien		case FMT_HISTORY:
224167465Smp		    cz = fmthist('h', info);
22559243Sobrien		    break;
22659243Sobrien		case FMT_SCHED:
227167465Smp		    cz = xasprintf("%d", *(int *)info);
22859243Sobrien		    break;
22959243Sobrien		default:
230167465Smp		    cz = xasprintf("%d", eventno + 1);
23159243Sobrien		    break;
23259243Sobrien		}
233167465Smp		tprintf_append_mbs(&buf, cz, attributes);
234167465Smp		xfree(cz);
23559243Sobrien		break;
23659243Sobrien	    case 'T':		/* 24 hour format	 */
23759243Sobrien	    case '@':
23859243Sobrien	    case 't':		/* 12 hour am/pm format */
23959243Sobrien	    case 'p':		/* With seconds	*/
24059243Sobrien	    case 'P':
24159243Sobrien		{
24259243Sobrien		    char    ampm = 'a';
24359243Sobrien		    int     hr = t->tm_hour;
24459243Sobrien
24559243Sobrien		    /* addition by Hans J. Albertsson */
24659243Sobrien		    /* and another adapted from Justin Bur */
24759243Sobrien		    if (adrof(STRampm) || (*cp != 'T' && *cp != 'P')) {
24859243Sobrien			if (hr >= 12) {
24959243Sobrien			    if (hr > 12)
25059243Sobrien				hr -= 12;
25159243Sobrien			    ampm = 'p';
25259243Sobrien			}
25359243Sobrien			else if (hr == 0)
25459243Sobrien			    hr = 12;
25559243Sobrien		    }		/* else do a 24 hour clock */
25659243Sobrien
25759243Sobrien		    /* "DING!" stuff by Hans also */
25859243Sobrien		    if (t->tm_min || print_prompt_did_ding ||
25959243Sobrien			what != FMT_PROMPT || adrof(STRnoding)) {
26059243Sobrien			if (t->tm_min)
26159243Sobrien			    print_prompt_did_ding = 0;
262167465Smp			/*
263167465Smp			 * Pad hour to 2 characters if padhour is set,
264167465Smp			 * by ADAM David Alan Martin
265167465Smp			 */
266167465Smp			p = Itoa(hr, adrof(STRpadhour) ? 2 : 0, attributes);
267167465Smp			Strbuf_append(&buf, p);
268167465Smp			xfree(p);
269167465Smp			Strbuf_append1(&buf, attributes | ':');
270167465Smp			p = Itoa(t->tm_min, 2, attributes);
271167465Smp			Strbuf_append(&buf, p);
272167465Smp			xfree(p);
27359243Sobrien			if (*cp == 'p' || *cp == 'P') {
274167465Smp			    Strbuf_append1(&buf, attributes | ':');
275167465Smp			    p = Itoa(t->tm_sec, 2, attributes);
276167465Smp			    Strbuf_append(&buf, p);
277167465Smp			    xfree(p);
27859243Sobrien			}
27959243Sobrien			if (adrof(STRampm) || (*cp != 'T' && *cp != 'P')) {
280167465Smp			    Strbuf_append1(&buf, attributes | ampm);
281167465Smp			    Strbuf_append1(&buf, attributes | 'm');
28259243Sobrien			}
28359243Sobrien		    }
28459243Sobrien		    else {	/* we need to ding */
285167465Smp			size_t i;
28659243Sobrien
287167465Smp			for (i = 0; STRDING[i] != 0; i++)
288167465Smp			    Strbuf_append1(&buf, attributes | STRDING[i]);
28959243Sobrien			print_prompt_did_ding = 1;
29059243Sobrien		    }
29159243Sobrien		}
29259243Sobrien		break;
29359243Sobrien
29459243Sobrien	    case 'M':
29559243Sobrien#ifndef HAVENOUTMP
29659243Sobrien		if (what == FMT_WHO)
297167465Smp		    cz = who_info(info, 'M');
29859243Sobrien		else
29959243Sobrien#endif /* HAVENOUTMP */
300167465Smp		    cz = getenv("HOST");
30159243Sobrien		/*
30259243Sobrien		 * Bug pointed out by Laurent Dami <dami@cui.unige.ch>: don't
30359243Sobrien		 * derefrence that NULL (if HOST is not set)...
30459243Sobrien		 */
30559243Sobrien		if (cz != NULL)
306167465Smp		    tprintf_append_mbs(&buf, cz, attributes);
307167465Smp		if (what == FMT_WHO)
308167465Smp		    xfree(cz);
30959243Sobrien		break;
31059243Sobrien
311167465Smp	    case 'm': {
312167465Smp		char *scz = NULL;
31359243Sobrien#ifndef HAVENOUTMP
31459243Sobrien		if (what == FMT_WHO)
315167465Smp		    scz = cz = who_info(info, 'm');
316167465Smp		else
31759243Sobrien#endif /* HAVENOUTMP */
318167465Smp		    cz = getenv("HOST");
31959243Sobrien
32059243Sobrien		if (cz != NULL)
321167465Smp		    while (*cz != 0 && (what == FMT_WHO || *cz != '.')) {
322167465Smp			Char wc;
323167465Smp
324167465Smp			cz += one_mbtowc(&wc, cz, MB_LEN_MAX);
325167465Smp			Strbuf_append1(&buf, wc | attributes);
326145479Smp		    }
327167465Smp		if (scz)
328167465Smp		    xfree(scz);
32959243Sobrien		break;
330167465Smp	    }
33159243Sobrien
33259243Sobrien			/* lukem: new directory prompt code */
33359243Sobrien	    case '~':
33459243Sobrien	    case '/':
33559243Sobrien	    case '.':
33659243Sobrien	    case 'c':
33759243Sobrien	    case 'C':
33859243Sobrien		Scp = *cp;
33959243Sobrien		if (Scp == 'c')		/* store format type (c == .) */
34059243Sobrien		    Scp = '.';
34159243Sobrien		if ((z = varval(STRcwd)) == STRNULL)
34259243Sobrien		    break;		/* no cwd, so don't do anything */
34359243Sobrien
34459243Sobrien			/* show ~ whenever possible - a la dirs */
34559243Sobrien		if (Scp == '~' || Scp == '.' ) {
346167465Smp		    static Char *olddir = NULL;
347167465Smp
34859243Sobrien		    if (tlength == 0 || olddir != z) {
34959243Sobrien			olddir = z;		/* have we changed dir? */
35059243Sobrien			olduser = getusername(&olddir);
35159243Sobrien		    }
35259243Sobrien		    if (olduser)
35359243Sobrien			z = olddir;
35459243Sobrien		}
35559243Sobrien		updirs = pdirs = 0;
35659243Sobrien
35759243Sobrien			/* option to determine fixed # of dirs from path */
35859243Sobrien		if (Scp == '.' || Scp == 'C') {
35959243Sobrien		    int skip;
36069408Sache#ifdef WINNT_NATIVE
361145479Smp		    Char *oldz = z;
36259243Sobrien		    if (z[1] == ':') {
363167465Smp			Strbuf_append1(&buf, attributes | *z++);
364167465Smp			Strbuf_append1(&buf, attributes | *z++);
36559243Sobrien		    }
366167465Smp		    if (*z == '/' && z[1] == '/') {
367167465Smp			Strbuf_append1(&buf, attributes | *z++);
368167465Smp			Strbuf_append1(&buf, attributes | *z++);
369167465Smp			do {
370167465Smp			    Strbuf_append1(&buf, attributes | *z++);
371167465Smp			} while(*z != '/');
372167465Smp		    }
37369408Sache#endif /* WINNT_NATIVE */
37459243Sobrien		    q = z;
37559243Sobrien		    while (*z)				/* calc # of /'s */
37659243Sobrien			if (*z++ == '/')
37759243Sobrien			    updirs++;
378145479Smp
379145479Smp#ifdef WINNT_NATIVE
380145479Smp		    /*
381145479Smp		     * for format type c, prompt will be following...
382145479Smp		     * c:/path                => c:/path
383145479Smp		     * c:/path/to             => c:to
384145479Smp		     * //machine/share        => //machine/share
385145479Smp		     * //machine/share/folder => //machine:folder
386145479Smp		     */
387145479Smp		    if (oldz[0] == '/' && oldz[1] == '/' && updirs > 1)
388167465Smp			Strbuf_append1(&buf, attributes | ':');
389145479Smp#endif /* WINNT_NATIVE */
39059243Sobrien		    if ((Scp == 'C' && *q != '/'))
39159243Sobrien			updirs++;
39259243Sobrien
39359243Sobrien		    if (cp[1] == '0') {			/* print <x> or ...  */
39459243Sobrien			pdirs = 1;
39559243Sobrien			cp++;
39659243Sobrien		    }
39759243Sobrien		    if (cp[1] >= '1' && cp[1] <= '9') {	/* calc # to skip  */
39859243Sobrien			skip = cp[1] - '0';
39959243Sobrien			cp++;
40059243Sobrien		    }
40159243Sobrien		    else
40259243Sobrien			skip = 1;
40359243Sobrien
40459243Sobrien		    updirs -= skip;
40559243Sobrien		    while (skip-- > 0) {
40659243Sobrien			while ((z > q) && (*z != '/'))
40759243Sobrien			    z--;			/* back up */
40859243Sobrien			if (skip && z > q)
40959243Sobrien			    z--;
41059243Sobrien		    }
41159243Sobrien		    if (*z == '/' && z != q)
41259243Sobrien			z++;
41359243Sobrien		} /* . || C */
41459243Sobrien
41559243Sobrien							/* print ~[user] */
41659243Sobrien		if ((olduser) && ((Scp == '~') ||
41759243Sobrien		     (Scp == '.' && (pdirs || (!pdirs && updirs <= 0))) )) {
418167465Smp		    Strbuf_append1(&buf, attributes | '~');
419167465Smp		    for (q = olduser; *q; q++)
420167465Smp			Strbuf_append1(&buf, attributes | *q);
42159243Sobrien		}
42259243Sobrien
42359243Sobrien			/* RWM - tell you how many dirs we've ignored */
42459243Sobrien			/*       and add '/' at front of this         */
42559243Sobrien		if (updirs > 0 && pdirs) {
42659243Sobrien		    if (adrof(STRellipsis)) {
427167465Smp			Strbuf_append1(&buf, attributes | '.');
428167465Smp			Strbuf_append1(&buf, attributes | '.');
429167465Smp			Strbuf_append1(&buf, attributes | '.');
43059243Sobrien		    } else {
431167465Smp			Strbuf_append1(&buf, attributes | '/');
432167465Smp			Strbuf_append1(&buf, attributes | '<');
43359243Sobrien			if (updirs > 9) {
434167465Smp			    Strbuf_append1(&buf, attributes | '9');
435167465Smp			    Strbuf_append1(&buf, attributes | '+');
43659243Sobrien			} else
437167465Smp			    Strbuf_append1(&buf, attributes | ('0' + updirs));
438167465Smp			Strbuf_append1(&buf, attributes | '>');
43959243Sobrien		    }
44059243Sobrien		}
441167465Smp
442167465Smp		while (*z)
443167465Smp		    Strbuf_append1(&buf, attributes | *z++);
44459243Sobrien		break;
44559243Sobrien			/* lukem: end of new directory prompt code */
44659243Sobrien
44759243Sobrien	    case 'n':
44859243Sobrien#ifndef HAVENOUTMP
44959243Sobrien		if (what == FMT_WHO) {
450167465Smp		    cz = who_info(info, 'n');
451167465Smp		    tprintf_append_mbs(&buf, cz, attributes);
452167465Smp		    xfree(cz);
45359243Sobrien		}
45459243Sobrien		else
45559243Sobrien#endif /* HAVENOUTMP */
45659243Sobrien		{
45759243Sobrien		    if ((z = varval(STRuser)) != STRNULL)
458167465Smp			while (*z)
459167465Smp			    Strbuf_append1(&buf, attributes | *z++);
46059243Sobrien		}
46159243Sobrien		break;
462232633Smp	    case 'N':
463232633Smp		if ((z = varval(STReuser)) != STRNULL)
464232633Smp		    while (*z)
465232633Smp			Strbuf_append1(&buf, attributes | *z++);
466232633Smp		break;
46759243Sobrien	    case 'l':
46859243Sobrien#ifndef HAVENOUTMP
46959243Sobrien		if (what == FMT_WHO) {
470167465Smp		    cz = who_info(info, 'l');
471167465Smp		    tprintf_append_mbs(&buf, cz, attributes);
472167465Smp		    xfree(cz);
47359243Sobrien		}
47459243Sobrien		else
47559243Sobrien#endif /* HAVENOUTMP */
47659243Sobrien		{
47759243Sobrien		    if ((z = varval(STRtty)) != STRNULL)
478167465Smp			while (*z)
479167465Smp			    Strbuf_append1(&buf, attributes | *z++);
48059243Sobrien		}
48159243Sobrien		break;
48259243Sobrien	    case 'd':
483167465Smp		tprintf_append_mbs(&buf, day_list[t->tm_wday], attributes);
48459243Sobrien		break;
48559243Sobrien	    case 'D':
486167465Smp		p = Itoa(t->tm_mday, 2, attributes);
487167465Smp		Strbuf_append(&buf, p);
488167465Smp		xfree(p);
48959243Sobrien		break;
49059243Sobrien	    case 'w':
491167465Smp		tprintf_append_mbs(&buf, month_list[t->tm_mon], attributes);
49259243Sobrien		break;
49359243Sobrien	    case 'W':
494167465Smp		p = Itoa(t->tm_mon + 1, 2, attributes);
495167465Smp		Strbuf_append(&buf, p);
496167465Smp		xfree(p);
49759243Sobrien		break;
49859243Sobrien	    case 'y':
499167465Smp		p = Itoa(t->tm_year % 100, 2, attributes);
500167465Smp		Strbuf_append(&buf, p);
501167465Smp		xfree(p);
50259243Sobrien		break;
50359243Sobrien	    case 'Y':
504167465Smp		p = Itoa(t->tm_year + 1900, 4, attributes);
505167465Smp		Strbuf_append(&buf, p);
506167465Smp		xfree(p);
50759243Sobrien		break;
50859243Sobrien	    case 'S':		/* start standout */
50959243Sobrien		attributes |= STANDOUT;
51059243Sobrien		break;
51159243Sobrien	    case 'B':		/* start bold */
51259243Sobrien		attributes |= BOLD;
51359243Sobrien		break;
51459243Sobrien	    case 'U':		/* start underline */
51559243Sobrien		attributes |= UNDER;
51659243Sobrien		break;
51759243Sobrien	    case 's':		/* end standout */
51859243Sobrien		attributes &= ~STANDOUT;
51959243Sobrien		break;
52059243Sobrien	    case 'b':		/* end bold */
52159243Sobrien		attributes &= ~BOLD;
52259243Sobrien		break;
52359243Sobrien	    case 'u':		/* end underline */
52459243Sobrien		attributes &= ~UNDER;
52559243Sobrien		break;
52659243Sobrien	    case 'L':
52759243Sobrien		ClearToBottom();
52859243Sobrien		break;
529100616Smp
530100616Smp	    case 'j':
531100616Smp		{
532100616Smp		    int njobs = -1;
533100616Smp		    struct process *pp;
534167465Smp
535100616Smp		    for (pp = proclist.p_next; pp; pp = pp->p_next)
536100616Smp			njobs++;
537232633Smp		    if (njobs == -1)
538232633Smp			njobs++;
539167465Smp		    p = Itoa(njobs, 1, attributes);
540167465Smp		    Strbuf_append(&buf, p);
541167465Smp		    xfree(p);
542100616Smp		    break;
543100616Smp		}
54459243Sobrien	    case '?':
54559243Sobrien		if ((z = varval(STRstatus)) != STRNULL)
546167465Smp		    while (*z)
547167465Smp			Strbuf_append1(&buf, attributes | *z++);
54859243Sobrien		break;
54959243Sobrien	    case '$':
550167465Smp		expdollar(&buf, &cp, attributes);
551167465Smp		/* cp should point the last char of current % sequence */
552100616Smp		cp--;
55359243Sobrien		break;
55459243Sobrien	    case '%':
555167465Smp		Strbuf_append1(&buf, attributes | '%');
55659243Sobrien		break;
55759243Sobrien	    case '{':		/* literal characters start */
55859243Sobrien#if LITERAL == 0
55959243Sobrien		/*
56059243Sobrien		 * No literal capability, so skip all chars in the literal
56159243Sobrien		 * string
56259243Sobrien		 */
563167465Smp		while (*cp != '\0' && (cp[-1] != '%' || *cp != '}'))
56459243Sobrien		    cp++;
56559243Sobrien#endif				/* LITERAL == 0 */
56659243Sobrien		attributes |= LITERAL;
56759243Sobrien		break;
56859243Sobrien	    case '}':		/* literal characters end */
56959243Sobrien		attributes &= ~LITERAL;
57059243Sobrien		break;
57159243Sobrien	    default:
57259243Sobrien#ifndef HAVENOUTMP
57359243Sobrien		if (*cp == 'a' && what == FMT_WHO) {
574167465Smp		    cz = who_info(info, 'a');
575167465Smp		    tprintf_append_mbs(&buf, cz, attributes);
576167465Smp		    xfree(cz);
57759243Sobrien		}
578167465Smp		else
57959243Sobrien#endif /* HAVENOUTMP */
58059243Sobrien		{
581167465Smp		    Strbuf_append1(&buf, attributes | '%');
582167465Smp		    Strbuf_append1(&buf, attributes | *cp);
58359243Sobrien		}
58459243Sobrien		break;
58559243Sobrien	    }
58659243Sobrien	}
587167465Smp	else if (*cp == '\\' || *cp == '^')
588167465Smp	    Strbuf_append1(&buf, attributes | parseescape(&cp));
58959243Sobrien	else if (*cp == HIST) {	/* EGS: handle '!'s in prompts */
590167465Smp	    if (what == FMT_HISTORY)
591167465Smp		cz = fmthist('h', info);
59259243Sobrien	    else
593167465Smp		cz = xasprintf("%d", eventno + 1);
594167465Smp	    tprintf_append_mbs(&buf, cz, attributes);
595167465Smp	    xfree(cz);
59659243Sobrien	}
597167465Smp	else
598167465Smp	    Strbuf_append1(&buf, attributes | *cp); /* normal character */
59959243Sobrien    }
600167465Smp    cleanup_ignore(&buf);
601167465Smp    cleanup_until(&buf);
602167465Smp    return Strbuf_finish(&buf);
60359243Sobrien}
60459243Sobrien
605167465Smpint
606167465Smpexpdollar(struct Strbuf *buf, const Char **srcp, Char attr)
60759243Sobrien{
60859243Sobrien    struct varent *vp;
60959243Sobrien    const Char *src = *srcp;
610167465Smp    Char *var, *val;
611167465Smp    size_t i;
612167465Smp    int curly = 0;
61359243Sobrien
61459243Sobrien    /* found a variable, expand it */
615167465Smp    var = xmalloc((Strlen(src) + 1) * sizeof (*var));
616167465Smp    for (i = 0; ; i++) {
61759243Sobrien	var[i] = *++src & TRIM;
61859243Sobrien	if (i == 0 && var[i] == '{') {
61959243Sobrien	    curly = 1;
62059243Sobrien	    var[i] = *++src & TRIM;
62159243Sobrien	}
622167465Smp	if (!alnum(var[i]) && var[i] != '_') {
623167465Smp
62459243Sobrien	    var[i] = '\0';
62559243Sobrien	    break;
62659243Sobrien	}
62759243Sobrien    }
62859243Sobrien    if (curly && (*src & TRIM) == '}')
62959243Sobrien	src++;
63059243Sobrien
63159243Sobrien    vp = adrof(var);
632100616Smp    if (vp && vp->vec) {
63359243Sobrien	for (i = 0; vp->vec[i] != NULL; i++) {
634167465Smp	    for (val = vp->vec[i]; *val; val++)
635167465Smp		if (*val != '\n' && *val != '\r')
636167465Smp		    Strbuf_append1(buf, *val | attr);
637167465Smp	    if (vp->vec[i+1])
638167465Smp		Strbuf_append1(buf, ' ' | attr);
63959243Sobrien	}
64059243Sobrien    }
64159243Sobrien    else {
642167465Smp	val = (!vp) ? tgetenv(var) : NULL;
643167465Smp	if (val) {
644167465Smp	    for (; *val; val++)
645167465Smp		if (*val != '\n' && *val != '\r')
646167465Smp		    Strbuf_append1(buf, *val | attr);
647167465Smp	} else {
648167465Smp	    *srcp = src;
649167465Smp	    xfree(var);
650167465Smp	    return 0;
651167465Smp	}
65259243Sobrien    }
65359243Sobrien
65459243Sobrien    *srcp = src;
655167465Smp    xfree(var);
656167465Smp    return 1;
65759243Sobrien}
658