sw

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

commit 4efcacb0b566d74ba6f5c64064e67aa24968e36f
Author: Claudio Alessi <smoppy@gmail.com>
Date:   Fri,  9 Mar 2018 21:35:11 +0100

init

Diffstat:
AMakefile | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AREADME.md | 2++
Aarg.h | 41+++++++++++++++++++++++++++++++++++++++++
Aconfig.mk | 25+++++++++++++++++++++++++
Asw.1 | 0
Asw.c | 251+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 376 insertions(+), 0 deletions(-)

diff --git a/Makefile b/Makefile @@ -0,0 +1,57 @@ +# sw - simple wallet +# See LICENSE file for copyright and license details. + +include config.mk + +APPNAME=sw +SRC = ${APPNAME}.c +OBJ = ${SRC:.c=.o} + +all: options ${APPNAME} + +options: + @echo ${APPNAME} build options: + @echo "CFLAGS = ${CFLAGS}" + @echo "LDFLAGS = ${LDFLAGS}" + @echo "CC = ${CC}" + +.c.o: + @echo CC $< + @${CC} -c ${CFLAGS} $< + +${OBJ}: config.mk + +${APPNAME}: ${OBJ} + @echo CC -o $@ + @${CC} -o $@ ${OBJ} ${LDFLAGS} + +clean: + @echo cleaning + @rm -f ${APPNAME} ${OBJ} ${APPNAME}-${VERSION}.tar.gz + +dist: clean + @echo creating dist tarball + @mkdir -p ${APPNAME}-${VERSION} + @cp -R LICENSE Makefile README config.mk \ + ${APPNAME}.1 ${SRC} ${APPNAME}-${VERSION} + @tar -cf ${APPNAME}-${VERSION}.tar ${APPNAME}-${VERSION} + @gzip ${APPNAME}-${VERSION}.tar + @rm -rf ${APPNAME}-${VERSION} + +install: all + @echo installing executable file to ${DESTDIR}${PREFIX}/bin + @mkdir -p ${DESTDIR}${PREFIX}/bin + @cp -f ${APPNAME} ${DESTDIR}${PREFIX}/bin + @chmod 755 ${DESTDIR}${PREFIX}/bin/${APPNAME} + @echo installing manual page to ${DESTDIR}${MANPREFIX}/man1 + @mkdir -p ${DESTDIR}${MANPREFIX}/man1 + @sed "s/VERSION/${VERSION}/g" < ${APPNAME}.1 > ${DESTDIR}${MANPREFIX}/man1/${APPNAME}.1 + @chmod 644 ${DESTDIR}${MANPREFIX}/man1/${APPNAME}.1 + +uninstall: + @echo removing executable file from ${DESTDIR}${PREFIX}/bin + @rm -f ${DESTDIR}${PREFIX}/bin/${APPNAME} + @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1 + @rm -f ${DESTDIR}${MANPREFIX}/man1/${APPNAME}.1 + +.PHONY: all options clean dist install uninstall diff --git a/README.md b/README.md @@ -0,0 +1,2 @@ +simple wallet +============= diff --git a/arg.h b/arg.h @@ -0,0 +1,41 @@ +/* + * Copy me if you can. + * by 20h + */ + +#ifndef __ARG_H__ +#define __ARG_H__ + +extern char *argv0; + +#define USED(x) ((void)(x)) + +#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\ + argv[0] && argv[0][1]\ + && argv[0][0] == '-';\ + argc--, argv++) {\ + char _argc;\ + char **_argv;\ + if (argv[0][1] == '-' && argv[0][2] == '\0') {\ + argv++;\ + argc--;\ + break;\ + }\ + for (argv[0]++, _argv = argv; argv[0][0];\ + argv[0]++) {\ + if (_argv != argv)\ + break;\ + _argc = argv[0][0];\ + switch (_argc) + +#define ARGEND }\ + USED(_argc);\ + }\ + USED(argv);\ + USED(argc); + +#define EARGF(x) ((argv[1] == NULL)? ((x), abort(), (char *)0) :\ + (argc--, argv++, argv[0])) + +#endif + diff --git a/config.mk b/config.mk @@ -0,0 +1,25 @@ +# sw - simple wallet +VERSION = 0.1 + +# Customize below to fit your system + +# paths +PREFIX = /usr/local +MANPREFIX = ${PREFIX}/share/man + +# includes and libs +INCS = +LIBS = + +# flags +CPPFLAGS = -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=2 -DVERSION=\"${VERSION}\" +#CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS} +CFLAGS = -std=c99 -pedantic -Wall -Wno-deprecated-declarations -Os ${INCS} ${CPPFLAGS} +LDFLAGS = -s ${LIBS} + +# Solaris +#CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\" +#LDFLAGS = ${LIBS} + +# compiler and linker +CC = cc diff --git a/sw.1 b/sw.1 diff --git a/sw.c b/sw.c @@ -0,0 +1,251 @@ +/* simple wallet */ + +#define _XOPEN_SOURCE +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include "arg.h" +char *argv0; + +typedef struct Movement Movement; +struct Movement { + int id; + int ts; + float amount; + char note[64]; + Movement *next; +}; + +/* function declarations */ +int addmov(char *date, float amount, char *note); +void attach(Movement *m); +void attach_sorted_desc(Movement **head, Movement *m); +void deletemov(int id); +void detach(Movement *m); +void die(const char *errstr, ...); +void *ecalloc(size_t nmemb, size_t size); +void freemovs(void); +void loadmovs(void); +void savemovs(void); +void showmovs(void); +void sortmovs(void); +int strfts(char *s); +void usage(void); + +/* variables */ +Movement *movs; +FILE *movsfile; +char movsfilename[256]; + +/* function implementations */ +int +addmov(char *date, float amount, char *note) { + Movement *m; + int id = 0; + + for(m = movs; m; m = m->next) + if(m->id > id) + id = m->id; + ++id; + m = ecalloc(1, sizeof(Movement)); + m->id = id; + m->ts = strfts(date); + m->amount = amount; + memcpy(m->note, note, sizeof(m->note)); + m->note[ sizeof(m->note) - 1] = '\0'; + attach(m); + return 0; +} + +void +attach(Movement *m) { + m->next = movs; + movs = m; +} + +void +attach_sorted_desc(Movement **head, Movement *m) { + Movement *t; + + if(!*head || m->ts > (*head)->ts) { + m->next = *head; + *head = m; + return; + } + t = *head; + while(t->next && m->ts <= t->next->ts) + t = t->next; + m->next = t->next; + t->next = m; +} + +void +deletemov(int id) { + Movement *m; + + for(m = movs; m; m = m->next) + if(m->id == id) + break; + if(m) { + detach(m); + free(m); + } +} + +void +detach(Movement *m) { + Movement **tm; + + for (tm = &movs; *tm && *tm != m; tm = &(*tm)->next); + *tm = m->next; +} + +void +die(const char *errstr, ...) { + va_list ap; + + va_start(ap, errstr); + vfprintf(stderr, errstr, ap); + va_end(ap); + exit(1); +} + +void * +ecalloc(size_t nmemb, size_t size) { + void *p; + + if(!(p = calloc(nmemb, size))) + die("Cannot allocate memory.\n"); + return p; +} + +void +freemovs(void) { + Movement *m; + + while(movs) { + m = movs; + detach(m); + free(m); + } +} + +void +loadmovs(void) { + Movement *m; + int r; + + rewind(movsfile); + while(1) { + m = ecalloc(1, sizeof(Movement)); + r = fscanf(movsfile, "%d %d %f %[^\n]", &m->id, &m->ts, &m->amount, &m->note[0]); + if(r <= 0) { + if(feof(movsfile)) + break; + die("%s: file corrupted\n", movsfilename); + } + attach(m); + } +} + +void +savemovs(void) { + Movement *m; + + movsfile = fopen(movsfilename, "w"); + for(m = movs; m; m = m->next) + fprintf(movsfile, "%d %d %f %s\n", m->id, m->ts, m->amount, m->note); + fclose(movsfile); +} + +void +showmovs(void) { + Movement *m; + time_t ts; + float tot = 0; + int nmovs = 0; + char time[32]; + + printf("%3s | %16s | %8s | %s\n", "id", "date", "amount", "note"); + for(m = movs; m; m = m->next) { + ts = m->ts; + strftime(time, sizeof time, "%d/%m/%Y %H:%M", localtime(&ts)); + printf("%3d | %16s | %8.2f | %s\n", m->id, time, m->amount, m->note); + tot += m->amount; + ++nmovs; + } + printf("%3s | %17s: %8.2f |\n", "", "Wallet balance", tot); + printf("%3s | %17s: %8d |\n", "", "Total movements", nmovs); +} + +void +sortmovs(void) { + Movement *sorted = NULL, *m, *t; + + m = movs; + while(m) { + t = m; + m = m->next; + attach_sorted_desc(&sorted, t); + } + movs = sorted; +} + +int +strfts(char *s) { + struct tm tm = {0}; + + if(!strcmp(s, "now")) + return time(NULL); + strptime(s, "%d/%m/%Y %H:%M", &tm); + return mktime(&tm); +} + +void +usage(void) { + die("Usage: %s [-v] [-d <id>] [-f <file>] [<date> <amount> <note>]\n", argv0); +} + +int +main(int argc, char *argv[]) { + int delid = 0; + + ARGBEGIN { + case 'd': delid = atoi(EARGF(usage())); break; + case 'f': + snprintf(movsfilename, sizeof movsfilename, "%s", EARGF(usage())); + break; + case 'v': die("sw-"VERSION"\n"); + default: usage(); + } ARGEND; + + if(!*movsfilename) + snprintf(movsfilename, sizeof movsfilename, "%s/%s", getenv("HOME"), ".sw"); + movsfile = fopen(movsfilename, "r"); + if(!movsfile) + die("%s: cannot open the file\n", movsfilename); + loadmovs(); + fclose(movsfile); + + if(delid) { + deletemov(delid); + savemovs(); + freemovs(); + return 0; + } + if(argc) { + if(argc != 3) + usage(); + addmov(argv[0], atof(argv[1]), argv[2]); + savemovs(); + freemovs(); + return 0; + } + sortmovs(); + showmovs(); + freemovs(); + return 0; +}