Added all of the MH sources, including RCS files, in
[mmh] / docs / historical / mh-6.8.5 / sbr / fmtcompile.c
1 /* fmtcompile.c - "compile" format strings for fmtscan */
2 #ifndef lint
3 static char ident[] = "@(#)$Id: fmtcompile.c,v 1.17 1993/08/19 21:05:42 jromine Exp shettich $";
4 #endif  /* lint */
5
6 #include "../h/mh.h"
7 #include "../h/addrsbr.h"
8 #include "../h/formatsbr.h"
9 #include "../zotnet/tws.h"
10 #include "../h/fmtcompile.h"
11 #include <ctype.h>
12 #include <stdio.h>
13 #include <sys/types.h>
14 #include <sys/stat.h>
15
16 static struct format *formatvec;        /* array to hold formats */
17 static struct format *next_fp;          /* next free format slot */
18 static struct format *fp;               /* current format slot */
19 static struct comp *cm;                 /* most recent comp ref */
20 static struct ftable *ftbl;             /* most recent func ref */
21 static int ncomp;
22 static int infunction;                  /* function nesting cnt */
23
24 extern char *getusr();
25 extern struct mailname fmt_mnull;
26
27 struct ftable {
28     char        *name;          /* function name */
29     char        type;           /* argument type */
30 #define         TF_COMP 0           /* component expected */
31 #define         TF_NUM  1           /* number expected */
32 #define         TF_STR  2           /* string expected */
33 #define         TF_EXPR 3           /* component or func. expected */
34 #define         TF_NONE 4           /* no argument */
35 #define         TF_MYBOX 5          /* special - get current user's mbox */
36 #define         TF_NOW  6           /* special - get current unix time */
37 #define         TF_EXPR_SV 7        /* like expr but save current str reg */
38 #define         TF_NOP  8           /* like expr but no result */
39     char        f_type;         /* fmt type */
40     char        extra;          /* arg. type dependent extra info */
41     char        flags;
42 #define         TFL_PUTS  1         /* implicit putstr if top level */
43 #define         TFL_PUTN  2         /* implicit putnum if top level */
44 };
45
46 static struct ftable functable[] = {
47     "nonzero",  TF_EXPR,        FT_V_NE,        FT_IF_V_NE,     0,
48     "zero",     TF_EXPR,        FT_V_EQ,        FT_IF_V_EQ,     0,
49     "eq",       TF_NUM,         FT_V_EQ,        FT_IF_V_EQ,     0,
50     "ne",       TF_NUM,         FT_V_NE,        FT_IF_V_NE,     0,
51     "gt",       TF_NUM,         FT_V_GT,        FT_IF_V_GT,     0,
52     "null",     TF_EXPR,        FT_S_NULL,      FT_IF_S_NULL,   0,
53     "nonnull",  TF_EXPR,        FT_S_NONNULL,   FT_IF_S,        0,
54     "match",    TF_STR,         FT_V_MATCH,     FT_IF_MATCH,    0,
55     "amatch",   TF_STR,         FT_V_AMATCH,    FT_IF_AMATCH,   0,
56
57     "putstr",   TF_EXPR,        FT_STR,         0,              0,
58     "putstrf",  TF_EXPR,        FT_STRF,        0,              0,
59     "putnum",   TF_EXPR,        FT_NUM,         0,              0,
60     "putnumf",  TF_EXPR,        FT_NUMF,        0,              0,
61     "putaddr",  TF_STR,         FT_PUTADDR,     0,              0,
62     "void",     TF_NOP,         0,              0,              0,
63
64     "comp",     TF_COMP,        FT_LS_COMP,     0,              TFL_PUTS,
65     "lit",      TF_STR,         FT_LS_LIT,      0,              TFL_PUTS,
66     "getenv",   TF_STR,         FT_LS_GETENV,   0,              TFL_PUTS,
67     "profile",  TF_STR,         FT_LS_MFIND,    0,              TFL_PUTS,
68     "trim",     TF_EXPR,        FT_LS_TRIM,     0,              0,
69     "compval",  TF_COMP,        FT_LV_COMP,     0,              TFL_PUTN,
70     "compflag", TF_COMP,        FT_LV_COMPFLAG, 0,              TFL_PUTN,
71     "num",      TF_NUM,         FT_LV_LIT,      0,              TFL_PUTN,
72     "msg",      TF_NONE,        FT_LV_DAT,      0,              TFL_PUTN,
73     "cur",      TF_NONE,        FT_LV_DAT,      1,              TFL_PUTN,
74     "size",     TF_NONE,        FT_LV_DAT,      2,              TFL_PUTN,
75     "width",    TF_NONE,        FT_LV_DAT,      3,              TFL_PUTN,
76     "unseen",   TF_NONE,        FT_LV_DAT,      4,              TFL_PUTN,
77     "dat",      TF_NUM,         FT_LV_DAT,      0,              TFL_PUTN,
78     "strlen",   TF_NONE,        FT_LV_STRLEN,   0,              TFL_PUTN,
79     "me",       TF_MYBOX,       FT_LS_LIT,      0,              TFL_PUTS,
80     "plus",     TF_NUM,         FT_LV_PLUS_L,   0,              TFL_PUTN,
81     "minus",    TF_NUM,         FT_LV_MINUS_L,  0,              TFL_PUTN,
82     "divide",   TF_NUM,         FT_LV_DIVIDE_L, 0,              TFL_PUTN,
83     "modulo",   TF_NUM,         FT_LV_MODULO_L, 0,              TFL_PUTN,
84     "charleft", TF_NONE,        FT_LV_CHAR_LEFT, 0,             TFL_PUTN,
85     "timenow",  TF_NOW,         FT_LV_LIT,      0,              TFL_PUTN,
86
87     "month",    TF_COMP,        FT_LS_MONTH,    FT_PARSEDATE,   TFL_PUTS,
88     "lmonth",   TF_COMP,        FT_LS_LMONTH,   FT_PARSEDATE,   TFL_PUTS,
89     "tzone",    TF_COMP,        FT_LS_ZONE,     FT_PARSEDATE,   TFL_PUTS,
90     "day",      TF_COMP,        FT_LS_DAY,      FT_PARSEDATE,   TFL_PUTS,
91     "weekday",  TF_COMP,        FT_LS_WEEKDAY,  FT_PARSEDATE,   TFL_PUTS,
92     "tws",      TF_COMP,        FT_LS_822DATE,  FT_PARSEDATE,   TFL_PUTS,
93     "sec",      TF_COMP,        FT_LV_SEC,      FT_PARSEDATE,   TFL_PUTN,
94     "min",      TF_COMP,        FT_LV_MIN,      FT_PARSEDATE,   TFL_PUTN,
95     "hour",     TF_COMP,        FT_LV_HOUR,     FT_PARSEDATE,   TFL_PUTN,
96     "mday",     TF_COMP,        FT_LV_MDAY,     FT_PARSEDATE,   TFL_PUTN,
97     "mon",      TF_COMP,        FT_LV_MON,      FT_PARSEDATE,   TFL_PUTN,
98     "year",     TF_COMP,        FT_LV_YEAR,     FT_PARSEDATE,   TFL_PUTN,
99     "yday",     TF_COMP,        FT_LV_YDAY,     FT_PARSEDATE,   TFL_PUTN,
100     "wday",     TF_COMP,        FT_LV_WDAY,     FT_PARSEDATE,   TFL_PUTN,
101     "zone",     TF_COMP,        FT_LV_ZONE,     FT_PARSEDATE,   TFL_PUTN,
102     "clock",    TF_COMP,        FT_LV_CLOCK,    FT_PARSEDATE,   TFL_PUTN,
103     "rclock",   TF_COMP,        FT_LV_RCLOCK,   FT_PARSEDATE,   TFL_PUTN,
104     "sday",     TF_COMP,        FT_LV_DAYF,     FT_PARSEDATE,   TFL_PUTN,
105     "szone",    TF_COMP,        FT_LV_ZONEF,    FT_PARSEDATE,   TFL_PUTN,
106     "dst",      TF_COMP,        FT_LV_DST,      FT_PARSEDATE,   TFL_PUTN,
107     "pretty",   TF_COMP,        FT_LS_PRETTY,   FT_PARSEDATE,   TFL_PUTS,
108     "nodate",   TF_COMP,        FT_LV_COMPFLAG, FT_PARSEDATE,   TFL_PUTN,
109     "date2local", TF_COMP,      FT_LOCALDATE,   FT_PARSEDATE,   0,
110     "date2gmt", TF_COMP,        FT_GMTDATE,     FT_PARSEDATE,   0,
111
112     "pers",     TF_COMP,        FT_LS_PERS,     FT_PARSEADDR,   TFL_PUTS,
113     "mbox",     TF_COMP,        FT_LS_MBOX,     FT_PARSEADDR,   TFL_PUTS,
114     "host",     TF_COMP,        FT_LS_HOST,     FT_PARSEADDR,   TFL_PUTS,
115     "path",     TF_COMP,        FT_LS_PATH,     FT_PARSEADDR,   TFL_PUTS,
116     "gname",    TF_COMP,        FT_LS_GNAME,    FT_PARSEADDR,   TFL_PUTS,
117     "note",     TF_COMP,        FT_LS_NOTE,     FT_PARSEADDR,   TFL_PUTS,
118     "addr",     TF_COMP,        FT_LS_ADDR,     FT_PARSEADDR,   TFL_PUTS,
119     "proper",   TF_COMP,        FT_LS_822ADDR,  FT_PARSEADDR,   TFL_PUTS,
120     "type",     TF_COMP,        FT_LV_HOSTTYPE, FT_PARSEADDR,   TFL_PUTN,
121     "ingrp",    TF_COMP,        FT_LV_INGRPF,   FT_PARSEADDR,   TFL_PUTN,
122     "nohost",   TF_COMP,        FT_LV_NOHOSTF,  FT_PARSEADDR,   TFL_PUTN,
123     "formataddr", TF_EXPR_SV,   FT_FORMATADDR,  FT_FORMATADDR,  0,
124     "friendly", TF_COMP,        FT_LS_FRIENDLY, FT_PARSEADDR,   TFL_PUTS,
125
126     "mymbox",   TF_COMP,        FT_LV_COMPFLAG, FT_MYMBOX,      TFL_PUTN,
127     "addtoseq", TF_STR,         FT_ADDTOSEQ,    0,              0,
128
129     (char *)0,  0,              0,              0,              0    
130 };
131
132
133 time_t time ();
134
135 static struct ftable *lookup(name)
136     register char *name;
137 {
138     register struct ftable *t = functable;
139     register char *nm;
140     register char c = *name;
141
142     while (nm = t->name) {
143         if (*nm == c && strcmp (nm, name) == 0)
144             return (ftbl = t);
145
146         t++;
147     }
148     return (struct ftable *)0;
149 }
150
151
152 #define NEWCOMP(cm,name)\
153         cm = ((struct comp *)calloc(1, sizeof (struct comp)));\
154         cm->c_name = name; ncomp++;\
155         i = CHASH(name); cm->c_next = wantcomp[i]; wantcomp[i] = cm;
156
157 #define NEWFMT (next_fp++)
158 #define NEW(type,fill,wid)\
159         fp=NEWFMT; fp->f_type=(type); fp->f_fill=(fill); fp->f_width=(wid);
160
161 #define ADDC(name)\
162         FINDCOMP( cm, name );\
163         if ( ! cm ) {\
164             NEWCOMP(cm,name);\
165         }\
166         fp->f_comp = cm;
167
168 #define LV( type, value )       NEW(type,0,0); fp->f_value = (value);
169 #define LS( type, str )         NEW(type,0,0); fp->f_text = (str);
170 #define PUTCOMP( comp )         NEW(FT_COMP,0,0); ADDC(comp);
171 #define PUTLIT( str )           NEW(FT_LIT,0,0); fp->f_text = (str);
172 #define PUTC( c )               NEW(FT_CHAR,0,0); fp->f_char = (c);
173
174 static char *compile();
175 static char *do_spec();
176 static char *do_name();
177 static char *do_func();
178 static char *do_expr();
179 static char *do_if();
180 static char *do_loop();
181
182 static char *format_string;
183 static char *usr_fstring;       /* for CERROR */
184
185 #define CERROR(str) compile_error (str, cp)
186
187 static void
188 compile_error(str, cp)
189     char *str;
190     char *cp;
191 {
192     int errpos = cp - format_string;
193     int errctx = errpos > 20 ? 20 : errpos;
194     int i;
195
196     usr_fstring[errpos] = '\0';
197     for (i = errpos-errctx; i < errpos; i++)
198 #ifdef LOCALE
199         if (iscntrl(usr_fstring[i]))
200 #else
201         if (usr_fstring[i] < 32)
202 #endif
203             usr_fstring[i] = '_';
204     advise(NULLCP, "\"%s\": format compile error - %s",
205            &usr_fstring[errpos-errctx], str);
206     adios (NULLCP, "%*s", errctx+1, "^");
207 }
208
209 /*
210  * Compile format string "fstring" into format list "fmt".
211  * Return the number of header components found in the format
212  * string.
213  */
214 fmt_compile( fstring, fmt )
215     register char *fstring;
216     struct format **fmt;
217 {
218     register char *cp;
219     int i;
220
221     if (format_string)
222         (void) free (format_string);
223     format_string = getcpy (fstring);
224     usr_fstring = fstring;
225
226     /* init the component hash table. */
227     for (i = 0; i < sizeof(wantcomp)/sizeof(wantcomp[0]); i++)
228         wantcomp[i] = 0;
229
230     bzero ((char *) &fmt_mnull, sizeof fmt_mnull);
231
232     /* it takes at least 4 char to generate one format so we
233      * allocate a worst-case format array using 1/4 the length
234      * of the format string.  We actually need twice this much
235      * to handle both pre-processing (e.g., address parsing) and
236      * normal processing.
237      */
238     i = strlen(fstring)/2 + 1;
239     next_fp = formatvec = (struct format *)calloc ((unsigned) i,
240                                                    sizeof(struct format));
241     if (next_fp == NULL)
242         adios (NULLCP, "unable to allocate format storage");
243
244     ncomp = 0;
245     infunction = 0;
246
247     cp = compile(format_string);
248     if (*cp) {
249         CERROR("extra '%>', '%|' or '%?'");
250     }
251     LV(FT_DONE,0);              /* really done */
252     *fmt = formatvec;
253
254     return (ncomp);
255 }
256
257 static char *compile (sp)
258     register char *sp;
259 {
260     register char *cp = sp;
261     register int  c;
262
263     for (;;) {
264         sp = cp;
265         while ((c = *cp) && c != '%')
266             cp++;
267         *cp = 0;
268         switch (cp-sp) {
269         case 0:
270             break;
271         case 1:
272             PUTC(*sp);
273             break;
274         default:
275             PUTLIT(sp);
276             break;
277         }
278         if (c == 0)
279             return (cp);
280
281         switch (c = *++cp) {
282         case '%':
283             PUTC (*cp);
284             cp++;
285             break;
286
287         case '|':
288         case '>':
289         case '?':
290         case ']':
291             return (cp);
292
293         case '<':
294             cp = do_if(++cp);
295             break;
296
297         case '[':       /* ] */
298             cp = do_loop(++cp);
299             break;
300
301         case ';':       /* comment line */
302             cp++;
303             while ((c = *cp++) && c != '\n')
304                 continue;
305             break;
306
307         default:
308             cp = do_spec(cp);
309             break;
310         }
311     }
312 }
313
314
315 static char *do_spec(sp)
316     register char *sp;
317 {
318     register char *cp = sp;
319     register int c;
320 #ifndef lint
321     register int ljust = 0;
322 #endif  /* not lint */
323     register int wid = 0;
324     register char fill = ' ';
325
326     c = *cp++;
327     if (c == '-') {
328         ljust++;
329         c = *cp++;
330     }
331     if (c == '0') {
332         fill = c;
333         c = *cp++;
334     }
335     while (isdigit(c)) {
336         wid = wid*10 + (c - '0');
337         c = *cp++;
338     }
339     if (c == '{') {
340         cp = do_name(cp, 0);
341         if (! infunction)
342             fp->f_type = wid? FT_COMPF : FT_COMP;
343     }
344     else if (c == '(') {
345         cp = do_func(cp);
346         if (! infunction) {
347             if (ftbl->flags & TFL_PUTS) {
348                 LV( wid? FT_STRF : FT_STR, ftbl->extra);
349             }
350             else if (ftbl->flags & TFL_PUTN) {
351                 LV( wid? FT_NUMF : FT_NUM, ftbl->extra);
352             }
353         }
354     }
355     else {
356         CERROR("component or function name expected");
357     }
358     if (ljust)
359         wid = -wid;
360     fp->f_width = wid;
361     fp->f_fill = fill;
362
363     return (cp);
364 }
365
366 static char *do_name(sp, preprocess)
367     char *sp;
368     int  preprocess;
369 {
370     register char *cp = sp;
371     register int c;
372     register int i;
373     static int primed = 0;
374
375     while (isalnum(c = *cp++) || c == '-' || c == '_')
376         ;
377     if (c != '}') {
378         CERROR("'}' expected");
379     }
380     cp[-1] = '\0';
381     PUTCOMP(sp);
382     switch (preprocess) {
383
384     case FT_PARSEDATE:
385         if (cm->c_type & CT_ADDR) {
386             CERROR("component used as both date and address");
387         }
388         if (! (cm->c_type & CT_DATE)) {
389             cm->c_tws = (struct tws *)
390                                 calloc((unsigned) 1, sizeof *cm -> c_tws);
391             fp->f_type = preprocess;
392             PUTCOMP(sp);
393             cm->c_type |= CT_DATE;
394         }
395         break;
396
397     case FT_MYMBOX:
398         if (!primed) {
399             (void) ismymbox ((struct mailname *) 0);
400             primed++;
401         }
402         cm->c_type |= CT_MYMBOX;
403         /* fall through */
404     case FT_PARSEADDR:
405         if (cm->c_type & CT_DATE) {
406             CERROR("component used as both date and address");
407         }
408         if (! (cm->c_type & CT_ADDRPARSE)) {
409             cm->c_mn = &fmt_mnull;
410             fp->f_type = preprocess;
411             PUTCOMP(sp);
412             cm->c_type |= (CT_ADDR | CT_ADDRPARSE);
413         }
414         break;
415
416     case FT_FORMATADDR:
417         if (cm->c_type & CT_DATE) {
418             CERROR("component used as both date and address");
419         }
420         cm->c_type |= CT_ADDR;
421         break;
422     }
423     return (cp);
424 }
425
426 static char *do_func(sp)
427     char *sp;
428 {
429     register char *cp = sp;
430     register int c;
431     register struct ftable *t;
432     register int n;
433     int mflag;          /* minus sign in NUM */
434
435     infunction++;
436
437     while (isalnum(c = *cp++)) 
438         ;
439     if (c != '(' && c != '{' && c != ' ' && c != ')') {
440         CERROR("'(', '{', ' ' or ')' expected");
441     }
442     cp[-1] = '\0';
443     if ((t = lookup (sp)) == 0) {
444         CERROR("unknown function");
445     }
446     if (isspace(c))
447         c = *cp++;
448
449     switch (t->type) {
450
451     case TF_COMP:
452         if (c != '{') {
453             CERROR("component name expected");
454         }
455         cp = do_name(cp, t->extra);
456         fp->f_type = t->f_type;
457         c = *cp++;
458         break;
459
460     case TF_NUM:
461         if (mflag = (c == '-'))
462             c = *cp++;
463         n = 0;
464         while (isdigit(c)) {
465             n = n*10 + (c - '0');
466             c = *cp++;
467         }
468         if (mflag)
469             n = (-n);
470         LV(t->f_type,n);
471         break;
472
473     case TF_STR:
474         sp = cp - 1;
475         while (c && c != ')')
476             c = *cp++;
477         cp[-1] = '\0';
478         LS(t->f_type,sp);
479         break;
480
481     case TF_NONE:
482         LV(t->f_type,t->extra);
483         break;
484
485     case TF_MYBOX:
486         LS(t->f_type, getusr());
487         break;
488
489     case TF_NOW:
490         LV(t->f_type, time((time_t *) 0));
491         break;
492
493     case TF_EXPR_SV:
494         LV(FT_SAVESTR, 0);
495         /* fall through */
496     case TF_EXPR:
497         *--cp = c;
498         cp = do_expr(cp, t->extra);
499         LV(t->f_type, 0);
500         c = *cp++;
501         ftbl = t;
502         break;
503
504     case TF_NOP:
505         *--cp = c;
506         cp = do_expr(cp, t->extra);
507         c = *cp++;
508         ftbl = t;
509         break;
510     }
511     if (c != ')') {
512         CERROR("')' expected");
513     }
514     --infunction;
515     return (cp);
516 }
517
518 static char *do_expr (sp, preprocess)
519     char *sp;
520 {
521     register char *cp = sp;
522     register int  c;
523
524     if ((c = *cp++) == '{') {
525         cp = do_name (cp, preprocess);
526         fp->f_type = FT_LS_COMP;
527     } else if (c == '(') {
528         cp = do_func (cp);
529     } else if (c == ')') {
530         return (--cp);
531     } else if (c == '%' && *cp == '<') {
532         cp = do_if (cp+1);
533     } else {
534         CERROR ("'(', '{', '%<' or ')' expected");
535     }
536     return (cp);
537 }
538
539 static char *do_loop(sp)
540     register char *sp;
541 {
542     register char *cp = sp;
543     struct format *floop;
544
545     floop = next_fp;
546     cp = compile (cp);
547     if (*cp++ != ']')
548         CERROR ("']' expected");
549
550     LV(FT_DONE, 1);             /* not yet done */
551     LV(FT_GOTO, 0);
552     fp->f_skip = floop - fp;    /* skip backwards */
553
554     return cp;
555 }
556
557 static char *do_if(sp)
558     register char *sp;
559 {
560     register char *cp = sp;
561     register struct format *fexpr,
562                            *fif = (struct format *)NULL;
563     register int c = '<';
564
565     for (;;) {
566         if (c == '<') {                 /* doing an IF */
567             if ((c = *cp++) == '{') /*}*/{
568                 cp = do_name(cp, 0);
569                 fp->f_type = FT_LS_COMP;
570                 LV (FT_IF_S, 0);
571             }
572             else if (c == '(') {
573                 cp = do_func(cp);
574                 /* see if we can merge the load and the "if" */
575                 if (ftbl->f_type >= IF_FUNCS)
576                     fp->f_type = ftbl->extra;
577                 else {
578                     LV (FT_IF_V_NE, 0);
579                 }
580             }
581             else {
582                 CERROR("'(' or '{' expected");  /*}*/
583             }
584         }
585
586         fexpr = fp;                     /* loc of [ELS]IF */
587         cp = compile (cp);              /* compile IF TRUE stmts */
588         if (fif)
589             fif->f_skip = next_fp - fif;
590
591         if ((c = *cp++) == '|') {       /* the last ELSE */
592             LV(FT_GOTO, 0);
593             fif = fp;                   /* loc of GOTO */
594             fexpr->f_skip = next_fp - fexpr;
595
596             fexpr = (struct format *)NULL;/* no extra ENDIF */
597
598             cp = compile (cp);          /* compile ELSE stmts */
599             fif->f_skip = next_fp - fif;
600             c = *cp++;
601         }
602         else if (c == '?') {            /* another ELSIF */
603             LV(FT_GOTO, 0);
604             fif = fp;                   /* loc of GOTO */
605             fexpr->f_skip = next_fp - fexpr;
606
607             c = '<';                    /* impersonate an IF */
608             continue;
609         }
610         break;
611     }
612
613     if (c != '>') {
614         CERROR("'>' expected.");
615     }
616
617     if (fexpr)                          /* IF ... [ELSIF ...] ENDIF */
618         fexpr->f_skip = next_fp - fexpr;
619
620     return (cp);
621 }