/* * Primitives: * -date select by transaction date * -edate select by entry date * -flag {x,-x} select by flag present/absent * -cat category-pattern select by category * -from from-tag select by from-field * -to to-tag select by to-field * -account tag ( -from tag -or -to tag ) * -amount select by amount * -absamount select by amount absolute value * -currency currency select by currency * * can be either one of lt, le, gt, ge, ne, eq, followed * by an appropriate value (a date for -date and -edate, an amount for * -amount), or one of gtlt, gtle, gelt, gele, followed by two such * values. In the first case, the condition is true if the record's * value obeys the indicated relation with the value; in the second * case, if it is gt/ge with respect to the first value and lt/le with * respect to the second (these are "between" comparisons). * * For -date and -edate, can be beq or bne. beq matches * if the date matches as given or if it matches after swapping the * month and day-of-month numbers; bne is the negative of beq. (If * the day-of-month figure is greater than 12, beq functions * identically to eq.) * * "date" means a date, where missing parts are supplied from the * current date (trailing digits of the year can be replaced; the * month and day must be replaced entirely or not at all). As * special cases, "now" and "today" refer to the current date. * Any of these forms can be followed by a + or - sign and a * number, which moves the date forward or backward by that many * days. Note that the entire date spec must be a single argument * (though it may contain whitespace). * "category-pattern" is presently just a string, which must be a * prefix of the category. * "from-tag" and "to-tag" are strings which must be matched literally. * "amount" is an amount; it must be a number, as appears in the entry. * "currency" is a string, which must match the currency of the entry. * Note that currency tags in entries are uppercase, and note also * that if you give the default currency it will not match an * entry with no currency. (Entries with no currency are * considered to have a zero-length currency tag for purposes of * this program.) * * These can be combined with -and, -or, -not, and parentheses. (Note * that ( and ) are special to the shell and will usually need to be * escaped. Each token must be a separate argument, as with find(1) - * but unlike find, all boolean connectives must be specified.) * Precedence is first the primitives described above, then * parentheses, then -not, then -and, and finally -or. * * Before all other arguments, one may specify -split , where is * a number; in this case, records that would normally be dropped are * written to the file descriptor given by the number. (This is * primarily intended for use in shell scripts.) */ #include #include #include #include #include #include "deconst.h" extern const char *__progname; enum nodetype { NT_PRIM, NT_NOT, NT_AND, NT_OR } ; enum primtype { PT_DATE, PT_EDATE, PT_FLAG, PT_CATEGORY, PT_FROM, PT_TO, PT_ACCOUNT, PT_AMOUNT, PT_ABSAMOUNT, PT_CURRENCY } ; enum cmp { C_LT, C_LE, C_GT, C_GE, C_NE, C_EQ, C_GTLT, C_GTLE, C_GELT, C_GELE, C_BEQ, C_BNE } ; typedef enum nodetype NODETYPE; typedef enum primtype PRIMTYPE; typedef enum cmp CMP; typedef struct node NODE; typedef struct prim PRIM; typedef struct date DATE; typedef struct str STR; struct date { unsigned int year : 12; #define MAXYEAR ((1<<12)-1) unsigned int month : 4; unsigned int day : 5; } ; struct str { char *s; int l; } ; struct prim { PRIMTYPE type; union { struct { CMP cmp; DATE date; DATE date2; } date; STR s; struct { CMP cmp; long int amt; long int amt2; } amount; } u; } ; struct node { NODETYPE type; union { PRIM *prim; struct { NODE *lhs; NODE *rhs; } binary; struct { NODE *arg; } unary; } u; } ; static int debugging = 0; static NODE *expr; static int parse_ac0; static int parse_ac; static char **parse_av; #define HAVENARGS(n) (parse_ac>=(n)) #define ARGFWD(n) (parse_av[(n)]) #define NEXTARG() (ARGFWD(0)) #define NEXTARGIS(s) ((parse_ac>0) && !strcmp(NEXTARG(),(s))) #define EATARG() do { parse_ac --; parse_av ++; } while (0) static DATE curdate; static STR line = { 0, 0 }; static DATE date; static DATE edate; static STR flags; static STR category; static STR from; static STR to; static long int amount; static STR currency; static FILE *failfile = 0; static void getcurtime(void) { long int now; struct tm *tm; time(&now); tm = localtime(&now); curdate.year = tm->tm_year + 1900; curdate.month = tm->tm_mon + 1; curdate.day = tm->tm_mday; } static char *bindex(const char *str, int len, char c) { char *s; s = deconst(str); for (;len>0;s++,len--) if (*s == c) return(s); return(0); } static inline unsigned int yeardays(unsigned int) __attribute__ ((const)); static inline unsigned int yeardays(unsigned int y) { if (y % 4) return(365); if (y % 100) return(366); if (y % 400) return(365); return(366); } static inline unsigned int monthdays(unsigned int, unsigned int) __attribute__ ((const)); static inline unsigned int monthdays(unsigned int m, unsigned int y) { if (m == 2) { if (y % 4) return(28); if (y % 100) return(29); if (y % 400) return(28); return(29); } /* JanFebMarAprMayJunJulAugSepOctNovDec */ return(" \37\00\37\36\37\36\37\37\36\37\36\37"[m]); } static void syntaxerror(const char *, ...) __attribute__ ((noreturn,format(printf,1,2))); static void syntaxerror(const char *fmt, ...) { va_list ap; fprintf(stderr,"%s: syntax error found at argument %d: ",__progname,parse_ac0+1-parse_ac); va_start(ap,fmt); vfprintf(stderr,fmt,ap); va_end(ap); fprintf(stderr,"\n"); exit(0); } static void warning(const char *, ...) __attribute__ ((format(printf,1,2))); static void warning(const char *fmt, ...) { va_list ap; fprintf(stderr,"%s: warning at argument %d: ",__progname,parse_ac0+1-parse_ac); va_start(ap,fmt); vfprintf(stderr,fmt,ap); va_end(ap); fprintf(stderr,"\n"); } static int twocmp(CMP c) { switch (c) { case C_GTLT: case C_GTLE: case C_GELT: case C_GELE: return(1); break; default: break; } return(0); } static void cmparg(CMP *vp) { if (NEXTARGIS("lt")) { *vp = C_LT; return; } if (NEXTARGIS("le")) { *vp = C_LE; return; } if (NEXTARGIS("gt")) { *vp = C_GT; return; } if (NEXTARGIS("ge")) { *vp = C_GE; return; } if (NEXTARGIS("ne")) { *vp = C_NE; return; } if (NEXTARGIS("eq")) { *vp = C_EQ; return; } if (NEXTARGIS("gtlt")) { *vp = C_GTLT; return; } if (NEXTARGIS("gtle")) { *vp = C_GTLE; return; } if (NEXTARGIS("gelt")) { *vp = C_GELT; return; } if (NEXTARGIS("gele")) { *vp = C_GELE; return; } syntaxerror("invalid comparison `%s'",*parse_av); } static void inc_date(int *yp, int *mp, int *dp, int yinc, int minc, int dinc, int neg) { #define y (*yp) #define m (*mp) #define d (*dp) y += neg ? -yinc : yinc; m += neg ? -minc : minc; m --; if (m < 0) { minc = ((-m) / 12) + 1; y -= minc; m += 12 * minc; } if (m >= 12) { y += m / 12; m %= 12; } m ++; if (neg) dinc = -dinc; dinc += d - 1; d = 1; while (dinc < 0) { dinc += yeardays(y-(m<3)); y --; } while (dinc > 366) { dinc -= yeardays(y+(m>2)); y ++; } while (dinc >= monthdays(m,y)) { dinc -= monthdays(m,y); m ++; if (m > 12) { m = 1; y ++; } } d += dinc; #undef y #undef m #undef d } static void datearg(DATE *dp, DATE *defdate) { char digbuf[9]; int n; int y; int m; int d; char *cp; cp = NEXTARG(); y = curdate.year; m = curdate.month; d = curdate.day; if (debugging) { printf("# datearg entry: %04d %02d %02d, string \"%s\"\n",y,m,d,cp); } if (defdate && (cp[0] == '@')) { cp ++; d = defdate->day; m = defdate->month; y = defdate->year; if (debugging) { printf("# datearg using default: %04d %02d %02d\n",y,m,d); } } if (!bcmp(cp,"now",3)) { cp += 3; } else if (!bcmp(cp,"today",5)) { cp += 5; } else { sprintf(&digbuf[0],"%4d%02d%02d",y,m,d); n = 0; while (1) { switch (*cp) { case '0' ... '9': if (n > 7) syntaxerror("too many digits in date"); if (n > 0) bcopy(&digbuf[8-n],&digbuf[7-n],n); digbuf[7] = *cp; n ++; break; case ' ': case '\t': break; default: goto breakwhile1; break; } cp ++; } breakwhile1:; switch (n) { case 1: digbuf[6] = '0'; break; case 3: digbuf[4] = '0'; break; } d = atoi(&digbuf[6]); digbuf[6] = '\0'; m = atoi(&digbuf[4]); digbuf[4] = '\0'; y = atoi(&digbuf[0]); if ((y < 0) || (y > MAXYEAR)) syntaxerror("year out of range in date spec"); if ((m < 1) || (m > 12)) syntaxerror("month number out of range in date spec"); if ((d < 1) || (d > monthdays(m,y))) syntaxerror("day number out of range for month/year in date spec"); } while ((*cp == '-') || (*cp == '+')) { int neg; int hadu; int ndig; if (debugging) { printf("# datearg outer: %04d %02d %02d, string left \"%s\"\n",y,m,d,cp); } neg = (*cp == '-'); cp ++; hadu = 0; while (1) { n = 0; ndig = 0; if (debugging) { printf("# datearg inner: %04d %02d %02d, string left \"%s\"\n",y,m,d,cp); } while ((*cp >= '0') && (*cp <= '9')) { n = (n * 10) + (*cp - '0'); cp ++; ndig ++; } if (ndig == 0) break; switch (*cp) { case 'y': hadu = 1; cp ++; inc_date(&y,&m,&d,n,0,0,neg); continue; break; case 'm': hadu = 1; cp ++; inc_date(&y,&m,&d,0,n,0,neg); continue; break; case 'd': hadu = 1; cp ++; inc_date(&y,&m,&d,0,0,n,neg); continue; break; } if (hadu) warning("missing unit in date increment - days assumed"); inc_date(&y,&m,&d,0,0,n,neg); break; } } if (debugging) { printf("# datearg final: %04d %02d %02d, string left \"%s\"\n",y,m,d,cp); } if (*cp) warning("junk after date spec ignored"); if ((y < 0) || (y > MAXYEAR)) syntaxerror("increment puts year out of range"); dp->year = y; dp->month = m; dp->day = d; } static void amountarg(long int *ap) { int flags; #define F_POS 1 #define F_NEG 2 char *cp; long int v; int ndig; cp = NEXTARG(); flags = 0; while (1) { switch (*cp) { case '+': if (flags & F_POS) syntaxerror("multiple + signs in amount"); if (flags & F_NEG) syntaxerror("conflicting signs in amount"); flags |= F_POS; break; case '-': if (flags & F_NEG) syntaxerror("multiple - signs in amount"); if (flags & F_POS) syntaxerror("conflicting signs in amount"); flags |= F_NEG; break; default: goto breakwhile1; break; } cp ++; } breakwhile1:; v = 0; ndig = 0; while (1) { switch (*cp) { case '0' ... '9': v = (v * 10) + (*cp - '0'); ndig ++; break; default: warning("junk after amount ignored"); /* fall through */ case '\0': *ap = (flags & F_NEG) ? -v : v; return; break; } cp ++; } } static NODE *parse_top(void); /* forward */ static NODE *parse_unary(void) { NODE *n; PRIM *p; if (NEXTARGIS("-not")) { EATARG(); n = malloc(sizeof(NODE)); n->type = NT_NOT; n->u.unary.arg = parse_unary(); return(n); } if (NEXTARGIS("(")) { EATARG(); n = parse_top(); if (NEXTARGIS(")")) { EATARG(); return(n); } else { syntaxerror("unmatched ("); } } n = malloc(sizeof(NODE)); n->type = NT_PRIM; p = malloc(sizeof(PRIM)); n->u.prim = p; if (NEXTARGIS("-date") || NEXTARGIS("-edate")) { if (! HAVENARGS(3)) syntaxerror("missing argument(s) after %s",NEXTARG()); p->type = NEXTARGIS("-date") ? PT_DATE : PT_EDATE; EATARG(); if (NEXTARGIS("beq")) p->u.date.cmp = C_BEQ; else if (NEXTARGIS("bne")) p->u.date.cmp = C_BNE; else cmparg(&p->u.date.cmp); EATARG(); datearg(&p->u.date.date,0); EATARG(); if (twocmp(p->u.date.cmp)) { if (! HAVENARGS(1)) syntaxerror("missing argument after %s",(p->type==PT_DATE)?"-date":"-edate"); datearg(&p->u.date.date2,&p->u.date.date); EATARG(); } return(n); } if (NEXTARGIS("-flag")) { if (! HAVENARGS(2)) syntaxerror("missing argument after %s",NEXTARG()); p->type = PT_FLAG; EATARG(); p->u.s.s = NEXTARG(); p->u.s.l = strlen(p->u.s.s); switch (p->u.s.l) { case 1: break; case 2: if (p->u.s.s[0] != '-') { default: syntaxerror("invalid flag spec"); } break; } EATARG(); return(n); } if ( NEXTARGIS("-cat") || NEXTARGIS("-from") || NEXTARGIS("-to") || NEXTARGIS("-account") || NEXTARGIS("-currency") ) { if (! HAVENARGS(2)) syntaxerror("missing argument after %s",NEXTARG()); switch (NEXTARG()[1]) { case 'c': switch (NEXTARG()[2]) { case 'a': p->type = PT_CATEGORY; break; case 'u': p->type = PT_CURRENCY; break; } break; case 'a': p->type = PT_ACCOUNT; break; case 'f': p->type = PT_FROM; break; case 't': p->type = PT_TO; break; } EATARG(); p->u.s.s = NEXTARG(); p->u.s.l = strlen(p->u.s.s); EATARG(); return(n); } if (NEXTARGIS("-amount")) { if (! HAVENARGS(3)) syntaxerror("missing argument(s) after %s",NEXTARG()); p->type = PT_AMOUNT; EATARG(); cmparg(&p->u.amount.cmp); EATARG(); amountarg(&p->u.amount.amt); EATARG(); if (twocmp(p->u.amount.cmp)) { if (! HAVENARGS(1)) syntaxerror("missing argument(s) after -amount"); amountarg(&p->u.amount.amt2); EATARG(); } return(n); } if (NEXTARGIS("-absamount")) { if (! HAVENARGS(3)) syntaxerror("missing argument(s) after %s",NEXTARG()); p->type = PT_ABSAMOUNT; EATARG(); cmparg(&p->u.amount.cmp); EATARG(); amountarg(&p->u.amount.amt); if (p->u.amount.amt < 0) syntaxerror("negative amount for -absamount"); EATARG(); if (twocmp(p->u.amount.cmp)) { if (! HAVENARGS(1)) syntaxerror("missing argument(s) after -absamount"); amountarg(&p->u.amount.amt2); if (p->u.amount.amt2 < 0) syntaxerror("negative amount for -absamount"); EATARG(); } return(n); } syntaxerror("missing/invalid primitive"); } static NODE *parse_and(void) { NODE *lhs; NODE *rhs; NODE *n; lhs = parse_unary(); while (NEXTARGIS("-and")) { EATARG(); rhs = parse_unary(); n = malloc(sizeof(NODE)); n->type = NT_AND; n->u.binary.lhs = lhs; n->u.binary.rhs = rhs; lhs = n; } return(lhs); } static NODE *parse_or(void) { NODE *lhs; NODE *rhs; NODE *n; lhs = parse_and(); while (NEXTARGIS("-or")) { EATARG(); rhs = parse_and(); n = malloc(sizeof(NODE)); n->type = NT_OR; n->u.binary.lhs = lhs; n->u.binary.rhs = rhs; lhs = n; } return(lhs); } static NODE *parse_top(void) { return(parse_or()); } static void printcmp(CMP c, void *a1, void *a2, void (*print)(void *)) { const char *s1; const char *s2; s2 = 0; switch (c) { case C_LT: s1 = "<"; break; case C_LE: s1 = "<="; break; case C_GT: s1 = ">"; break; case C_GE: s1 = ">="; break; case C_NE: s1 = "!="; break; case C_EQ: s1 = "=="; break; case C_GTLT: s1 = ">"; s2 = "<"; break; case C_GTLE: s1 = ">"; s2 = "<="; break; case C_GELT: s1 = ">="; s2 = "<"; break; case C_GELE: s1 = ">="; s2 = "<="; break; case C_BEQ: s1 = "(==)"; break; case C_BNE: s1 = "(!=)"; break; default: printf("?cmp(%d)",(int)c); return; break; } printf("%s",s1); if (print) (*print)(a1); if (s2) { printf(",%s",s2); if (print) (*print)(a2); } } static void printdate(void *dvp) { printf("%4d%02d%02d",((DATE *)dvp)->year,((DATE *)dvp)->month,((DATE *)dvp)->day); } static void printamount(void *amp) { printf("%ld",*(long int *)amp); } static void print_expr(NODE *n) { switch (n->type) { case NT_PRIM: switch (n->u.prim->type) { case PT_DATE: printf("[date"); if (0) { case PT_EDATE: printf("[edate"); } printcmp(n->u.prim->u.date.cmp,&n->u.prim->u.date.date,&n->u.prim->u.date.date2,printdate); printf("]"); break; case PT_FLAG: printf("[flag:"); if (0) { case PT_CATEGORY: printf("[category:"); } if (0) { case PT_FROM: printf("[from:"); } if (0) { case PT_TO: printf("[to:"); } if (0) { case PT_ACCOUNT: printf("[account:"); } if (0) { case PT_CURRENCY: printf("[currency:"); } fwrite(n->u.prim->u.s.s,1,n->u.prim->u.s.l,stdout); printf("]"); break; case PT_AMOUNT: printf("[amount"); printcmp(n->u.prim->u.amount.cmp,&n->u.prim->u.amount.amt,&n->u.prim->u.amount.amt2,printamount); printf("]"); break; case PT_ABSAMOUNT: printf("[absamount"); printcmp(n->u.prim->u.amount.cmp,&n->u.prim->u.amount.amt,&n->u.prim->u.amount.amt2,printamount); printf("]"); break; default: printf("?prim(%d)",(int)n->u.prim->type); break; } break; case NT_NOT: putchar('!'); print_expr(n->u.unary.arg); break; case NT_AND: putchar('('); print_expr(n->u.binary.lhs); putchar('&'); print_expr(n->u.binary.rhs); putchar(')'); break; case NT_OR: putchar('('); print_expr(n->u.binary.lhs); putchar('|'); print_expr(n->u.binary.rhs); putchar(')'); break; default: printf("?node(%d)",(int)n->type); break; } } static int trans_date(const char *bp, DATE *dp) { int i; unsigned int y; unsigned int m; unsigned int d; d = 0; for (i=0;i<8;i++) { if ((bp[i] < '0') || (bp[i] > '9')) return(0); d = (10 * d) + (bp[i] - '0'); } y = d / 10000; m = (d / 100) % 100; d %= 100; if (y > MAXYEAR) return(0); if ((m < 1) || (m > 12)) return(0); if ((d < 1) || (d > monthdays(m,y))) return(0); dp->year = y; dp->month = m; dp->day = d; return(1); } static int parse_trans(void) { int i; int l; char *c0; char *c1; char *c2; char *c3; int neg; l = line.l; if (l < 22) return(0); if ((line.s[8] != ':') || (line.s[17] != ':')) return(0); if (!trans_date(line.s,&date) || !trans_date(line.s+9,&edate)) return(0); l -= 18; c0 = bindex(line.s+18,l,':'); if (c0 == 0) return(0); l -= c0 - (line.s+18); c1 = bindex(c0+1,l-1,':'); if (c1 == 0) return(0); l -= c1 - c0; c2 = bindex(c1+1,l-1,':'); if (c2 == 0) return(0); l -= c2 - c1; c3 = bindex(c2+1,l-1,':'); if (c3 == 0) return(0); l -= c3 - c2; if (l < 2) return(0); if (c3+l != line.s+line.l) abort(); flags.s = line.s + 18; flags.l = c0 - flags.s; category.s = c0 + 1; category.l = c1 - category.s; from.s = c1 + 1; from.l = c2 - from.s; to.s = c2 + 1; to.l = c3 - to.s; amount = 0; neg = 0; i = 1; if (c3[i] == '-') { neg = 1; i ++; } for (;i '9')) return(0); amount = (amount * 10) + (c3[i] - '0'); } if (neg) amount = - amount; if (i < l) { currency.s = c3 + i + 1; currency.l = l - (i + 1); } else { currency.s = 0; currency.l = 0; } return(1); } static int read_trans(void) { int c; char *buf; int have; int len; buf = 0; have = 0; len = 0; while (1) { c = getchar(); if (c == EOF) { if (len > 0) { fprintf(stderr,"%s: warning: unterminated last line ignored\n",__progname); } return(0); } if (c == '\n') { free(line.s); line.s = buf; line.l = len; if (parse_trans()) return(1); fprintf(stderr,"%s: warning: malformed input line ignored: ",__progname); fwrite(line.s,1,line.l,stderr); fprintf(stderr,"\n"); buf = 0; have = 0; len = 0; continue; } while (len >= have) buf = realloc(buf,have+=8); buf[len++] = c; } } static int compare(CMP how, const void *a1, const void *a2, const void *actual, CMP (*cmp)(const void *, const void *)) { switch (how) { case C_LT: return((*cmp)(actual,a1)==C_LT); break; case C_LE: return((*cmp)(actual,a1)!=C_GT); break; case C_GT: return((*cmp)(actual,a1)==C_GT); break; case C_GE: return((*cmp)(actual,a1)!=C_LT); break; case C_NE: return((*cmp)(actual,a1)!=C_EQ); break; case C_EQ: return((*cmp)(actual,a1)==C_EQ); break; case C_GTLT: return( ((*cmp)(actual,a1)==C_GT) && ((*cmp)(actual,a2)==C_LT) ); break; case C_GTLE: return( ((*cmp)(actual,a1)==C_GT) && ((*cmp)(actual,a2)!=C_GT) ); break; case C_GELT: return( ((*cmp)(actual,a1)!=C_LT) && ((*cmp)(actual,a2)==C_LT) ); break; case C_GELE: return( ((*cmp)(actual,a1)!=C_LT) && ((*cmp)(actual,a2)!=C_GT) ); break; { int eq; case C_BNE: eq = 0; if (0) { case C_BEQ: eq = 1; } if ((*cmp)(actual,a1) == C_EQ) return(eq); if (((const DATE *)a1)->day <= 12) { DATE dtmp; dtmp = *(const DATE *)a1; dtmp.day = ((const DATE *)a1)->month; dtmp.month = ((const DATE *)a1)->day; if ((*cmp)(actual,&dtmp) == C_EQ) return(eq); } return(!eq); } } abort(); } static CMP cmpdate(const void *dvp1, const void *dvp2) { #define d1 ((const DATE *)dvp1) #define d2 ((const DATE *)dvp2) if (d1->year < d2->year) return(C_LT); else if (d1->year > d2->year) return(C_GT); else if (d1->month < d2->month) return(C_LT); else if (d1->month > d2->month) return(C_GT); else if (d1->day < d2->day) return(C_LT); else if (d1->day > d2->day) return(C_GT); else return(C_EQ); #undef d1 #undef d2 } static CMP cmpamount(const void *ap1, const void *ap2) { #define a1 (*(const long int *)ap1) #define a2 (*(const long int *)ap2) if (a1 < a2) return(C_LT); if (a1 > a2) return(C_GT); return(C_EQ); #undef a1 #undef a2 } static int eval_expr(NODE *n) { DATE *datep; STR *strp; int val; static int depth = 0; if (debugging) { printf("# %*s> ",2*depth,""); print_expr(n); printf("\n"); } depth ++; switch (n->type) { case NT_PRIM: switch (n->u.prim->type) { case PT_DATE: datep = &date; if (0) { case PT_EDATE: datep = &edate; } val = compare(n->u.prim->u.date.cmp,&n->u.prim->u.date.date,&n->u.prim->u.date.date2,datep,cmpdate); break; case PT_FLAG: if (n->u.prim->u.s.l == 2) { val = !bindex(flags.s,flags.l,n->u.prim->u.s.s[1]); } else { val = !!bindex(flags.s,flags.l,n->u.prim->u.s.s[0]); } break; case PT_CATEGORY: val = (category.l >= n->u.prim->u.s.l) && !bcmp(category.s,n->u.prim->u.s.s,n->u.prim->u.s.l); break; case PT_FROM: strp = &from; if (0) { case PT_TO: strp = &to; } val = (strp->l == n->u.prim->u.s.l) && !bcmp(strp->s,n->u.prim->u.s.s,strp->l); break; case PT_ACCOUNT: val = ( ( (to.l == n->u.prim->u.s.l) && !bcmp(to.s,n->u.prim->u.s.s,to.l) ) ) || ( (from.l == n->u.prim->u.s.l) && !bcmp(from.s,n->u.prim->u.s.s,from.l) ); break; case PT_AMOUNT: val = compare(n->u.prim->u.amount.cmp,&n->u.prim->u.amount.amt,&n->u.prim->u.amount.amt2,&amount,cmpamount); break; case PT_ABSAMOUNT: { long int absamt; absamt = (amount < 0) ? -amount : amount; val = compare(n->u.prim->u.amount.cmp,&n->u.prim->u.amount.amt,&n->u.prim->u.amount.amt2,&absamt,cmpamount); } break; case PT_CURRENCY: val = (currency.l == n->u.prim->u.s.l) && !bcmp(currency.s,n->u.prim->u.s.s,currency.l); break; default: abort(); break; } break; case NT_NOT: val = !eval_expr(n->u.unary.arg); break; case NT_AND: val = eval_expr(n->u.binary.lhs) && eval_expr(n->u.binary.rhs); break; case NT_OR: val = eval_expr(n->u.binary.lhs) || eval_expr(n->u.binary.rhs); break; default: abort(); break; } depth --; if (debugging) { printf("# %*s< %d\n",2*depth,"",val); } return(val); } static void write_trans(FILE *f) { fwrite(line.s,1,line.l,f); putc('\n',f); } int main(int, char **); int main(int ac, char **av) { getcurtime(); if ((ac > 2) && !strcmp(av[1],"-debug")) { debugging = 1; ac --; av ++; } if ((ac > 3) && !strcmp(av[1],"-split")) { failfile = fdopen(atoi(av[2]),"w"); ac -= 2; av += 2; } parse_ac = ac - 1; parse_av = av + 1; parse_ac0 = parse_ac; expr = parse_top(); if (debugging) { printf("Expression: "); print_expr(expr); printf("\n"); } while (read_trans()) { if (debugging) { printf("# ["); fwrite(line.s,1,line.l,stdout); printf("]\n"); } if (eval_expr(expr)) { write_trans(stdout); } else if (failfile) { write_trans(failfile); } } exit(0); }