]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - contrib/tcsh/tc.prompt.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / contrib / tcsh / tc.prompt.c
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
35 RCSID("$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
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                 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
605 int
606 expdollar(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 }