xref: /dragonfly/contrib/tcsh-6/tc.prompt.c (revision 84d884bf08edef6c02f15218458cd5df8010b654)
1 /*
2  * tc.prompt.c: Prompt printing stuff
3  */
4 /*-
5  * Copyright (c) 1980, 1991 The Regents of the University of California.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 #include "sh.h"
33 #include "ed.h"
34 #include "tw.h"
35 
36 /*
37  * kfk 21oct1983 -- add @ (time) and / ($cwd) in prompt.
38  * PWP 4/27/87 -- rearange for tcsh.
39  * mrdch@com.tau.edu.il 6/26/89 - added ~, T and .# - rearanged to switch()
40  *                 instead of if/elseif
41  * Luke Mewburn, <lukem@cs.rmit.edu.au>
42  *        6-Sep-91  changed date format
43  *        16-Feb-94 rewrote directory prompt code, added $ellipsis
44  *        29-Dec-96 added rprompt support
45  */
46 
47 static const char   *month_list[12];
48 static const char   *day_list[7];
49 
50 void
dateinit(void)51 dateinit(void)
52 {
53 #ifdef notyet
54   int i;
55 
56   setlocale(LC_TIME, "");
57 
58   for (i = 0; i < 12; i++)
59       xfree((ptr_t) month_list[i]);
60   month_list[0] = strsave(_time_info->abbrev_month[0]);
61   month_list[1] = strsave(_time_info->abbrev_month[1]);
62   month_list[2] = strsave(_time_info->abbrev_month[2]);
63   month_list[3] = strsave(_time_info->abbrev_month[3]);
64   month_list[4] = strsave(_time_info->abbrev_month[4]);
65   month_list[5] = strsave(_time_info->abbrev_month[5]);
66   month_list[6] = strsave(_time_info->abbrev_month[6]);
67   month_list[7] = strsave(_time_info->abbrev_month[7]);
68   month_list[8] = strsave(_time_info->abbrev_month[8]);
69   month_list[9] = strsave(_time_info->abbrev_month[9]);
70   month_list[10] = strsave(_time_info->abbrev_month[10]);
71   month_list[11] = strsave(_time_info->abbrev_month[11]);
72 
73   for (i = 0; i < 7; i++)
74       xfree((ptr_t) day_list[i]);
75   day_list[0] = strsave(_time_info->abbrev_wkday[0]);
76   day_list[1] = strsave(_time_info->abbrev_wkday[1]);
77   day_list[2] = strsave(_time_info->abbrev_wkday[2]);
78   day_list[3] = strsave(_time_info->abbrev_wkday[3]);
79   day_list[4] = strsave(_time_info->abbrev_wkday[4]);
80   day_list[5] = strsave(_time_info->abbrev_wkday[5]);
81   day_list[6] = strsave(_time_info->abbrev_wkday[6]);
82 #else
83   month_list[0] = "Jan";
84   month_list[1] = "Feb";
85   month_list[2] = "Mar";
86   month_list[3] = "Apr";
87   month_list[4] = "May";
88   month_list[5] = "Jun";
89   month_list[6] = "Jul";
90   month_list[7] = "Aug";
91   month_list[8] = "Sep";
92   month_list[9] = "Oct";
93   month_list[10] = "Nov";
94   month_list[11] = "Dec";
95 
96   day_list[0] = "Sun";
97   day_list[1] = "Mon";
98   day_list[2] = "Tue";
99   day_list[3] = "Wed";
100   day_list[4] = "Thu";
101   day_list[5] = "Fri";
102   day_list[6] = "Sat";
103 #endif
104 }
105 
106 void
printprompt(int promptno,const char * str)107 printprompt(int promptno, const char *str)
108 {
109     static  const Char *ocp = NULL;
110     static  const char *ostr = NULL;
111     time_t  lclock = time(NULL);
112     const Char *cp;
113 
114     switch (promptno) {
115     default:
116     case 0:
117           cp = varval(STRprompt);
118           break;
119     case 1:
120           cp = varval(STRprompt2);
121           break;
122     case 2:
123           cp = varval(STRprompt3);
124           break;
125     case 3:
126           if (ocp != NULL) {
127               cp = ocp;
128               str = ostr;
129           }
130           else
131               cp = varval(STRprompt);
132           break;
133     }
134 
135     if (promptno < 2) {
136           ocp = cp;
137           ostr = str;
138     }
139 
140     xfree(Prompt);
141     Prompt = NULL;
142     Prompt = tprintf(FMT_PROMPT, cp, str, lclock, NULL);
143     if (!editing) {
144           for (cp = Prompt; *cp ; )
145               (void) putwraw(*cp++);
146           SetAttributes(0);
147           flush();
148     }
149 
150     xfree(RPrompt);
151     RPrompt = NULL;
152     if (promptno == 0) {      /* determine rprompt if using main prompt */
153           cp = varval(STRrprompt);
154           RPrompt = tprintf(FMT_PROMPT, cp, NULL, lclock, NULL);
155                                         /* if not editing, put rprompt after prompt */
156           if (!editing && RPrompt[0] != '\0') {
157               for (cp = RPrompt; *cp ; )
158                     (void) putwraw(*cp++);
159               SetAttributes(0);
160               putraw(' ');
161               flush();
162           }
163     }
164 }
165 
166 static void
tprintf_append_mbs(struct Strbuf * buf,const char * mbs,Char attributes)167 tprintf_append_mbs(struct Strbuf *buf, const char *mbs, Char attributes)
168 {
169     while (*mbs != 0) {
170           Char wc;
171 
172           mbs += one_mbtowc(&wc, mbs, MB_LEN_MAX);
173           Strbuf_append1(buf, wc | attributes);
174     }
175 }
176 
177 Char *
tprintf(int what,const Char * fmt,const char * str,time_t tim,ptr_t info)178 tprintf(int what, const Char *fmt, const char *str, time_t tim, ptr_t info)
179 {
180     struct Strbuf buf = Strbuf_INIT;
181     Char   *z, *q;
182     Char    attributes = 0;
183     static int print_prompt_did_ding = 0;
184     char *cz;
185 
186     Char *p;
187     const Char *cp = fmt;
188     Char Scp;
189     struct tm *t = localtime(&tim);
190 
191                               /* prompt stuff */
192     static Char *olduser = NULL;
193     int updirs;
194     size_t pdirs;
195 
196     cleanup_push(&buf, Strbuf_cleanup);
197     for (; *cp; cp++) {
198           if ((*cp == '%') && ! (cp[1] == '\0')) {
199               cp++;
200               switch (*cp) {
201               case 'R':
202                     if (what == FMT_HISTORY) {
203                         cz = fmthist('R', info);
204                         tprintf_append_mbs(&buf, cz, attributes);
205                         xfree(cz);
206                     } else {
207                         if (str != NULL)
208                               tprintf_append_mbs(&buf, str, attributes);
209                     }
210                     break;
211               case '#':
212 #ifdef __CYGWIN__
213                     /* Check for being member of the Administrators group */
214                     {
215                               gid_t grps[NGROUPS_MAX];
216                               int grp, gcnt;
217 
218                               gcnt = getgroups(NGROUPS_MAX, grps);
219 # define DOMAIN_GROUP_RID_ADMINS 544
220                               for (grp = 0; grp < gcnt; ++grp)
221                                         if (grps[grp] == DOMAIN_GROUP_RID_ADMINS)
222                                                   break;
223                               Scp = (grp < gcnt) ? PRCHROOT : PRCH;
224                     }
225 #else
226                     Scp = (uid == 0 || euid == 0) ? PRCHROOT : PRCH;
227 #endif
228                     if (Scp != '\0')
229                         Strbuf_append1(&buf, attributes | Scp);
230                     break;
231               case '!':
232               case 'h':
233                     switch (what) {
234                     case FMT_HISTORY:
235                         cz = fmthist('h', info);
236                         break;
237                     case FMT_SCHED:
238                         cz = xasprintf("%d", *(int *)info);
239                         break;
240                     default:
241                         cz = xasprintf("%d", eventno + 1);
242                         break;
243                     }
244                     tprintf_append_mbs(&buf, cz, attributes);
245                     xfree(cz);
246                     break;
247               case 'T':                 /* 24 hour format    */
248               case '@':
249               case 't':                 /* 12 hour am/pm format */
250               case 'p':                 /* With seconds     */
251               case 'P':
252                     {
253                         char    ampm = 'a';
254                         int     hr = t->tm_hour;
255 
256                         /* addition by Hans J. Albertsson */
257                         /* and another adapted from Justin Bur */
258                         if (adrof(STRampm) || (*cp != 'T' && *cp != 'P')) {
259                               if (hr >= 12) {
260                                   if (hr > 12)
261                                         hr -= 12;
262                                   ampm = 'p';
263                               }
264                               else if (hr == 0)
265                                   hr = 12;
266                         }               /* else do a 24 hour clock */
267 
268                         /* "DING!" stuff by Hans also */
269                         if (t->tm_min || print_prompt_did_ding ||
270                               what != FMT_PROMPT || adrof(STRnoding)) {
271                               if (t->tm_min)
272                                   print_prompt_did_ding = 0;
273                               /*
274                                * Pad hour to 2 characters if padhour is set,
275                                * by ADAM David Alan Martin
276                                */
277                               p = Itoa(hr, adrof(STRpadhour) ? 2 : 0, attributes);
278                               Strbuf_append(&buf, p);
279                               xfree(p);
280                               Strbuf_append1(&buf, attributes | ':');
281                               p = Itoa(t->tm_min, 2, attributes);
282                               Strbuf_append(&buf, p);
283                               xfree(p);
284                               if (*cp == 'p' || *cp == 'P') {
285                                   Strbuf_append1(&buf, attributes | ':');
286                                   p = Itoa(t->tm_sec, 2, attributes);
287                                   Strbuf_append(&buf, p);
288                                   xfree(p);
289                               }
290                               if (adrof(STRampm) || (*cp != 'T' && *cp != 'P')) {
291                                   Strbuf_append1(&buf, attributes | ampm);
292                                   Strbuf_append1(&buf, attributes | 'm');
293                               }
294                         }
295                         else {          /* we need to ding */
296                               size_t i;
297 
298                               for (i = 0; STRDING[i] != 0; i++)
299                                   Strbuf_append1(&buf, attributes | STRDING[i]);
300                               print_prompt_did_ding = 1;
301                         }
302                     }
303                     break;
304 
305               case 'M':
306 #ifndef HAVENOUTMP
307                     if (what == FMT_WHO)
308                         cz = who_info(info, 'M');
309                     else
310 #endif /* HAVENOUTMP */
311                         cz = getenv("HOST");
312                     /*
313                      * Bug pointed out by Laurent Dami <dami@cui.unige.ch>: don't
314                      * derefrence that NULL (if HOST is not set)...
315                      */
316                     if (cz != NULL)
317                         tprintf_append_mbs(&buf, cz, attributes);
318                     if (what == FMT_WHO)
319                         xfree(cz);
320                     break;
321 
322               case 'm': {
323                     char *scz = NULL;
324 #ifndef HAVENOUTMP
325                     if (what == FMT_WHO)
326                         scz = cz = who_info(info, 'm');
327                     else
328 #endif /* HAVENOUTMP */
329                         cz = getenv("HOST");
330 
331                     if (cz != NULL)
332                         while (*cz != 0 && (what == FMT_WHO || *cz != '.')) {
333                               Char wc;
334 
335                               cz += one_mbtowc(&wc, cz, MB_LEN_MAX);
336                               Strbuf_append1(&buf, wc | attributes);
337                         }
338                     if (scz)
339                         xfree(scz);
340                     break;
341               }
342 
343                               /* lukem: new directory prompt code */
344               case '~':
345               case '/':
346               case '.':
347               case 'c':
348               case 'C':
349                     Scp = *cp;
350                     if (Scp == 'c')               /* store format type (c == .) */
351                         Scp = '.';
352                     if ((z = varval(STRcwd)) == STRNULL)
353                         break;                    /* no cwd, so don't do anything */
354 
355                               /* show ~ whenever possible - a la dirs */
356                     if (Scp == '~' || Scp == '.' ) {
357                         static Char *olddir = NULL;
358 
359                         if (tlength == 0 || olddir != z) {
360                               olddir = z;                   /* have we changed dir? */
361                               olduser = getusername(&olddir);
362                         }
363                         if (olduser)
364                               z = olddir;
365                     }
366                     updirs = pdirs = 0;
367 
368                               /* option to determine fixed # of dirs from path */
369                     if (Scp == '.' || Scp == 'C') {
370                         int skip;
371 #ifdef WINNT_NATIVE
372                         Char *oldz = z;
373                         if (z[1] == ':') {
374                               Strbuf_append1(&buf, attributes | *z++);
375                               Strbuf_append1(&buf, attributes | *z++);
376                         }
377                         if (*z == '/' && z[1] == '/') {
378                               Strbuf_append1(&buf, attributes | *z++);
379                               Strbuf_append1(&buf, attributes | *z++);
380                               do {
381                                   Strbuf_append1(&buf, attributes | *z++);
382                               } while (*z != '/');
383                         }
384 #endif /* WINNT_NATIVE */
385                         q = z;
386                         while (*z)                                    /* calc # of /'s */
387                               if (*z++ == '/')
388                                   updirs++;
389 
390 #ifdef WINNT_NATIVE
391                         /*
392                          * for format type c, prompt will be following...
393                          * c:/path                => c:/path
394                          * c:/path/to             => c:to
395                          * //machine/share        => //machine/share
396                          * //machine/share/folder => //machine:folder
397                          */
398                         if (oldz[0] == '/' && oldz[1] == '/' && updirs > 1)
399                               Strbuf_append1(&buf, attributes | ':');
400 #endif /* WINNT_NATIVE */
401                         if ((Scp == 'C' && *q != '/'))
402                               updirs++;
403 
404                         if (cp[1] == '0') {                           /* print <x> or ...  */
405                               pdirs = 1;
406                               cp++;
407                         }
408                         if (cp[1] >= '1' && cp[1] <= '9') { /* calc # to skip  */
409                               skip = cp[1] - '0';
410                               cp++;
411                         }
412                         else
413                               skip = 1;
414 
415                         updirs -= skip;
416                         while (skip-- > 0) {
417                               while ((z > q) && (*z != '/'))
418                                   z--;                      /* back up */
419                               if (skip && z > q)
420                                   z--;
421                         }
422                         if (*z == '/' && z != q)
423                               z++;
424                     } /* . || C */
425 
426                                                                       /* print ~[user] */
427                     if ((olduser) && ((Scp == '~') ||
428                          (Scp == '.' && (pdirs || (!pdirs && updirs <= 0))) )) {
429                         Strbuf_append1(&buf, attributes | '~');
430                         for (q = olduser; *q; q++)
431                               Strbuf_append1(&buf, attributes | *q);
432                     }
433 
434                               /* RWM - tell you how many dirs we've ignored */
435                               /*       and add '/' at front of this         */
436                     if (updirs > 0 && pdirs) {
437                         if (adrof(STRellipsis)) {
438                               Strbuf_append1(&buf, attributes | '.');
439                               Strbuf_append1(&buf, attributes | '.');
440                               Strbuf_append1(&buf, attributes | '.');
441                         } else {
442                               Strbuf_append1(&buf, attributes | '/');
443                               Strbuf_append1(&buf, attributes | '<');
444                               if (updirs > 9) {
445                                   Strbuf_append1(&buf, attributes | '9');
446                                   Strbuf_append1(&buf, attributes | '+');
447                               } else
448                                   Strbuf_append1(&buf, attributes | ('0' + updirs));
449                               Strbuf_append1(&buf, attributes | '>');
450                         }
451                     }
452 
453                     while (*z)
454                         Strbuf_append1(&buf, attributes | *z++);
455                     break;
456                               /* lukem: end of new directory prompt code */
457 
458               case 'n':
459 #ifndef HAVENOUTMP
460                     if (what == FMT_WHO) {
461                         cz = who_info(info, 'n');
462                         tprintf_append_mbs(&buf, cz, attributes);
463                         xfree(cz);
464                     }
465                     else
466 #endif /* HAVENOUTMP */
467                     {
468                         if ((z = varval(STRuser)) != STRNULL)
469                               while (*z)
470                                   Strbuf_append1(&buf, attributes | *z++);
471                     }
472                     break;
473               case 'N':
474                     if ((z = varval(STReuser)) != STRNULL)
475                         while (*z)
476                               Strbuf_append1(&buf, attributes | *z++);
477                     break;
478               case 'l':
479 #ifndef HAVENOUTMP
480                     if (what == FMT_WHO) {
481                         cz = who_info(info, 'l');
482                         tprintf_append_mbs(&buf, cz, attributes);
483                         xfree(cz);
484                     }
485                     else
486 #endif /* HAVENOUTMP */
487                     {
488                         if ((z = varval(STRtty)) != STRNULL)
489                               while (*z)
490                                   Strbuf_append1(&buf, attributes | *z++);
491                     }
492                     break;
493               case 'd':
494                     tprintf_append_mbs(&buf, day_list[t->tm_wday], attributes);
495                     break;
496               case 'D':
497                     p = Itoa(t->tm_mday, 2, attributes);
498                     Strbuf_append(&buf, p);
499                     xfree(p);
500                     break;
501               case 'w':
502                     tprintf_append_mbs(&buf, month_list[t->tm_mon], attributes);
503                     break;
504               case 'W':
505                     p = Itoa(t->tm_mon + 1, 2, attributes);
506                     Strbuf_append(&buf, p);
507                     xfree(p);
508                     break;
509               case 'y':
510                     p = Itoa(t->tm_year % 100, 2, attributes);
511                     Strbuf_append(&buf, p);
512                     xfree(p);
513                     break;
514               case 'Y':
515                     p = Itoa(t->tm_year + 1900, 4, attributes);
516                     Strbuf_append(&buf, p);
517                     xfree(p);
518                     break;
519               case 'S':                 /* start standout */
520                     attributes |= STANDOUT;
521                     break;
522               case 'B':                 /* start bold */
523                     attributes |= BOLD;
524                     break;
525               case 'U':                 /* start underline */
526                     attributes |= UNDER;
527                     break;
528               case 's':                 /* end standout */
529                     attributes &= ~STANDOUT;
530                     break;
531               case 'b':                 /* end bold */
532                     attributes &= ~BOLD;
533                     break;
534               case 'u':                 /* end underline */
535                     attributes &= ~UNDER;
536                     break;
537               case 'L':
538                     ClearToBottom();
539                     break;
540 
541               case 'j':
542                     {
543                         int njobs = -1;
544                         struct process *pp;
545 
546                         for (pp = proclist.p_next; pp; pp = pp->p_next)
547                               njobs++;
548                         if (njobs == -1)
549                               njobs++;
550                         p = Itoa(njobs, 1, attributes);
551                         Strbuf_append(&buf, p);
552                         xfree(p);
553                         break;
554                     }
555               case '?':
556                     if ((z = varval(STRstatus)) != STRNULL)
557                         while (*z)
558                               Strbuf_append1(&buf, attributes | *z++);
559                     break;
560               case '$':
561                     expdollar(&buf, &cp, attributes);
562                     /* cp should point the last char of current % sequence */
563                     cp--;
564                     break;
565               case '%':
566                     Strbuf_append1(&buf, attributes | '%');
567                     break;
568               case '{':                 /* literal characters start */
569 #if LITERAL == 0
570                     /*
571                      * No literal capability, so skip all chars in the literal
572                      * string
573                      */
574                     while (*cp != '\0' && (cp[-1] != '%' || *cp != '}'))
575                         cp++;
576 #endif                                  /* LITERAL == 0 */
577                     attributes |= LITERAL;
578                     break;
579               case '}':                 /* literal characters end */
580                     attributes &= ~LITERAL;
581                     break;
582               default:
583 #ifndef HAVENOUTMP
584                     if (*cp == 'a' && what == FMT_WHO) {
585                         cz = who_info(info, 'a');
586                         tprintf_append_mbs(&buf, cz, attributes);
587                         xfree(cz);
588                     }
589                     else
590 #endif /* HAVENOUTMP */
591                     {
592                         Strbuf_append1(&buf, attributes | '%');
593                         Strbuf_append1(&buf, attributes | *cp);
594                     }
595                     break;
596               }
597           }
598           else if (*cp == '\\' || *cp == '^')
599               Strbuf_append1(&buf, attributes | parseescape(&cp, TRUE));
600           else if (*cp == HIST) {       /* EGS: handle '!'s in prompts */
601               if (what == FMT_HISTORY)
602                     cz = fmthist('h', info);
603               else
604                     cz = xasprintf("%d", eventno + 1);
605               tprintf_append_mbs(&buf, cz, attributes);
606               xfree(cz);
607           }
608           else
609               Strbuf_append1(&buf, attributes | *cp); /* normal character */
610     }
611     cleanup_ignore(&buf);
612     cleanup_until(&buf);
613     return Strbuf_finish(&buf);
614 }
615 
616 int
expdollar(struct Strbuf * buf,const Char ** srcp,Char attr)617 expdollar(struct Strbuf *buf, const Char **srcp, Char attr)
618 {
619     struct varent *vp;
620     const Char *src = *srcp;
621     Char *var, *val;
622     size_t i;
623     int curly = 0;
624 
625     /* found a variable, expand it */
626     var = xmalloc((Strlen(src) + 1) * sizeof (*var));
627     for (i = 0; ; i++) {
628           var[i] = *++src & TRIM;
629           if (i == 0 && var[i] == '{') {
630               curly = 1;
631               var[i] = *++src & TRIM;
632           }
633           if (!alnum(var[i]) && var[i] != '_') {
634 
635               var[i] = '\0';
636               break;
637           }
638     }
639     if (curly && (*src & TRIM) == '}')
640           src++;
641 
642     vp = adrof(var);
643     if (vp && vp->vec) {
644           for (i = 0; vp->vec[i] != NULL; i++) {
645               for (val = vp->vec[i]; *val; val++)
646                     if (*val != '\n' && *val != '\r')
647                         Strbuf_append1(buf, *val | attr);
648               if (vp->vec[i+1])
649                     Strbuf_append1(buf, ' ' | attr);
650           }
651     }
652     else {
653           val = (!vp) ? tgetenv(var) : NULL;
654           if (val) {
655               for (; *val; val++)
656                     if (*val != '\n' && *val != '\r')
657                         Strbuf_append1(buf, *val | attr);
658           } else {
659               *srcp = src;
660               xfree(var);
661               return 0;
662           }
663     }
664 
665     *srcp = src;
666     xfree(var);
667     return 1;
668 }
669