/* $Header: /p/tcsh/cvsroot/tcsh/tc.prompt.c,v 3.70 2011/10/27 22:41:06 christos Exp $ */ /* * tc.prompt.c: Prompt printing stuff */ /*- * Copyright (c) 1980, 1991 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "sh.h" RCSID("$tcsh: tc.prompt.c,v 3.70 2011/10/27 22:41:06 christos Exp $") #include "ed.h" #include "tw.h" /* * kfk 21oct1983 -- add @ (time) and / ($cwd) in prompt. * PWP 4/27/87 -- rearange for tcsh. * mrdch@com.tau.edu.il 6/26/89 - added ~, T and .# - rearanged to switch() * instead of if/elseif * Luke Mewburn, * 6-Sep-91 changed date format * 16-Feb-94 rewrote directory prompt code, added $ellipsis * 29-Dec-96 added rprompt support */ static const char *month_list[12]; static const char *day_list[7]; void dateinit(void) { #ifdef notyet int i; setlocale(LC_TIME, ""); for (i = 0; i < 12; i++) xfree((ptr_t) month_list[i]); month_list[0] = strsave(_time_info->abbrev_month[0]); month_list[1] = strsave(_time_info->abbrev_month[1]); month_list[2] = strsave(_time_info->abbrev_month[2]); month_list[3] = strsave(_time_info->abbrev_month[3]); month_list[4] = strsave(_time_info->abbrev_month[4]); month_list[5] = strsave(_time_info->abbrev_month[5]); month_list[6] = strsave(_time_info->abbrev_month[6]); month_list[7] = strsave(_time_info->abbrev_month[7]); month_list[8] = strsave(_time_info->abbrev_month[8]); month_list[9] = strsave(_time_info->abbrev_month[9]); month_list[10] = strsave(_time_info->abbrev_month[10]); month_list[11] = strsave(_time_info->abbrev_month[11]); for (i = 0; i < 7; i++) xfree((ptr_t) day_list[i]); day_list[0] = strsave(_time_info->abbrev_wkday[0]); day_list[1] = strsave(_time_info->abbrev_wkday[1]); day_list[2] = strsave(_time_info->abbrev_wkday[2]); day_list[3] = strsave(_time_info->abbrev_wkday[3]); day_list[4] = strsave(_time_info->abbrev_wkday[4]); day_list[5] = strsave(_time_info->abbrev_wkday[5]); day_list[6] = strsave(_time_info->abbrev_wkday[6]); #else month_list[0] = "Jan"; month_list[1] = "Feb"; month_list[2] = "Mar"; month_list[3] = "Apr"; month_list[4] = "May"; month_list[5] = "Jun"; month_list[6] = "Jul"; month_list[7] = "Aug"; month_list[8] = "Sep"; month_list[9] = "Oct"; month_list[10] = "Nov"; month_list[11] = "Dec"; day_list[0] = "Sun"; day_list[1] = "Mon"; day_list[2] = "Tue"; day_list[3] = "Wed"; day_list[4] = "Thu"; day_list[5] = "Fri"; day_list[6] = "Sat"; #endif } void printprompt(int promptno, const char *str) { static const Char *ocp = NULL; static const char *ostr = NULL; time_t lclock = time(NULL); const Char *cp; switch (promptno) { default: case 0: cp = varval(STRprompt); break; case 1: cp = varval(STRprompt2); break; case 2: cp = varval(STRprompt3); break; case 3: if (ocp != NULL) { cp = ocp; str = ostr; } else cp = varval(STRprompt); break; } if (promptno < 2) { ocp = cp; ostr = str; } xfree(Prompt); Prompt = NULL; Prompt = tprintf(FMT_PROMPT, cp, str, lclock, NULL); if (!editing) { for (cp = Prompt; *cp ; ) (void) putwraw(*cp++); SetAttributes(0); flush(); } xfree(RPrompt); RPrompt = NULL; if (promptno == 0) { /* determine rprompt if using main prompt */ cp = varval(STRrprompt); RPrompt = tprintf(FMT_PROMPT, cp, NULL, lclock, NULL); /* if not editing, put rprompt after prompt */ if (!editing && RPrompt[0] != '\0') { for (cp = RPrompt; *cp ; ) (void) putwraw(*cp++); SetAttributes(0); putraw(' '); flush(); } } } static void tprintf_append_mbs(struct Strbuf *buf, const char *mbs, Char attributes) { while (*mbs != 0) { Char wc; mbs += one_mbtowc(&wc, mbs, MB_LEN_MAX); Strbuf_append1(buf, wc | attributes); } } Char * tprintf(int what, const Char *fmt, const char *str, time_t tim, ptr_t info) { struct Strbuf buf = Strbuf_INIT; Char *z, *q; Char attributes = 0; static int print_prompt_did_ding = 0; char *cz; Char *p; const Char *cp = fmt; Char Scp; struct tm *t = localtime(&tim); /* prompt stuff */ static Char *olduser = NULL; int updirs; size_t pdirs; cleanup_push(&buf, Strbuf_cleanup); for (; *cp; cp++) { if ((*cp == '%') && ! (cp[1] == '\0')) { cp++; switch (*cp) { case 'R': if (what == FMT_HISTORY) { cz = fmthist('R', info); tprintf_append_mbs(&buf, cz, attributes); xfree(cz); } else { if (str != NULL) tprintf_append_mbs(&buf, str, attributes); } break; case '#': Scp = (uid == 0 || euid == 0) ? PRCHROOT : PRCH; if (Scp != '\0') Strbuf_append1(&buf, attributes | Scp); break; case '!': case 'h': switch (what) { case FMT_HISTORY: cz = fmthist('h', info); break; case FMT_SCHED: cz = xasprintf("%d", *(int *)info); break; default: cz = xasprintf("%d", eventno + 1); break; } tprintf_append_mbs(&buf, cz, attributes); xfree(cz); break; case 'T': /* 24 hour format */ case '@': case 't': /* 12 hour am/pm format */ case 'p': /* With seconds */ case 'P': { char ampm = 'a'; int hr = t->tm_hour; /* addition by Hans J. Albertsson */ /* and another adapted from Justin Bur */ if (adrof(STRampm) || (*cp != 'T' && *cp != 'P')) { if (hr >= 12) { if (hr > 12) hr -= 12; ampm = 'p'; } else if (hr == 0) hr = 12; } /* else do a 24 hour clock */ /* "DING!" stuff by Hans also */ if (t->tm_min || print_prompt_did_ding || what != FMT_PROMPT || adrof(STRnoding)) { if (t->tm_min) print_prompt_did_ding = 0; /* * Pad hour to 2 characters if padhour is set, * by ADAM David Alan Martin */ p = Itoa(hr, adrof(STRpadhour) ? 2 : 0, attributes); Strbuf_append(&buf, p); xfree(p); Strbuf_append1(&buf, attributes | ':'); p = Itoa(t->tm_min, 2, attributes); Strbuf_append(&buf, p); xfree(p); if (*cp == 'p' || *cp == 'P') { Strbuf_append1(&buf, attributes | ':'); p = Itoa(t->tm_sec, 2, attributes); Strbuf_append(&buf, p); xfree(p); } if (adrof(STRampm) || (*cp != 'T' && *cp != 'P')) { Strbuf_append1(&buf, attributes | ampm); Strbuf_append1(&buf, attributes | 'm'); } } else { /* we need to ding */ size_t i; for (i = 0; STRDING[i] != 0; i++) Strbuf_append1(&buf, attributes | STRDING[i]); print_prompt_did_ding = 1; } } break; case 'M': #ifndef HAVENOUTMP if (what == FMT_WHO) cz = who_info(info, 'M'); else #endif /* HAVENOUTMP */ cz = getenv("HOST"); /* * Bug pointed out by Laurent Dami : don't * derefrence that NULL (if HOST is not set)... */ if (cz != NULL) tprintf_append_mbs(&buf, cz, attributes); if (what == FMT_WHO) xfree(cz); break; case 'm': { char *scz = NULL; #ifndef HAVENOUTMP if (what == FMT_WHO) scz = cz = who_info(info, 'm'); else #endif /* HAVENOUTMP */ cz = getenv("HOST"); if (cz != NULL) while (*cz != 0 && (what == FMT_WHO || *cz != '.')) { Char wc; cz += one_mbtowc(&wc, cz, MB_LEN_MAX); Strbuf_append1(&buf, wc | attributes); } if (scz) xfree(scz); break; } /* lukem: new directory prompt code */ case '~': case '/': case '.': case 'c': case 'C': Scp = *cp; if (Scp == 'c') /* store format type (c == .) */ Scp = '.'; if ((z = varval(STRcwd)) == STRNULL) break; /* no cwd, so don't do anything */ /* show ~ whenever possible - a la dirs */ if (Scp == '~' || Scp == '.' ) { static Char *olddir = NULL; if (tlength == 0 || olddir != z) { olddir = z; /* have we changed dir? */ olduser = getusername(&olddir); } if (olduser) z = olddir; } updirs = pdirs = 0; /* option to determine fixed # of dirs from path */ if (Scp == '.' || Scp == 'C') { int skip; #ifdef WINNT_NATIVE Char *oldz = z; if (z[1] == ':') { Strbuf_append1(&buf, attributes | *z++); Strbuf_append1(&buf, attributes | *z++); } if (*z == '/' && z[1] == '/') { Strbuf_append1(&buf, attributes | *z++); Strbuf_append1(&buf, attributes | *z++); do { Strbuf_append1(&buf, attributes | *z++); } while(*z != '/'); } #endif /* WINNT_NATIVE */ q = z; while (*z) /* calc # of /'s */ if (*z++ == '/') updirs++; #ifdef WINNT_NATIVE /* * for format type c, prompt will be following... * c:/path => c:/path * c:/path/to => c:to * //machine/share => //machine/share * //machine/share/folder => //machine:folder */ if (oldz[0] == '/' && oldz[1] == '/' && updirs > 1) Strbuf_append1(&buf, attributes | ':'); #endif /* WINNT_NATIVE */ if ((Scp == 'C' && *q != '/')) updirs++; if (cp[1] == '0') { /* print or ... */ pdirs = 1; cp++; } if (cp[1] >= '1' && cp[1] <= '9') { /* calc # to skip */ skip = cp[1] - '0'; cp++; } else skip = 1; updirs -= skip; while (skip-- > 0) { while ((z > q) && (*z != '/')) z--; /* back up */ if (skip && z > q) z--; } if (*z == '/' && z != q) z++; } /* . || C */ /* print ~[user] */ if ((olduser) && ((Scp == '~') || (Scp == '.' && (pdirs || (!pdirs && updirs <= 0))) )) { Strbuf_append1(&buf, attributes | '~'); for (q = olduser; *q; q++) Strbuf_append1(&buf, attributes | *q); } /* RWM - tell you how many dirs we've ignored */ /* and add '/' at front of this */ if (updirs > 0 && pdirs) { if (adrof(STRellipsis)) { Strbuf_append1(&buf, attributes | '.'); Strbuf_append1(&buf, attributes | '.'); Strbuf_append1(&buf, attributes | '.'); } else { Strbuf_append1(&buf, attributes | '/'); Strbuf_append1(&buf, attributes | '<'); if (updirs > 9) { Strbuf_append1(&buf, attributes | '9'); Strbuf_append1(&buf, attributes | '+'); } else Strbuf_append1(&buf, attributes | ('0' + updirs)); Strbuf_append1(&buf, attributes | '>'); } } while (*z) Strbuf_append1(&buf, attributes | *z++); break; /* lukem: end of new directory prompt code */ case 'n': #ifndef HAVENOUTMP if (what == FMT_WHO) { cz = who_info(info, 'n'); tprintf_append_mbs(&buf, cz, attributes); xfree(cz); } else #endif /* HAVENOUTMP */ { if ((z = varval(STRuser)) != STRNULL) while (*z) Strbuf_append1(&buf, attributes | *z++); } break; case 'N': if ((z = varval(STReuser)) != STRNULL) while (*z) Strbuf_append1(&buf, attributes | *z++); break; case 'l': #ifndef HAVENOUTMP if (what == FMT_WHO) { cz = who_info(info, 'l'); tprintf_append_mbs(&buf, cz, attributes); xfree(cz); } else #endif /* HAVENOUTMP */ { if ((z = varval(STRtty)) != STRNULL) while (*z) Strbuf_append1(&buf, attributes | *z++); } break; case 'd': tprintf_append_mbs(&buf, day_list[t->tm_wday], attributes); break; case 'D': p = Itoa(t->tm_mday, 2, attributes); Strbuf_append(&buf, p); xfree(p); break; case 'w': tprintf_append_mbs(&buf, month_list[t->tm_mon], attributes); break; case 'W': p = Itoa(t->tm_mon + 1, 2, attributes); Strbuf_append(&buf, p); xfree(p); break; case 'y': p = Itoa(t->tm_year % 100, 2, attributes); Strbuf_append(&buf, p); xfree(p); break; case 'Y': p = Itoa(t->tm_year + 1900, 4, attributes); Strbuf_append(&buf, p); xfree(p); break; case 'S': /* start standout */ attributes |= STANDOUT; break; case 'B': /* start bold */ attributes |= BOLD; break; case 'U': /* start underline */ attributes |= UNDER; break; case 's': /* end standout */ attributes &= ~STANDOUT; break; case 'b': /* end bold */ attributes &= ~BOLD; break; case 'u': /* end underline */ attributes &= ~UNDER; break; case 'L': ClearToBottom(); break; case 'j': { int njobs = -1; struct process *pp; for (pp = proclist.p_next; pp; pp = pp->p_next) njobs++; if (njobs == -1) njobs++; p = Itoa(njobs, 1, attributes); Strbuf_append(&buf, p); xfree(p); break; } case '?': if ((z = varval(STRstatus)) != STRNULL) while (*z) Strbuf_append1(&buf, attributes | *z++); break; case '$': expdollar(&buf, &cp, attributes); /* cp should point the last char of current % sequence */ cp--; break; case '%': Strbuf_append1(&buf, attributes | '%'); break; case '{': /* literal characters start */ #if LITERAL == 0 /* * No literal capability, so skip all chars in the literal * string */ while (*cp != '\0' && (cp[-1] != '%' || *cp != '}')) cp++; #endif /* LITERAL == 0 */ attributes |= LITERAL; break; case '}': /* literal characters end */ attributes &= ~LITERAL; break; default: #ifndef HAVENOUTMP if (*cp == 'a' && what == FMT_WHO) { cz = who_info(info, 'a'); tprintf_append_mbs(&buf, cz, attributes); xfree(cz); } else #endif /* HAVENOUTMP */ { Strbuf_append1(&buf, attributes | '%'); Strbuf_append1(&buf, attributes | *cp); } break; } } else if (*cp == '\\' || *cp == '^') Strbuf_append1(&buf, attributes | parseescape(&cp)); else if (*cp == HIST) { /* EGS: handle '!'s in prompts */ if (what == FMT_HISTORY) cz = fmthist('h', info); else cz = xasprintf("%d", eventno + 1); tprintf_append_mbs(&buf, cz, attributes); xfree(cz); } else Strbuf_append1(&buf, attributes | *cp); /* normal character */ } cleanup_ignore(&buf); cleanup_until(&buf); return Strbuf_finish(&buf); } int expdollar(struct Strbuf *buf, const Char **srcp, Char attr) { struct varent *vp; const Char *src = *srcp; Char *var, *val; size_t i; int curly = 0; /* found a variable, expand it */ var = xmalloc((Strlen(src) + 1) * sizeof (*var)); for (i = 0; ; i++) { var[i] = *++src & TRIM; if (i == 0 && var[i] == '{') { curly = 1; var[i] = *++src & TRIM; } if (!alnum(var[i]) && var[i] != '_') { var[i] = '\0'; break; } } if (curly && (*src & TRIM) == '}') src++; vp = adrof(var); if (vp && vp->vec) { for (i = 0; vp->vec[i] != NULL; i++) { for (val = vp->vec[i]; *val; val++) if (*val != '\n' && *val != '\r') Strbuf_append1(buf, *val | attr); if (vp->vec[i+1]) Strbuf_append1(buf, ' ' | attr); } } else { val = (!vp) ? tgetenv(var) : NULL; if (val) { for (; *val; val++) if (*val != '\n' && *val != '\r') Strbuf_append1(buf, *val | attr); } else { *srcp = src; xfree(var); return 0; } } *srcp = src; xfree(var); return 1; }