Merge branch 'vendor/LESS'
[dragonfly.git] / contrib / tcsh-6 / tc.prompt.c
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
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
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
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 *
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));
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
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 }