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