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: /* */ 14: 15: 16: #define NFMTS MAXARGS 17: #define QUOTE '\\' 18: 19: static char *formats = 0; 20: extern char *formataddr (); /* hook for custom address formatting */ 21: 22: int fmt_norm = AD_NAME; 23: struct mailname fmt_mnull; 24: 25: /* */ 26: 27: /* MAJOR HACK: See MHCHANGES for discussion */ 28: 29: char *new_fs (form, format, def) 30: register char *form, 31: *format, 32: *def; 33: { 34: struct stat st; 35: register FILE *fp; 36: 37: if (formats) 38: free (formats); 39: 40: if (form) { 41: if ((fp = fopen (libpath (form), "r")) == NULL) 42: adios (form, "unable to open format file"); 43: 44: if (fstat (fileno (fp), &st) == NOTOK) 45: adios (form, "unable to stat format file"); 46: 47: if ((formats = malloc ((unsigned) st.st_size)) == NULLCP) 48: adios (form, "unable to allocate space for format"); 49: 50: if (read (fileno(fp), formats, st.st_size) != st.st_size) 51: adios (form, "error reading format file"); 52: 53: (void) fclose (fp); 54: } 55: else { 56: formats = getcpy (format ? format : def); 57: } 58: 59: normalize (formats); 60: 61: return formats; 62: } 63: 64: /* */ 65: 66: static normalize (cp) 67: register char *cp; 68: { 69: register char *dp; 70: 71: for (dp = cp; *cp; cp++) 72: if (*cp != QUOTE) 73: *dp++ = *cp; 74: else 75: switch (*++cp) { 76: #define grot(x) case 'x': *dp++ = '\x'; break; 77: grot (b); 78: grot (f); 79: grot (n); 80: grot (r); 81: grot (t); 82: 83: case '\n': 84: break; 85: 86: case NULL: 87: cp--; /* fall */ 88: default: 89: *dp++ = *cp; 90: break; 91: } 92: 93: *dp = NULL; 94: } 95: 96: /* */ 97: /* 98: * test if string "sub" appears anywhere in string "str" 99: * (case insensitive). 100: */ 101: 102: static int match (str, sub) 103: register char *str, 104: *sub; 105: { 106: register int c1; 107: register int c2; 108: register char *s1; 109: register char *s2; 110: 111: while (c1 = *sub) { 112: while ((c2 = *str++) && (c1 | 040) != (c2 | 040)) 113: ; 114: if (! c2) 115: return 0; 116: s1 = sub + 1; s2 = str; 117: while ((c1 = *s1++) && (c1 | 040) == (*s2++ | 040)) 118: ; 119: if (! c1) 120: return 1; 121: } 122: return 1; 123: } 124: /* */ 125: 126: /* macros to format data */ 127: 128: #define PUTDF(cp, num, wid, fill) if (cp + wid < ep){\ 129: if((i = (num))<0) i = -(num);\ 130: sp = cp + wid;\ 131: do {\ 132: *--sp = (i % 10) + '0';\ 133: i /= 10;\ 134: } while (i > 0 && sp > cp);\ 135: if (i > 0)\ 136: *sp = '?';\ 137: else if ((num) < 0 && sp > cp)\ 138: *--sp = '-';\ 139: while (sp > cp)\ 140: *--sp = fill;\ 141: cp += wid;\ 142: } 143: #define PUTD(cp, num) if (cp < ep){\ 144: if((i = (num))==0) *cp++ = '0';\ 145: else {\ 146: if((i = (num))<0) \ 147: *cp++ = '-', i = -(num);\ 148: c = 10;\ 149: while (c <= i) \ 150: c *= 10;\ 151: while (cp < ep && c > 1) {\ 152: c /= 10;\ 153: *cp++ = (i / c) + '0';\ 154: i %= c;\ 155: }\ 156: }\ 157: } 158: #define PUTSF(cp, str, wid, fill) {\ 159: i = (wid);\ 160: if (sp = (str)) {\ 161: while ((c = *sp) && c <= 32)\ 162: sp++;\ 163: while( (c = *sp++) && --i >= 0 && cp < ep)\ 164: if ( c > 32 ) \ 165: *cp++ = c;\ 166: else {\ 167: while ( (c = *sp) && c <= 32 )\ 168: sp++;\ 169: *cp++ = ' ';\ 170: }\ 171: }\ 172: while( --i >= 0 && cp < ep)\ 173: *cp++ = fill;\ 174: } 175: #define PUTS(cp, str) {\ 176: if (sp = (str)) {\ 177: while ((c = *sp) && c <= 32)\ 178: sp++;\ 179: while( (c = *sp++) && cp < ep)\ 180: if ( c > 32 ) \ 181: *cp++ = c;\ 182: else {\ 183: while ( (c = *sp) && c <= 32 )\ 184: sp++;\ 185: *cp++ = ' ';\ 186: }\ 187: }\ 188: } 189: 190: 191: static char *lmonth[] = { "January", "February","March", "April", 192: "May", "June", "July", "August", 193: "September","October", "November","December" }; 194: 195: 196: fmtscan (format, scanl, width, dat) 197: struct format *format; 198: char *scanl; 199: int width; 200: int dat[]; 201: { 202: register char *cp = scanl; 203: register char *ep = scanl + width - 1; 204: register struct format *fmt = format; 205: register char *str = NULLCP; 206: register int value = 0; 207: register char *sp; 208: register int i; 209: register int c; 210: register struct comp *comp; 211: register struct tws *tws; 212: register struct mailname *mn; 213: char *savestr; 214: char buffer[BUFSIZ]; 215: 216: while (cp < ep) { 217: switch (fmt->f_type) { 218: 219: case FT_COMP: 220: PUTS (cp, fmt->f_comp->c_text); 221: break; 222: case FT_COMPF: 223: PUTSF (cp, fmt->f_comp->c_text, fmt->f_width, fmt->f_fill); 224: break; 225: 226: case FT_LIT: 227: sp = fmt->f_text; 228: while( (c = *sp++) && cp < ep) 229: *cp++ = c; 230: break; 231: case FT_LITF: 232: sp = fmt->f_text; i = fmt->f_width; 233: while( (c = *sp++) && --i >= 0 && cp < ep) 234: *cp++ = c; 235: while( --i >= 0 && cp < ep) 236: *cp++ = fmt->f_fill; 237: break; 238: 239: case FT_STR: 240: PUTS (cp, str); 241: break; 242: case FT_STRF: 243: PUTSF (cp, str, fmt->f_width, fmt->f_fill); 244: break; 245: case FT_STRFW: 246: adios (NULLCP, "internal error (FT_STRFW)"); 247: 248: case FT_NUM: 249: PUTD (cp, value); 250: break; 251: case FT_NUMF: 252: PUTDF (cp, value, fmt->f_width, fmt->f_fill); 253: break; 254: 255: case FT_CHAR: 256: *cp++ = fmt->f_char; 257: break; 258: 259: case FT_DONE: 260: goto finished; 261: 262: case FT_IF_S: 263: if (str == NULLCP || *str == NULL) { 264: fmt += fmt->f_skip; 265: continue; 266: } 267: break; 268: 269: case FT_IF_S_NULL: 270: if (str != NULLCP && *str != NULL) { 271: fmt += fmt->f_skip; 272: continue; 273: } 274: break; 275: 276: case FT_IF_V_EQ: 277: if (value != fmt->f_value) { 278: fmt += fmt->f_skip; 279: continue; 280: } 281: break; 282: 283: case FT_IF_V_NE: 284: if (value == fmt->f_value) { 285: fmt += fmt->f_skip; 286: continue; 287: } 288: break; 289: 290: case FT_IF_V_GT: 291: if (value <= fmt->f_value) { 292: fmt += fmt->f_skip; 293: continue; 294: } 295: break; 296: 297: case FT_IF_MATCH: 298: if (! match (str, fmt->f_text)) { 299: fmt += fmt->f_skip; 300: continue; 301: } 302: break; 303: 304: case FT_V_MATCH: 305: value = match (str, fmt->f_text); 306: break; 307: 308: case FT_IF_AMATCH: 309: if (! uprf (str, fmt->f_text)) { 310: fmt += fmt->f_skip; 311: continue; 312: } 313: break; 314: 315: case FT_V_AMATCH: 316: value = uprf (str, fmt->f_text); 317: break; 318: 319: case FT_S_NONNULL: 320: value = (str != NULLCP && *str != NULL); 321: break; 322: 323: case FT_S_NULL: 324: value = (str == NULLCP || *str == NULL); 325: break; 326: 327: case FT_V_EQ: 328: value = (fmt->f_value == value); 329: break; 330: 331: case FT_V_NE: 332: value = (fmt->f_value != value); 333: break; 334: 335: case FT_V_GT: 336: value = (fmt->f_value > value); 337: break; 338: 339: case FT_GOTO: 340: fmt += fmt->f_skip; 341: continue; 342: 343: case FT_NOP: 344: break; 345: 346: case FT_LS_COMP: 347: str = fmt->f_comp->c_text; 348: break; 349: case FT_LS_LIT: 350: str = fmt->f_text; 351: break; 352: 353: case FT_LV_COMPFLAG: 354: value = fmt->f_comp->c_flags; 355: break; 356: case FT_LV_COMP: 357: value = (comp = fmt->f_comp)->c_text ? atoi(comp->c_text) : 0; 358: break; 359: case FT_LV_LIT: 360: value = fmt->f_value; 361: break; 362: case FT_LV_DAT: 363: value = dat[fmt->f_value]; 364: break; 365: case FT_LV_STRLEN: 366: value = strlen(str); 367: break; 368: case FT_LV_CHAR_LEFT: 369: value = width - (cp - scanl); 370: break; 371: case FT_LV_PLUS_L: 372: value += fmt->f_value; 373: break; 374: case FT_LV_MINUS_L: 375: value = fmt->f_value - value; 376: break; 377: case FT_SAVESTR: 378: savestr = str; 379: break; 380: 381: case FT_LV_SEC: 382: value = fmt->f_comp->c_tws->tw_sec; 383: break; 384: case FT_LV_MIN: 385: value = fmt->f_comp->c_tws->tw_min; 386: break; 387: case FT_LV_HOUR: 388: value = fmt->f_comp->c_tws->tw_hour; 389: break; 390: case FT_LV_MDAY: 391: value = fmt->f_comp->c_tws->tw_mday; 392: break; 393: case FT_LV_MON: 394: value = fmt->f_comp->c_tws->tw_mon + 1; 395: break; 396: case FT_LS_MONTH: 397: str = tw_moty[fmt->f_comp->c_tws->tw_mon]; 398: break; 399: case FT_LS_LMONTH: 400: str = lmonth[fmt->f_comp->c_tws->tw_mon]; 401: break; 402: case FT_LS_ZONE: 403: str = dtwszone (fmt->f_comp->c_tws); 404: break; 405: case FT_LV_YEAR: 406: value = fmt->f_comp->c_tws->tw_year; 407: break; 408: case FT_LV_WDAY: 409: if (!(((tws = fmt->f_comp->c_tws)->tw_flags) & (TW_SEXP|TW_SIMP))) 410: set_dotw (tws); 411: value = tws->tw_wday; 412: break; 413: case FT_LS_DAY: 414: if (!(((tws = fmt->f_comp->c_tws)->tw_flags) & (TW_SEXP|TW_SIMP))) 415: set_dotw (tws); 416: str = tw_dotw[tws->tw_wday]; 417: break; 418: case FT_LS_WEEKDAY: 419: if (!(((tws = fmt->f_comp->c_tws)->tw_flags) & (TW_SEXP|TW_SIMP))) 420: set_dotw (tws); 421: str = tw_ldotw[tws->tw_wday]; 422: break; 423: case FT_LV_YDAY: 424: value = fmt->f_comp->c_tws->tw_yday; 425: break; 426: case FT_LV_ZONE: 427: value = fmt->f_comp->c_tws->tw_zone; 428: break; 429: case FT_LV_CLOCK: 430: if ((value = fmt->f_comp->c_tws->tw_clock) == 0) 431: value = twclock(fmt->f_comp->c_tws); 432: break; 433: case FT_LV_RCLOCK: 434: if ((value = fmt->f_comp->c_tws->tw_clock) == 0) 435: value = twclock(fmt->f_comp->c_tws); 436: value = time((long *) 0) - value; 437: break; 438: case FT_LV_DAYF: 439: if (!(((tws = fmt->f_comp->c_tws)->tw_flags) & (TW_SEXP|TW_SIMP))) 440: set_dotw (tws); 441: switch (fmt->f_comp->c_tws->tw_flags & TW_SDAY) { 442: case TW_SEXP: 443: value = 1; break; 444: case TW_SIMP: 445: value = 0; break; 446: default: 447: value = -1; break; 448: } 449: break; 450: case FT_LV_TZONEF: 451: value = fmt->f_comp->c_tws->tw_flags & TW_DST; 452: break; 453: case FT_LS_822DATE: 454: str = dasctime ( fmt->f_comp->c_tws , TW_ZONE); 455: break; 456: case FT_LS_PRETTY: 457: str = dasctime ( fmt->f_comp->c_tws, TW_NULL); 458: break; 459: 460: case FT_LS_PERS: 461: str = fmt->f_comp->c_mn->m_pers; 462: break; 463: case FT_LS_MBOX: 464: str = fmt->f_comp->c_mn->m_mbox; 465: break; 466: case FT_LS_HOST: 467: str = fmt->f_comp->c_mn->m_host; 468: break; 469: case FT_LS_PATH: 470: str = fmt->f_comp->c_mn->m_path; 471: break; 472: case FT_LS_GNAME: 473: str = fmt->f_comp->c_mn->m_gname; 474: break; 475: case FT_LS_NOTE: 476: str = fmt->f_comp->c_mn->m_note; 477: break; 478: case FT_LS_822ADDR: 479: str = adrformat( fmt->f_comp->c_mn ); 480: break; 481: case FT_LV_HOSTTYPE: 482: value = fmt->f_comp->c_mn->m_type; 483: break; 484: case FT_LV_INGRPF: 485: value = fmt->f_comp->c_mn->m_ingrp; 486: break; 487: case FT_LV_NOHOSTF: 488: value = fmt->f_comp->c_mn->m_nohost; 489: break; 490: case FT_LS_FRIENDLY: 491: #ifdef BERK 492: str = fmt->f_comp->c_mn->m_mbox; 493: #else not BERK 494: mn = fmt -> f_comp -> c_mn; 495: if ((str = mn -> m_pers) == NULL) 496: switch (mn -> m_type) { 497: case LOCALHOST: 498: str = mn -> m_mbox; 499: break; 500: case UUCPHOST: 501: (void) sprintf (buffer, "%s!%s", 502: mn -> m_host, mn -> m_mbox); 503: str = buffer; 504: break; 505: default: 506: if (mn -> m_mbox) { 507: (void) sprintf (buffer, "%s@%s", 508: mn -> m_mbox, mn -> m_host); 509: str= buffer; 510: } 511: else 512: str = mn -> m_text; 513: break; 514: } 515: #endif BERK 516: break; 517: 518: case FT_PARSEDATE: 519: comp = fmt->f_comp; 520: if ((sp = comp->c_text) && (tws = dparsetime(sp))) { 521: *comp->c_tws = *tws; 522: comp->c_flags = 0; 523: } else if (comp->c_flags >= 0) { 524: bzero ((char *) comp -> c_tws, sizeof *comp -> c_tws); 525: comp->c_flags = 1; 526: } 527: break; 528: 529: case FT_FORMATADDR: 530: /* hook for custom address list formatting (see replsbr.c) */ 531: str = formataddr (savestr, str); 532: break; 533: 534: case FT_PUTADDR: 535: /* output the str register as an address component, 536: * splitting it into multiple lines if necessary. The 537: * value reg. contains the max line length. The lit. 538: * field may contain a string to prepend to the result 539: * (e.g., "To: ") 540: */ 541: { 542: register char *lp = str; 543: register int indent; 544: register int wid = value; 545: register int len = strlen (str); 546: register char *lastb; 547: 548: sp = fmt->f_text; 549: indent = strlen (sp); 550: wid -= indent; 551: while( (c = *sp++) && cp < ep) 552: *cp++ = c; 553: while (len > wid) { 554: /* try to break at a comma; failing that, break at a 555: * space, failing that, just split the line. 556: */ 557: lastb = 0; sp = lp + wid; 558: while (sp > lp && (c = *--sp) != ',') { 559: if (! lastb && isspace(c)) 560: lastb = sp - 1; 561: } 562: if (sp == lp) 563: if (! (sp = lastb)) 564: sp = lp + wid - 1; 565: len -= sp - lp + 1; 566: while (cp < ep && lp <= sp) 567: *cp++ = *lp++; 568: *cp++ = '\n'; 569: for (i=indent; cp < ep && i > 0; i--) 570: *cp++ = ' '; 571: while (isspace(*lp)) 572: lp++, len--; 573: } 574: PUTS (cp, lp); 575: } 576: break; 577: 578: case FT_PARSEADDR: 579: comp = fmt->f_comp; 580: if (comp->c_mn != &fmt_mnull) 581: mnfree (comp->c_mn); 582: if ((sp = comp->c_text) && (sp = getname(sp)) && 583: (mn = getm (sp, NULLCP, 0, fmt_norm, NULLCP))) { 584: comp->c_mn = mn; 585: while (getname("")) 586: ; 587: } else 588: comp->c_mn = &fmt_mnull; 589: break; 590: 591: case FT_MYMBOX: 592: /* 593: * if there's no component, we say true. Otherwise we 594: * say "true" only if we can parse the address and it 595: * matches one of our addresses. 596: */ 597: comp = fmt->f_comp; 598: if (comp->c_mn != &fmt_mnull) 599: mnfree (comp->c_mn); 600: if ((sp = comp->c_text) && (sp = getname(sp)) && 601: (mn = getm (sp, NULLCP, 0, AD_NAME, NULLCP))) { 602: comp->c_mn = mn; 603: comp->c_flags = ismymbox(mn); 604: while (getname("")) 605: ; 606: } else { 607: comp->c_flags = (comp->c_text == 0); 608: comp->c_mn = &fmt_mnull; 609: } 610: break; 611: } 612: fmt++; 613: } 614: finished:; 615: if (cp[-1] != '\n') 616: *cp++ = '\n'; 617: *cp = NULL; 618: return (value); 619: }