Added all of the MH sources, including RCS files, in
[mmh] / docs / historical / mh-6.8.5 / local / lbl / sbr / formatsbr.c
1 /* formatsbr.c - format string interpretation */
2
3 #include "../h/mh.h"
4 #include "../h/addrsbr.h"
5 #include "../h/formatsbr.h"
6 #include "../zotnet/tws.h"
7 #include "../h/fmtcompile.h"
8 #include <ctype.h>
9 #include <stdio.h>
10 #include <sys/types.h>
11 #include <sys/stat.h>
12
13 #define NFMTS   MAXARGS
14 #define QUOTE '\\'
15
16 static char *formats = 0;
17 extern char *formataddr();      /* hook for custom address formatting */
18 struct msgs *fmt_current_folder;/* current folder (set by main program) */
19
20 int fmt_norm = AD_NAME;
21 struct mailname fmt_mnull;
22
23
24 static
25 normalize(cp)
26         register char *cp;
27 {
28         register char *dp;
29
30         for (dp = cp; *cp; cp++)
31                 if (*cp != QUOTE)
32                         *dp++ = *cp;
33                 else
34                         switch (*++cp) {
35
36                         case 'b':
37                                 *dp++ = '\b';
38                                 break;
39                         case 'f':
40                                 *dp++ = '\f';
41                                 break;
42                         case 'n':
43                                 *dp++ = '\n';
44                                 break;
45                         case 'r':
46                                 *dp++ = '\r';
47                                 break;
48                         case 't':
49                                 *dp++ = '\t';
50                                 break;
51
52                         case '\n':
53                                 break;
54
55                         case NULL:
56                                 cp--;
57                                 /* fall through */
58                         default:
59                                 *dp++ = *cp;
60                                 break;
61                         }
62
63         *dp = NULL;
64 }
65
66 char *
67 new_fs(form, format, def)
68         register char *form, *format, *def;
69 {
70         struct stat st;
71         register FILE *fp;
72
73         if (formats)
74                 free(formats);
75
76         if (form) {
77                 if ((fp = fopen(libpath(form), "r")) == NULL)
78                         adios(form, "unable to open format file");
79
80                 if (fstat(fileno(fp), &st) == NOTOK)
81                         adios(form, "unable to stat format file");
82
83                 if ((formats = malloc((unsigned) st.st_size + 1)) == NULLCP)
84                         adios(form, "unable to allocate space for format");
85
86                 if (read(fileno(fp), formats, st.st_size) != st.st_size)
87                         adios(form, "error reading format file");
88
89                 formats[st.st_size] = '\0';
90                 (void) fclose(fp);
91         } else {
92                 formats = getcpy(format ? format : def);
93         }
94
95         normalize(formats);
96
97         return formats;
98 }
99
100 /*
101  * test if string "sub" appears anywhere in string "str"
102  * (case insensitive).
103  */
104 static int
105 match(str, sub)
106         register char *str, *sub;
107 {
108         register int c1;
109         register int c2;
110         register char *s1;
111         register char *s2;
112
113         while (c1 = *sub) {
114                 while ((c2 = *str++) && (c1 | 040) != (c2 | 040))
115                         ;
116                 if (!c2)
117                         return 0;
118                 s1 = sub + 1;
119                 s2 = str;
120                 while ((c1 = *s1++) && (c1 | 040) == (*s2++ | 040))
121                         ;
122                 if (!c1)
123                         return 1;
124         }
125         return 1;
126 }
127
128 /* macros to format data */
129
130 #define PUTDF(cp, num, wid, fill) {\
131                 if (cp + wid < ep) {\
132                         if ((i = (num)) < 0)\
133                                 i = -(num);\
134                         if ((c = (wid)) < 0)\
135                                 c = -c;\
136                         sp = cp + c;\
137                         do {\
138                                 *--sp = (i % 10) + '0';\
139                                 i /= 10;\
140                         } while (i > 0 && sp > cp);\
141                         if (i > 0)\
142                                 *sp = '?';\
143                         else if ((num) < 0 && sp > cp)\
144                                 *--sp = '-';\
145                         while (sp > cp)\
146                                 *--sp = fill;\
147                         cp += c;\
148                 }}
149 #define PUTD(cp, num) {\
150                 if (cp < ep) {\
151                         if ((i = (num)) == 0)\
152                                 *cp++ = '0';\
153                         else {\
154                                 if ((i = (num)) < 0) \
155                                         *cp++ = '-', i = -(num);\
156                                 c = 10;\
157                                 while (c <= i) \
158                                         c *= 10;\
159                                 while (cp < ep && c > 1) {\
160                                         c /= 10;\
161                                         *cp++ = (i / c) + '0';\
162                                         i %= c;\
163                                 }\
164                         }\
165                 }}
166 #define PUTSF(cp, str, wid, fill) {\
167                 char *cp2 = cp;\
168                 rjust = 0;\
169                 if ((i = (wid)) < 0) {\
170                         i = -i;\
171                         rjust++;\
172                 }\
173                 if (sp = (str)) {\
174                         if (rjust) {\
175                                 c = strlen(sp);\
176                                 if (c > i)\
177                                         sp += c - i;\
178                                 else {\
179                                         while (--i >= c && cp < ep)\
180                                                 *cp++ = fill;\
181                                         i++;\
182                                 }\
183                         } else {\
184                                 while ((c = *sp) && c <= 32)\
185                                         sp++;\
186                         }\
187                         while ((c = *sp++) && --i >= 0 && cp < ep)\
188                                 if (c > 32) \
189                                         *cp++ = c;\
190                                 else if (c == '\010') {\
191                                         if (cp > cp2)\
192                                                 --cp;\
193                                 } else {\
194                                         while ((c = *sp) && c <= 32)\
195                                                 sp++;\
196                                         *cp++ = ' ';\
197                                 }\
198                 }\
199                 if (!rjust)\
200                         while (--i >= 0 && cp < ep)\
201                                 *cp++ = fill;\
202                 }
203
204 #define PUTS(cp, str) {\
205                 char *cp2 = cp;\
206                 if (sp = (str)) {\
207                         while ((c = *sp) && c <= 32)\
208                                 sp++;\
209                         while ((c = *sp++) && cp < ep)\
210                                 if (c > 32) \
211                                         *cp++ = c;\
212                                 else if (c == '\010') {\
213                                         if (cp > cp2)\
214                                                 --cp;\
215                                 } else {\
216                                         while ((c = *sp) && c <= 32)\
217                                                 sp++;\
218                                         *cp++ = ' ';\
219                                 }\
220                 }}
221
222 static char *lmonth[] = {
223     "January", "February", "March", "April", "May", "June",
224     "July", "August", "September", "October", "November", "December"
225 };
226
227
228 fmtscan(format, scanl, width, dat)
229         struct format *format;
230         char *scanl;
231         int width;
232         int dat[];
233 {
234         register char *cp = scanl;
235         register char *ep = scanl + width - 1;
236         register struct format *fmt = format;
237         register char *str = NULLCP;
238         register int value = 0;
239         register char *sp;
240         register int i;
241         register int c;
242         register struct comp *comp;
243         register struct tws *tws;
244         register struct mailname *mn;
245         register int j;
246         int rjust;
247         long l;
248         char *savestr;
249         char buffer[BUFSIZ];
250
251         for (;;) {
252                 switch (fmt->f_type) {
253
254                 case FT_COMP:
255                         PUTS(cp, fmt->f_comp->c_text);
256                         break;
257                 case FT_COMPF:
258                         PUTSF(cp, fmt->f_comp->c_text, fmt->f_width,
259                             fmt->f_fill);
260                         break;
261
262                 case FT_LIT_FORCE:
263                         sp = fmt->f_text;
264                         i = strlen(sp);
265                         ep += i;        /* forced lits are `invisible' */
266
267                         while (c = *sp++)
268                                 *cp++ = c;
269                         break;
270                 case FT_LIT:
271                         sp = fmt->f_text;
272                         while ((c = *sp++) && cp < ep)
273                                 *cp++ = c;
274                         break;
275                 case FT_LITF:
276                         sp = fmt->f_text;
277
278                         /* By default we left justify */
279                         rjust = 0;
280                         if ((i = fmt->f_width) < 0) {
281                                 i = -i;
282                                 rjust++;
283                         }
284
285                         if (rjust) {
286                                 j = strlen(sp);
287                                 if (j > i)
288                                         sp += j - i;
289                                 else while (j < i && cp < ep) {
290                                         *cp++ = fmt->f_fill;
291                                         ++j;
292                                 }
293                         }
294
295                         while ((c = *sp++) && --i >= 0 && cp < ep)
296                                 *cp++ = c;
297
298                         while (--i >= 0 && cp < ep)
299                                 *cp++ = fmt->f_fill;
300                         break;
301
302                 case FT_STR:
303                         PUTS(cp, str);
304                         break;
305                 case FT_STRF:
306                         PUTSF(cp, str, fmt->f_width, fmt->f_fill);
307                         break;
308                 case FT_STRFW:
309                         adios(NULLCP, "internal error (FT_STRFW)");
310
311                 case FT_NUM:
312                         PUTD(cp, value);
313                         break;
314                 case FT_NUMF:
315                         PUTDF(cp, value, fmt->f_width, fmt->f_fill);
316                         break;
317
318                 case FT_CHAR:
319                         *cp++ = fmt->f_char;
320                         break;
321                 case FT_DONE:
322                         goto finished;
323
324                 case FT_IF_S:
325                         if (str == NULLCP || *str == NULL) {
326                                 fmt += fmt->f_skip;
327                                 continue;
328                         }
329                         break;
330
331                 case FT_IF_S_NULL:
332                         if (str != NULLCP && *str != NULL) {
333                                 fmt += fmt->f_skip;
334                                 continue;
335                         }
336                         break;
337
338                 case FT_IF_V_EQ:
339                         if (value != fmt->f_value) {
340                                 fmt += fmt->f_skip;
341                                 continue;
342                         }
343                         break;
344
345                 case FT_IF_V_NE:
346                         if (value == fmt->f_value) {
347                                 fmt += fmt->f_skip;
348                                 continue;
349                         }
350                         break;
351
352                 case FT_IF_V_GT:
353                         if (value <= fmt->f_value) {
354                                 fmt += fmt->f_skip;
355                                 continue;
356                         }
357                         break;
358
359                 case FT_IF_MATCH:
360                         if (!str || !match(str, fmt->f_text)) {
361                                 fmt += fmt->f_skip;
362                                 continue;
363                         }
364                         break;
365
366                 case FT_V_MATCH:
367                         if (str)
368                                 value = match(str, fmt->f_text);
369                         else
370                                 value = 0;
371                         break;
372
373                 case FT_IF_AMATCH:
374                         if (!str || !uprf(str, fmt->f_text)) {
375                                 fmt += fmt->f_skip;
376                                 continue;
377                         }
378                         break;
379
380                 case FT_V_AMATCH:
381                         if (str)
382                                 value = uprf(str, fmt->f_text);
383                         else
384                                 value = 0;
385                         break;
386
387                 case FT_S_NONNULL:
388                         value = (str != NULLCP && *str != NULL);
389                         break;
390
391                 case FT_S_NULL:
392                         value = (str == NULLCP || *str == NULL);
393                         break;
394
395                 case FT_V_EQ:
396                         value = (fmt->f_value == value);
397                         break;
398
399                 case FT_V_NE:
400                         value = (fmt->f_value != value);
401                         break;
402
403                 case FT_V_GT:
404                         value = (fmt->f_value > value);
405                         break;
406
407                 case FT_GOTO:
408                         fmt += fmt->f_skip;
409                         continue;
410
411                 case FT_NOP:
412                         break;
413
414                 case FT_LS_COMP:
415                         str = fmt->f_comp->c_text;
416                         break;
417                 case FT_LS_LIT:
418                         str = fmt->f_text;
419                         break;
420                 case FT_LS_TRIM:
421                         if (str) {
422                                 register char *xp;
423
424                                 /* Be careful since str can point into buffer */
425                                 bcopy(str, buffer, strlen(str) + 1);
426                                 str = buffer;
427
428                                 /* Eat leading whitespace */
429                                 while (isspace(*str))
430                                         str++;
431
432                                 /* Trim trailing whitespace */
433                                 xp = str + strlen(str) - 1;
434                                 while (xp > str && isspace(*xp))
435                                         *xp-- = '\0';
436
437                                 /* By default we left justify */
438                                 rjust = 0;
439                                 if ((i = fmt->f_width) < 0) {
440                                         i = -i;
441                                         rjust++;
442                                 }
443
444                                 /* If necessary, limit width and/or justify */
445                                 if (i > 0 && (j = strlen(str)) > i) {
446                                         if (!rjust)
447                                                 str[i] = '\0';
448                                         else
449                                                 str += j - i;
450                                 }
451                         }
452                         break;
453
454                 case FT_LV_COMPFLAG:
455                         value = fmt->f_comp->c_flags;
456                         break;
457                 case FT_LV_COMP:
458                         value = (comp = fmt->f_comp)->c_text ?
459                             atoi(comp->c_text) : 0;
460                         break;
461                 case FT_LV_LIT:
462                         value = fmt->f_value;
463                         break;
464                 case FT_LV_DAT:
465                         value = dat[fmt->f_value];
466                         break;
467                 case FT_LV_STRLEN:
468                         value = strlen(str);
469                         break;
470                 case FT_LV_CHAR_LEFT:
471                         value = width - (cp - scanl);
472                         break;
473                 case FT_LV_PLUS_L:
474                         value += fmt->f_value;
475                         break;
476                 case FT_LV_MINUS_L:
477                         value = fmt->f_value - value;
478                         break;
479                 case FT_LV_DIVIDE_L:
480                         if (fmt->f_value)
481                                 value = value / fmt->f_value;
482                         else
483                                 value = 0;
484                         break;
485                 case FT_SAVESTR:
486                         savestr = str;
487                         break;
488
489                 case FT_LV_SEC:
490                         value = fmt->f_comp->c_tws->tw_sec;
491                         break;
492                 case FT_LV_MIN:
493                         value = fmt->f_comp->c_tws->tw_min;
494                         break;
495                 case FT_LV_HOUR:
496                         value = fmt->f_comp->c_tws->tw_hour;
497                         break;
498                 case FT_LV_MDAY:
499                         value = fmt->f_comp->c_tws->tw_mday;
500                         break;
501                 case FT_LV_MON:
502                         value = fmt->f_comp->c_tws->tw_mon + 1;
503                         break;
504                 case FT_LS_MONTH:
505                         str = tw_moty[fmt->f_comp->c_tws->tw_mon];
506                         break;
507                 case FT_LS_LMONTH:
508                         str = lmonth[fmt->f_comp->c_tws->tw_mon];
509                         break;
510                 case FT_LS_ZONE:
511                         str = dtwszone(fmt->f_comp->c_tws);
512                         break;
513                 case FT_LV_YEAR:
514                         value = fmt->f_comp->c_tws->tw_year;
515                         break;
516                 case FT_LV_WDAY:
517                         if (!(((tws = fmt->f_comp->c_tws)->tw_flags) &
518                             (TW_SEXP | TW_SIMP)))
519                                 set_dotw(tws);
520                         value = tws->tw_wday;
521                         break;
522                 case FT_LS_DAY:
523                         if (!(((tws = fmt->f_comp->c_tws)->tw_flags) &
524                             (TW_SEXP | TW_SIMP)))
525                                 set_dotw(tws);
526                         str = tw_dotw[tws->tw_wday];
527                         break;
528                 case FT_LS_WEEKDAY:
529                         if (!(((tws = fmt->f_comp->c_tws)->tw_flags) &
530                             (TW_SEXP | TW_SIMP)))
531                                 set_dotw(tws);
532                         str = tw_ldotw[tws->tw_wday];
533                         break;
534                 case FT_LV_YDAY:
535                         value = fmt->f_comp->c_tws->tw_yday;
536                         break;
537                 case FT_LV_ZONE:
538                         value = fmt->f_comp->c_tws->tw_zone;
539                         break;
540                 case FT_LV_CLOCK:
541                         if ((value = fmt->f_comp->c_tws->tw_clock) == 0)
542                                 value = twclock(fmt->f_comp->c_tws);
543                         break;
544                 case FT_LV_RCLOCK:
545                         if ((value = fmt->f_comp->c_tws->tw_clock) == 0)
546                                 value = twclock(fmt->f_comp->c_tws);
547                         value = time((long *) 0) - value;
548                         break;
549                 case FT_LV_DAYF:
550                         if (!(((tws = fmt->f_comp->c_tws)->tw_flags) &
551                             (TW_SEXP | TW_SIMP)))
552                                 set_dotw(tws);
553                         switch (fmt->f_comp->c_tws->tw_flags & TW_SDAY) {
554                         case TW_SEXP:
555                                 value = 1;
556                                 break;
557                         case TW_SIMP:
558                                 value = 0;
559                                 break;
560                         default:
561                                 value = -1;
562                                 break;
563                         }
564                 case FT_LV_ZONEF:
565                         if ((fmt->f_comp->c_tws->tw_flags & TW_SZONE) ==
566                             TW_SZEXP)
567                                 value = 1;
568                         else
569                                 value = -1;
570                         break;
571                 case FT_LV_DST:
572                         value = fmt->f_comp->c_tws->tw_flags & TW_DST;
573                         break;
574                 case FT_LS_822DATE:
575                         str = dasctime(fmt->f_comp->c_tws, TW_ZONE);
576                         break;
577                 case FT_LS_PRETTY:
578                         str = dasctime(fmt->f_comp->c_tws, TW_NULL);
579                         break;
580
581                 case FT_LS_PERS:
582                         str = fmt->f_comp->c_mn->m_pers;
583                         break;
584                 case FT_LS_MBOX:
585                         str = fmt->f_comp->c_mn->m_mbox;
586                         break;
587                 case FT_LS_HOST:
588                         str = fmt->f_comp->c_mn->m_host;
589                         break;
590                 case FT_LS_PATH:
591                         str = fmt->f_comp->c_mn->m_path;
592                         break;
593                 case FT_LS_GNAME:
594                         str = fmt->f_comp->c_mn->m_gname;
595                         break;
596                 case FT_LS_NOTE:
597                         str = fmt->f_comp->c_mn->m_note;
598                         break;
599                 case FT_LS_822ADDR:
600                         str = adrformat(fmt->f_comp->c_mn);
601                         break;
602                 case FT_LV_HOSTTYPE:
603                         value = fmt->f_comp->c_mn->m_type;
604                         break;
605                 case FT_LV_INGRPF:
606                         value = fmt->f_comp->c_mn->m_ingrp;
607                         break;
608                 case FT_LV_NOHOSTF:
609                         value = fmt->f_comp->c_mn->m_nohost;
610                         break;
611                 case FT_LS_FRIENDLY:
612 #ifdef BERK
613                         str = fmt->f_comp->c_mn->m_mbox;
614 #else
615                         mn = fmt->f_comp->c_mn;
616                         if ((str = mn->m_pers) == NULL)
617                                 switch (mn->m_type) {
618
619                                 case LOCALHOST:
620                                         str = mn->m_mbox;
621                                         break;
622                                 case UUCPHOST:
623                                         (void) sprintf(buffer, "%s!%s",
624                                             mn->m_host, mn->m_mbox);
625                                         str = buffer;
626                                         break;
627                                 default:
628                                         if (mn->m_mbox) {
629                                                 (void) sprintf(buffer, "%s@%s",
630                                                     mn->m_mbox, mn->m_host);
631                                                 str = buffer;
632                                         } else
633                                                 str = mn->m_text;
634                                         break;
635                                 }
636 #endif BERK
637                         break;
638
639                 case FT_LOCALDATE:
640                         comp = fmt->f_comp;
641                         if ((l = comp->c_tws->tw_clock) == 0)
642                                 l = twclock(comp->c_tws);
643                         tws = dlocaltime(&l);
644                         *comp->c_tws = *tws;
645                         break;
646
647                 case FT_GMTDATE:
648                         comp = fmt->f_comp;
649                         if ((l = comp->c_tws->tw_clock) == 0)
650                                 l = twclock(comp->c_tws);
651                         tws = dgmtime(&l);
652                         *comp->c_tws = *tws;
653                         break;
654
655                 case FT_PARSEDATE:
656                         comp = fmt->f_comp;
657                         if ((sp = comp->c_text) && (tws = dparsetime(sp))) {
658                                 *comp->c_tws = *tws;
659                                 comp->c_flags = 0;
660                         } else if (comp->c_flags >= 0) {
661                                 bzero((char *)comp->c_tws,
662                                     sizeof(*comp->c_tws));
663                                 comp->c_flags = 1;
664                         }
665                         break;
666
667                 case FT_FORMATADDR:
668                         /* custom address list formatting hook */
669                         str = formataddr(savestr, str);
670                         break;
671
672                 case FT_PUTADDR:
673                         /*
674                          * Output the str register as an address component,
675                          * splitting it into multiple lines if necessary.
676                          * The value reg. contains the max line length.  The
677                          * lit. field may contain a string to prepend to the
678                          * result (e.g., "To: ")
679                          */
680                         {
681                                 register char *lp = str;
682                                 register int indent;
683                                 register int wid = value;
684                                 register int len = strlen(str);
685                                 register char *lastb;
686
687                                 sp = fmt->f_text;
688                                 indent = strlen(sp);
689                                 wid -= indent;
690                                 while ((c = *sp++) && cp < ep)
691                                         *cp++ = c;
692                                 while (len > wid) {
693                                         /*
694                                          * Try to break at a comma; failing
695                                          * that, break at a space, failing
696                                          * that, just split the line.
697                                          */
698                                         lastb = 0;
699                                         sp = lp + wid;
700                                         while (sp > lp && (c = *--sp) != ',') {
701                                                 if (!lastb && isspace(c))
702                                                         lastb = sp - 1;
703                                         }
704                                         if (sp == lp)
705                                                 if (!(sp = lastb))
706                                                         sp = lp + wid - 1;
707                                         len -= sp - lp + 1;
708                                         while (cp < ep && lp <= sp)
709                                                 *cp++ = *lp++;
710                                         *cp++ = '\n';
711                                         for (i = indent; cp < ep && i > 0; i--)
712                                                 *cp++ = ' ';
713                                         while (isspace(*lp))
714                                                 lp++, len--;
715                                 }
716                                 PUTS(cp, lp);
717                         }
718                         break;
719
720                 case FT_PARSEADDR:
721                         comp = fmt->f_comp;
722                         if (comp->c_mn != &fmt_mnull)
723                                 mnfree(comp->c_mn);
724                         if ((sp = comp->c_text) && (sp = getname(sp)) &&
725                                 (mn = getm(sp, NULLCP, 0, fmt_norm, NULLCP))) {
726                                 comp->c_mn = mn;
727                                 while (getname(""))
728                                         ;
729                         } else
730                                 comp->c_mn = &fmt_mnull;
731                         break;
732
733                 case FT_MYMBOX:
734                         /*
735                          * If there's no component, we say true.  Otherwise
736                          * we say "true" only if we can parse the address and
737                          * it matches one of our addresses.
738                          */
739                         comp = fmt->f_comp;
740                         if (comp->c_mn != &fmt_mnull)
741                                 mnfree(comp->c_mn);
742                         if ((sp = comp->c_text) && (sp = getname(sp)) &&
743                                 (mn = getm(sp, NULLCP, 0, AD_NAME, NULLCP))) {
744                                 comp->c_mn = mn;
745                                 comp->c_flags = ismymbox(mn);
746                                 while (sp = getname(sp))
747                                         if (comp->c_flags == 0 &&
748                                             (mn = getm(sp, NULLCP, 0,
749                                             AD_NAME, NULLCP)))
750                                                 comp->c_flags |= ismymbox(mn);
751                         } else {
752                                 comp->c_flags = (comp->c_text == 0);
753                                 comp->c_mn = &fmt_mnull;
754                         }
755                         break;
756
757                 case FT_ADDTOSEQ:
758                         /*
759                          * If we're working on a folder (as opposed to a
760                          * file), add the current msg to sequence given in
761                          * literal field.  Don't disturb string or value
762                          * registers.
763                          */
764                         if (fmt_current_folder)
765                                 (void) m_seqadd(fmt_current_folder,
766                                     fmt->f_text, dat[0], -1);
767                         break;
768                 }
769                 fmt++;
770         }
771 finished:
772         if (cp[-1] != '\n')
773                 *cp++ = '\n';
774         *cp = NULL;
775         return (value);
776 }