#include #include #include #include #include #include "deconst.h" #include "conflib.h" #include "currlib.h" extern const char *__progname; 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; } ; static STR line = { 0, 0 }; static DATE date; static DATE edate; static STR flags; static STR category; static STR from; static STR to; static STR amount; static char *currency; static long int amt; static const char *def_curr; static void *conf; 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 void handleargs(int ac, char **av, int early) { int skip; int errs; skip = 0; errs = 0; for (ac--,av++;ac;ac--,av++) { if (skip > 0) { skip --; continue; } if (**av != '-') { if (! early) conf_read(conf,*av,0); continue; } if (0) { needarg:; fprintf(stderr,"%s: %s needs a following argument\n",__progname,*av); errs ++; continue; } #define WANTARG() do { if (++skip >= ac) goto needarg; } while (0) if (!strcmp(*av,"-config")) { WANTARG(); if (! early) conf_read(conf,av[skip],0); continue; } if (!strcmp(*av,"-configline") || !strcmp(*av,"-set")) { WANTARG(); if (early) conf_line(conf,av[skip],0); continue; } #undef WANTARG fprintf(stderr,"%s: unrecognized option `%s'\n",__progname,*av); errs ++; } if (errs) { fprintf(stderr,"Usage: %s [options] [config-file]\n",__progname); exit(1); } } static inline unsigned int const 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 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 cvtamt(STR *s, long int *amt) { int i; int neg; long int a; neg = 0; i = 0; if (s->s[i] == '-') { neg = 1; i ++; } a = 0; for (;il;i++) { if ((s->s[i] < '0') || (s->s[i] > '9')) return(1); a = (a * 10) + (s->s[i] - '0'); } if (neg) a = - a; *amt = a; return(0); } static int parse_trans(void) { int l; char *c0; char *c1; char *c2; char *c3; char *c4; 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); flags.s = line.s + 18; flags.l = c0 - flags.s; c1 = bindex(c0+1,l-1,':'); if (c1 == 0) return(0); l -= c1 - c0; category.s = c0 + 1; category.l = c1 - category.s; c2 = bindex(c1+1,l-1,':'); if (c2 == 0) return(0); l -= c2 - c1; from.s = c1 + 1; from.l = c2 - from.s; c3 = bindex(c2+1,l-1,':'); if (c3 == 0) return(0); l -= c3 - c2; to.s = c2 + 1; to.l = c3 - to.s; c4 = bindex(c3+1,l-1,':'); if (currency) free(currency); if (c4 == 0) { amount.s = c3 + 1; amount.l = l - 1; currency = 0; c4 = c3; } else { l -= c4 - c3; amount.s = c3 + 1; amount.l = c4 - amount.s; currency = malloc(l); bcopy(c4+1,currency,l-1); currency[l-1] = '\0'; } if (l < 2) return(0); if (c4+l != line.s+line.l) abort(); if (cvtamt(&amount,&amt)) return(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 void format_trans(void) { static char *amtbuf = 0; static int amthave = 0; int fmtsize; if (!currency && !def_curr) { fprintf(stderr,"%s: no default currency configured and no currency in entry\n",__progname); return; } printf("%04d %02d %02d: ",date.year,date.month,date.day); if (flags.l) { putchar('['); fwrite(flags.s,1,flags.l,stdout); printf("] "); } fwrite(category.s,1,category.l,stdout); printf(": "); if (from.l) fwrite(from.s,1,from.l,stdout); else printf("(world)"); fmtsize = format_amount(0,amt<0,(amt<0)?-amt:amt,currency?currency:def_curr); if (fmtsize > amthave) { free(amtbuf); amthave = fmtsize + 1; amtbuf = malloc(amthave); } format_amount(amtbuf,amt<0,(amt<0)?-amt:amt,currency?currency:def_curr); printf(" -> %s -> ",amtbuf); if (to.l) fwrite(to.s,1,to.l,stdout); else printf("(world)"); printf("\n"); } static void init_curr(void) { const char *curr_file; curr_file = conf_path(conf,"currencies",0); if (curr_file) { load_currencies(curr_file,0); } else { fake_currencies(); } def_curr = conf_value(conf,"defcurrency",0); if (def_curr && !find_currency(def_curr)) { fprintf(stderr,"%s: default currency of %s isn't a known currency!\n",__progname,def_curr); exit(1); } } int main(int, char **); int main(int ac, char **av) { conf = conf_newstate(); handleargs(ac,av,1); handleargs(ac,av,0); conf_check_env(conf,"MDB_CONFIG_DB",0); init_curr(); while (read_trans()) format_trans(); exit(0); }