source: trunk/third/nmh/sbr/fmt_compile.c @ 12455

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