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