sw

simple wallet
git clone git://git.bitsmanent.org/sw
Log | Files | Refs | README | LICENSE

commit 8af46f6e144feb33016b6c193fa9bdf3885122a5
parent a67acaa170fa39cbcb898a223b82f787be80d2d6
Author: Claudio Alessi <smoppy@gmail.com>
Date:   Sun, 30 Jun 2024 12:04:20 +0200

Proper filters implementation. Allowed multiple -e flags.

Diffstat:
Msw.1 | 5+++--
Msw.c | 113++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
2 files changed, 101 insertions(+), 17 deletions(-)

diff --git a/sw.1 b/sw.1 @@ -3,7 +3,7 @@ sw \- simple wallet .SH SYNOPSIS .B sw -.RB [ \-v ]\ [ \-defit \ <arg>]\ [ \-l \ [limit]\ ]\ [<date\ [time]\>\ <amount>\ <note>] +.RB [ \-v ]\ [ \-dfit \ <arg>]\ [ \-e \ <text>\ ...]\ [ \-l \ [limit]]\ [<date\ [time]\>\ <amount>\ <note>] .SH DESCRIPTION sw is a simple wallet management tool which features a simple yet powerful interface to keep track of your money movements. @@ -16,7 +16,8 @@ prints version information to standard output, then exits. remove the specified movement, then exits. .TP .B \-e\ <text> -consider only movements that include the given string (case insensitive) +consider only movements that include the given string (case insensitive). May +be specified multiple times. .TP .B \-f\ <date\ [time]> consider only movements after the given date and optional time diff --git a/sw.c b/sw.c @@ -13,6 +13,15 @@ #include "arg.h" char *argv0; +enum {F_DATEFROM, F_DATETO, F_TEXT}; + +typedef union { + int i; + unsigned int ui; + float f; + const void *v; +} Arg; + typedef struct Movement Movement; struct Movement { int id; @@ -30,16 +39,28 @@ typedef struct { int count, pcount; } Totals; +typedef struct Filter Filter; +struct Filter { + int type; + Arg arg; + Filter *next; +}; + /* function declarations */ int addmov(char *date, float amount, char *note); +void addfilter(unsigned int type, void *data); void attach(Movement *m); +void attachfltr(Filter *f); void attach_sorted_desc(Movement **head, Movement *m); void deletemov(int id); void detach(Movement *m); +void detachfltr(Filter *f); void die(const char *errstr, ...); void *ecalloc(size_t nmemb, size_t size); +int filtermov(Movement *mov); int filtermovs(int from, int to, char *txt); void freemovs(void); +void freefltrs(void); void loadmovs(void); void refresh(void); void savemovs(void); @@ -50,11 +71,13 @@ void usage(void); /* variables */ Movement *movs; +Filter *filters; Totals totals; FILE *movsfile; char movsfilename[256]; int limit = INT_MAX; int filtered = 0; +int nfilters = 0; /* function implementations */ int @@ -77,12 +100,33 @@ addmov(char *date, float amount, char *note) { } void +addfilter(unsigned int type, void *data) { + Filter *f = ecalloc(1, sizeof(Filter)); + + switch(type) { + case F_TEXT: f->arg.v = data; break; + case F_DATEFROM: f->arg.i = strtots(data); break; + case F_DATETO: f->arg.i = strtots(data); break; + default: die("invalid filter type\n", type); + } + + f->type = type; + attachfltr(f); +} + +void attach(Movement *m) { m->next = movs; movs = m; } void +attachfltr(Filter *f) { + f->next = filters; + filters = f; +} + +void attach_sorted_desc(Movement **head, Movement *m) { Movement *t; @@ -120,6 +164,14 @@ detach(Movement *m) { } void +detachfltr(Filter *f) { + Filter **tf; + + for (tf = &filters; *tf && *tf != f; tf = &(*tf)->next); + *tf = f->next; +} + +void die(const char *errstr, ...) { va_list ap; @@ -139,21 +191,40 @@ ecalloc(size_t nmemb, size_t size) { } int +filtermov(Movement *m) { + Filter *f; + int ormatch = -1; + + for(f = filters; f; f = f->next) { + switch(f->type) { + case F_TEXT: + if(ormatch > 0) + break; + ormatch = !!strcasestr(m->note, (char *)f->arg.v); + break; + case F_DATEFROM: + if(!(m->ts >= f->arg.i)) + return 1; + break; + case F_DATETO: + if(!(m->ts <= f->arg.i)) + return 1; + break; + } + } + return ormatch == -1 ? 0 : !ormatch; +} + +int filtermovs(int from, int to, char *txt) { Movement *m; - int nf = 0; + int n = 0; - if(!(from || to || txt)) - return 0; for(m = movs; m; m = m->next) { - m->filtered = (txt ? !!strcasestr(m->note, txt) : 1) - && (from ? m->ts >= from : 1) - && (to ? m->ts <= to : 1) - ? 0 : 1; - if(m->filtered) - ++nf; + m->filtered = filtermov(m); + n += m->filtered; } - return nf; + return n; } void @@ -168,6 +239,17 @@ freemovs(void) { } void +freefltrs(void) { + Filter *f; + + while(filters) { + f = filters; + detachfltr(f); + free(f); + } +} + +void loadmovs(void) { Movement *m; int r; @@ -276,7 +358,7 @@ strtots(char *s) { void usage(void) { - die("Usage: %s [-v] [-defit <arg>] [-l [limit]] [<date [time]> <amount> <note>]\n", argv0); + die("Usage: %s [-v] [-dfit <arg>] [-e <text> ...] [-l [limit]] [<date [time]> <amount> <note>]\n", argv0); } int @@ -288,15 +370,14 @@ main(int argc, char *argv[]) { ARGBEGIN { case 'd': delid = atoi(EARGF(usage())); break; - case 'e': txt = EARGF(usage()); break; - case 'f': from = strtots(EARGF(usage())); break; + case 'e': addfilter(F_TEXT, EARGF(usage())); break; + case 'f': addfilter(F_DATEFROM, EARGF(usage())); break; case 'i': snprintf(movsfilename, sizeof movsfilename, "%s", EARGF(usage())); break; case 'l': - //limit = atoi(EARGF(usage())); num = ARGF(); limit = num ? atoi(num) : 25; break; - case 't': to = strtots(EARGF(usage())); break; + case 't': addfilter(F_DATETO, EARGF(usage())); break; case 'v': die("sw-"VERSION"\n"); default: usage(); } ARGEND; @@ -313,6 +394,7 @@ main(int argc, char *argv[]) { deletemov(delid); savemovs(); freemovs(); + freefltrs(); /* Only for coherence. We should check incompatible flags anyway */ return 0; } if(argc) { @@ -326,5 +408,6 @@ main(int argc, char *argv[]) { refresh(); showmovs(); freemovs(); + freefltrs(); return 0; }