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