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