Add regression test infrastructure.
[dragonfly.git] / contrib / tcsh / tc.prompt.c
1 /* $Header: /src/pub/tcsh/tc.prompt.c,v 3.46 2002/07/12 13:16:19 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("$Id: tc.prompt.c,v 3.46 2002/07/12 13:16:19 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 char   *month_list[12];
52 static char   *day_list[7];
53
54 void
55 dateinit()
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(promptno, str)
112     int     promptno;
113     char   *str;
114 {
115     static  Char *ocp = NULL;
116     static  char *ostr = NULL;
117     time_t  lclock = time(NULL);
118     Char   *cp;
119
120     switch (promptno) {
121     default:
122     case 0:
123         cp = varval(STRprompt);
124         break;
125     case 1:
126         cp = varval(STRprompt2);
127         break;
128     case 2:
129         cp = varval(STRprompt3);
130         break;
131     case 3:
132         if (ocp != NULL) {
133             cp = ocp;
134             str = ostr;
135         }
136         else 
137             cp = varval(STRprompt);
138         break;
139     }
140
141     if (promptno < 2) {
142         ocp = cp;
143         ostr = str;
144     }
145
146     PromptBuf[0] = '\0';
147     tprintf(FMT_PROMPT, PromptBuf, cp, 2 * INBUFSIZE - 2, str, lclock, NULL);
148
149     if (!editing) {
150         for (cp = PromptBuf; *cp ; )
151             (void) putraw(*cp++);
152         SetAttributes(0);
153         flush();
154     }
155
156     RPromptBuf[0] = '\0';
157     if (promptno == 0) {        /* determine rprompt if using main prompt */
158         cp = varval(STRrprompt);
159         tprintf(FMT_PROMPT, RPromptBuf, cp, INBUFSIZE - 2, NULL, lclock, NULL);
160
161                                 /* if not editing, put rprompt after prompt */
162         if (!editing && RPromptBuf[0] != '\0') {
163             for (cp = RPromptBuf; *cp ; )
164                 (void) putraw(*cp++);
165             SetAttributes(0);
166             putraw(' ');
167             flush();
168         }
169     }
170 }
171
172 void
173 tprintf(what, buf, fmt, siz, str, tim, info)
174     int what;
175     Char *buf;
176     const Char *fmt;
177     size_t siz;
178     char *str;
179     time_t tim;
180     ptr_t info;
181 {
182     Char   *z, *q;
183     Char    attributes = 0;
184     static int print_prompt_did_ding = 0;
185     Char    buff[BUFSIZE];
186     /* Need to be unsigned to avoid sign extension */
187     const unsigned char   *cz;
188     unsigned char    cbuff[BUFSIZE];
189
190     Char *p  = buf;
191     Char *ep = &p[siz];
192     const Char *cp = fmt;
193     Char Scp;
194     struct tm *t = localtime(&tim);
195
196                         /* prompt stuff */
197     static Char *olddir = NULL, *olduser = NULL;
198     extern int tlength; /* cache cleared */
199     int updirs;
200     size_t pdirs, sz;
201
202     for (; *cp; cp++) {
203         if (p >= ep)
204             break;
205 #ifdef DSPMBYTE
206         if (Ismbyte1(*cp) && ! (cp[1] == '\0'))
207         {
208             *p++ = attributes | *cp++;  /* normal character */
209             *p++ = attributes | *cp;    /* normal character */
210         }
211         else
212 #endif /* DSPMBYTE */
213         if ((*cp == '%') && ! (cp[1] == '\0')) {
214             cp++;
215             switch (*cp) {
216             case 'R':
217                 if (what == FMT_HISTORY)
218                     fmthist('R', info, (char *) (cz = cbuff), sizeof(cbuff));
219                 else
220                     cz = (unsigned char *) str;
221                 if (cz != NULL)
222                     for (; *cz; *p++ = attributes | *cz++)
223                         if (p >= ep) break;
224                 break;
225             case '#':
226                 *p++ = attributes | ((uid == 0) ? PRCHROOT : PRCH);
227                 break;
228             case '!':
229             case 'h':
230                 switch (what) {
231                 case FMT_HISTORY:
232                     fmthist('h', info, (char *) cbuff, sizeof(cbuff));
233                     break;
234                 case FMT_SCHED:
235                     (void) xsnprintf((char *) cbuff, sizeof(cbuff), "%d", 
236                         *(int *)info);
237                     break;
238                 default:
239                     (void) xsnprintf((char *) cbuff, sizeof(cbuff), "%d",
240                         eventno + 1);
241                     break;
242                 }
243                 for (cz = cbuff; *cz; *p++ = attributes | *cz++)
244                     if (p >= ep) break;
245                 break;
246             case 'T':           /* 24 hour format        */
247             case '@':
248             case 't':           /* 12 hour am/pm format */
249             case 'p':           /* With seconds */
250             case 'P':
251                 {
252                     char    ampm = 'a';
253                     int     hr = t->tm_hour;
254
255                     if (p >= ep - 10) break;
256
257                     /* addition by Hans J. Albertsson */
258                     /* and another adapted from Justin Bur */
259                     if (adrof(STRampm) || (*cp != 'T' && *cp != 'P')) {
260                         if (hr >= 12) {
261                             if (hr > 12)
262                                 hr -= 12;
263                             ampm = 'p';
264                         }
265                         else if (hr == 0)
266                             hr = 12;
267                     }           /* else do a 24 hour clock */
268
269                     /* "DING!" stuff by Hans also */
270                     if (t->tm_min || print_prompt_did_ding || 
271                         what != FMT_PROMPT || adrof(STRnoding)) {
272                         if (t->tm_min)
273                             print_prompt_did_ding = 0;
274                         p = Itoa(hr, p, 0, attributes);
275                         *p++ = attributes | ':';
276                         p = Itoa(t->tm_min, p, 2, attributes);
277                         if (*cp == 'p' || *cp == 'P') {
278                             *p++ = attributes | ':';
279                             p = Itoa(t->tm_sec, p, 2, attributes);
280                         }
281                         if (adrof(STRampm) || (*cp != 'T' && *cp != 'P')) {
282                             *p++ = attributes | ampm;
283                             *p++ = attributes | 'm';
284                         }
285                     }
286                     else {      /* we need to ding */
287                         int     i = 0;
288
289                         (void) Strcpy(buff, STRDING);
290                         while (buff[i]) {
291                             *p++ = attributes | buff[i++];
292                         }
293                         print_prompt_did_ding = 1;
294                     }
295                 }
296                 break;
297
298             case 'M':
299 #ifndef HAVENOUTMP
300                 if (what == FMT_WHO)
301                     cz = (unsigned char *) who_info(info, 'M',
302                         (char *) cbuff, sizeof(cbuff));
303                 else 
304 #endif /* HAVENOUTMP */
305                     cz = (unsigned char *) getenv("HOST");
306                 /*
307                  * Bug pointed out by Laurent Dami <dami@cui.unige.ch>: don't
308                  * derefrence that NULL (if HOST is not set)...
309                  */
310                 if (cz != NULL)
311                     for (; *cz ; *p++ = attributes | *cz++)
312                         if (p >= ep) break;
313                 break;
314
315             case 'm':
316 #ifndef HAVENOUTMP
317                 if (what == FMT_WHO)
318                     cz = (unsigned char *) who_info(info, 'm', (char *) cbuff,
319                         sizeof(cbuff));
320                 else 
321 #endif /* HAVENOUTMP */
322                     cz = (unsigned char *) getenv("HOST");
323
324                 if (cz != NULL)
325                     for ( ; *cz && (what == FMT_WHO || *cz != '.')
326                           ; *p++ = attributes | *cz++ )
327                         if (p >= ep) break;
328                 break;
329
330                         /* lukem: new directory prompt code */
331             case '~':
332             case '/':
333             case '.':
334             case 'c':
335             case 'C':
336                 Scp = *cp;
337                 if (Scp == 'c')         /* store format type (c == .) */
338                     Scp = '.';
339                 if ((z = varval(STRcwd)) == STRNULL)
340                     break;              /* no cwd, so don't do anything */
341
342                         /* show ~ whenever possible - a la dirs */
343                 if (Scp == '~' || Scp == '.' ) {
344                     if (tlength == 0 || olddir != z) {
345                         olddir = z;             /* have we changed dir? */
346                         olduser = getusername(&olddir);
347                     }
348                     if (olduser)
349                         z = olddir;
350                 }
351                 updirs = pdirs = 0;
352
353                         /* option to determine fixed # of dirs from path */
354                 if (Scp == '.' || Scp == 'C') {
355                     int skip;
356 #ifdef WINNT_NATIVE
357                     if (z[1] == ':') {
358                         *p++ = attributes | *z++;
359                         *p++ = attributes | *z++;
360                     }
361                         if (*z == '/' && z[1] == '/') {
362                                 *p++ = attributes | *z++;
363                                 *p++ = attributes | *z++;
364                                 do {
365                                         *p++ = attributes | *z++;
366                                 }while(*z != '/');
367                         }
368 #endif /* WINNT_NATIVE */
369                     q = z;
370                     while (*z)                          /* calc # of /'s */
371                         if (*z++ == '/')
372                             updirs++;
373                     if ((Scp == 'C' && *q != '/'))
374                         updirs++;
375
376                     if (cp[1] == '0') {                 /* print <x> or ...  */
377                         pdirs = 1;
378                         cp++;
379                     }
380                     if (cp[1] >= '1' && cp[1] <= '9') { /* calc # to skip  */
381                         skip = cp[1] - '0';
382                         cp++;
383                     }
384                     else
385                         skip = 1;
386
387                     updirs -= skip;
388                     while (skip-- > 0) {
389                         while ((z > q) && (*z != '/'))
390                             z--;                        /* back up */
391                         if (skip && z > q)
392                             z--;
393                     }
394                     if (*z == '/' && z != q)
395                         z++;
396                 } /* . || C */
397
398                                                         /* print ~[user] */
399                 if ((olduser) && ((Scp == '~') ||
400                      (Scp == '.' && (pdirs || (!pdirs && updirs <= 0))) )) {
401                     *p++ = attributes | '~';
402                     if (p >= ep) break;
403                     for (q = olduser; *q; *p++ = attributes | *q++)
404                         if (p >= ep) break;
405                 }
406
407                         /* RWM - tell you how many dirs we've ignored */
408                         /*       and add '/' at front of this         */
409                 if (updirs > 0 && pdirs) {
410                     if (p >= ep - 5) break;
411                     if (adrof(STRellipsis)) {
412                         *p++ = attributes | '.';
413                         *p++ = attributes | '.';
414                         *p++ = attributes | '.';
415                     } else {
416                         *p++ = attributes | '/';
417                         *p++ = attributes | '<';
418                         if (updirs > 9) {
419                             *p++ = attributes | '9';
420                             *p++ = attributes | '+';
421                         } else
422                             *p++ = attributes | ('0' + updirs);
423                         *p++ = attributes | '>';
424                     }
425                 }
426                 
427                 for (; *z ; *p++ = attributes | *z++)
428                     if (p >= ep) break;
429                 break;
430                         /* lukem: end of new directory prompt code */
431
432             case 'n':
433 #ifndef HAVENOUTMP
434                 if (what == FMT_WHO) {
435                     cz = (unsigned char *) who_info(info, 'n',
436                         (char *) cbuff, sizeof(cbuff));
437                     for (; cz && *cz ; *p++ = attributes | *cz++)
438                         if (p >= ep) break;
439                 }
440                 else  
441 #endif /* HAVENOUTMP */
442                 {
443                     if ((z = varval(STRuser)) != STRNULL)
444                         for (; *z; *p++ = attributes | *z++)
445                             if (p >= ep) break;
446                 }
447                 break;
448             case 'l':
449 #ifndef HAVENOUTMP
450                 if (what == FMT_WHO) {
451                     cz = (unsigned char *) who_info(info, 'l',
452                         (char *) cbuff, sizeof(cbuff));
453                     for (; cz && *cz ; *p++ = attributes | *cz++)
454                         if (p >= ep) break;
455                 }
456                 else  
457 #endif /* HAVENOUTMP */
458                 {
459                     if ((z = varval(STRtty)) != STRNULL)
460                         for (; *z; *p++ = attributes | *z++)
461                             if (p >= ep) break;
462                 }
463                 break;
464             case 'd':
465                 for (cz = (unsigned char *) day_list[t->tm_wday]; *cz;
466                     *p++ = attributes | *cz++)
467                     if (p >= ep) break;
468                 break;
469             case 'D':
470                 if (p >= ep - 3) break;
471                 p = Itoa(t->tm_mday, p, 2, attributes);
472                 break;
473             case 'w':
474                 if (p >= ep - 5) break;
475                 for (cz = (unsigned char *) month_list[t->tm_mon]; *cz;
476                     *p++ = attributes | *cz++)
477                     if (p >= ep) break;
478                 break;
479             case 'W':
480                 if (p >= ep - 3) break;
481                 p = Itoa(t->tm_mon + 1, p, 2, attributes);
482                 break;
483             case 'y':
484                 if (p >= ep - 3) break;
485                 p = Itoa(t->tm_year % 100, p, 2, attributes);
486                 break;
487             case 'Y':
488                 if (p >= ep - 5) break;
489                 p = Itoa(t->tm_year + 1900, p, 4, attributes);
490                 break;
491             case 'S':           /* start standout */
492                 attributes |= STANDOUT;
493                 break;
494             case 'B':           /* start bold */
495                 attributes |= BOLD;
496                 break;
497             case 'U':           /* start underline */
498                 attributes |= UNDER;
499                 break;
500             case 's':           /* end standout */
501                 attributes &= ~STANDOUT;
502                 break;
503             case 'b':           /* end bold */
504                 attributes &= ~BOLD;
505                 break;
506             case 'u':           /* end underline */
507                 attributes &= ~UNDER;
508                 break;
509             case 'L':
510                 ClearToBottom();
511                 break;
512
513             case 'j':
514                 {
515                     Char buf[128], *ebuf, *q;
516                     int njobs = -1;
517                     struct process *pp;
518                     for (pp = proclist.p_next; pp; pp = pp->p_next)
519                         njobs++;
520                     /* make sure we have space */
521                     ebuf = Itoa(njobs, buf, 1, attributes);
522                     for (q = buf; q < ebuf; *p++ = *q++)
523                         if (p >= ep) break;
524                     break;
525                 }
526             case '?':
527                 if ((z = varval(STRstatus)) != STRNULL)
528                     for (; *z; *p++ = attributes | *z++)
529                         if (p >= ep) break;
530                 break;
531             case '$':
532                 sz = ep - p;
533                 (void) expdollar(&p, &cp, &sz, attributes);
534                 /* cp should point the last char of currnet % sequence */
535                 cp--;
536                 break;
537             case '%':
538                 *p++ = attributes | '%';
539                 break;
540             case '{':           /* literal characters start */
541 #if LITERAL == 0
542                 /*
543                  * No literal capability, so skip all chars in the literal
544                  * string
545                  */
546                 while (*cp != '\0' && (*cp != '%' || cp[1] != '}'))
547                     cp++;
548 #endif                          /* LITERAL == 0 */
549                 attributes |= LITERAL;
550                 break;
551             case '}':           /* literal characters end */
552                 attributes &= ~LITERAL;
553                 break;
554             default:
555 #ifndef HAVENOUTMP
556                 if (*cp == 'a' && what == FMT_WHO) {
557                     cz = who_info(info, 'a', (char *) cbuff, sizeof(cbuff));
558                     for (; cz && *cz; *p++ = attributes | *cz++)
559                         if (p >= ep) break;
560                 }
561                 else 
562 #endif /* HAVENOUTMP */
563                 {
564                     if (p >= ep - 3) break;
565                     *p++ = attributes | '%';
566                     *p++ = attributes | *cp;
567                 }
568                 break;
569             }
570         }
571         else if (*cp == '\\' || *cp == '^') 
572             *p++ = attributes | parseescape(&cp);
573         else if (*cp == HIST) { /* EGS: handle '!'s in prompts */
574             if (what == FMT_HISTORY) 
575                 fmthist('h', info, (char *) cbuff, sizeof(cbuff));
576             else
577                 (void) xsnprintf((char *) cbuff, sizeof(cbuff), "%d", eventno + 1);
578             for (cz = cbuff; *cz; *p++ = attributes | *cz++)
579                 if (p >= ep) break;
580         }
581         else 
582             *p++ = attributes | *cp;    /* normal character */
583     }
584     *p = '\0';
585 }
586
587 Char *
588 expdollar(dstp, srcp, spp, attr)
589     Char **dstp;
590     const Char **srcp;
591     size_t *spp;
592     int     attr;
593 {
594     struct varent *vp;
595     Char var[MAXVARLEN];
596     const Char *src = *srcp;
597     Char *val;
598     Char *dst = *dstp;
599     int i, curly = 0;
600
601     /* found a variable, expand it */
602     for (i = 0; i < MAXVARLEN; i++) {
603         var[i] = *++src & TRIM;
604         if (i == 0 && var[i] == '{') {
605             curly = 1;
606             var[i] = *++src & TRIM;
607         }
608         if (!alnum(var[i])) {
609             
610             var[i] = '\0';
611             break;
612         }
613     }
614     if (curly && (*src & TRIM) == '}')
615         src++;
616
617     vp = adrof(var);
618     val = (!vp) ? tgetenv(var) : NULL;
619     if (vp && vp->vec) {
620         for (i = 0; vp->vec[i] != NULL; i++) {
621             for (val = vp->vec[i]; *spp > 0 && *val; (*spp)--)
622                 *dst++ = *val++ | attr;
623             if (vp->vec[i+1] && *spp > 0) {
624                 *dst++ = ' ' | attr;
625                 (*spp)--;
626             }
627         }
628     }
629     else if (val) {
630         for (; *spp > 0 && *val; (*spp)--)
631             *dst++ = *val++ | attr;
632     }
633     else {
634         **dstp = '\0';
635         *srcp = src;
636         return NULL;
637     }
638     *dst = '\0';
639
640     val = *dstp;
641     *srcp = src;
642     *dstp = dst;
643
644     return val;
645 }