payredu

[WIP] Cross-platform ledger GUI written in c99
git clone git@nonplanar.org:payredu.git
Log | Files | Refs | README

commit db705b3345e02c595119980a66b6442469892d32
parent 8b5eb06ff3db5f05ca5bebc5a57da3eb298ee719
Author: Bharatvaj Hemanth <bharatvaj@yahoo.com>
Date:   Wed, 26 Mar 2025 07:56:38 +0530

Merge branch 'main' of nonplanar.org:payredu

Diffstat:
M.gitignore | 4++++
MHACKING | 67++++++++++++++++++++++++++++++++++++++++++++++++-------------------
MMakefile | 44+++++++++++++++++++++++++++++++-------------
MREADME | 37+++++++++++++++++++++++--------------
Mbook.c | 15+++++++++++----
Mpayredu.c | 36+++++++++++++++++++++++++++++++++---
Mstrn.h | 2+-
7 files changed, 151 insertions(+), 54 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -3,6 +3,10 @@ hot hotbook *.so *.o +*.obj +*.a +payredu +payredu.exe tags tests/* diff --git a/HACKING b/HACKING @@ -7,38 +7,65 @@ yyyy/mm/dd <till \n> ...\n\n // mark posting on \n\n -payredo can be bulit as a standalone library(libpayredo) or can be built as an executable. +payredu can be bulit as a standalone library(libpayredu) or can be +built as +an executable. - make libpayredo.a - make libpayredo.so - make payredo + make libpayredu.a + make libpayredu.so + make payredu -You can control what you want to build by invoking it separately. By default builds everything. +You can control what you want to build by invoking it separately. By +default +builds everything. -commit.c ------- -The original ledger-cli does not do edits, to make the runtime simple but since the database is completely ASCII and unorganized, consequent and programmatic writes creates a stress on the CPU and RAM, making it unsuitable for building touch friendly GUIs or general clients on top of it without comprosing UX. +Makefile +-------- +This project strives to use the same Makefile to work on both gmake +and bmake. -To prevent this, payredo exposes a commit API which can be used by text editors and other frontends to validate added content before saving it to the file. +If you want to add any ${CC} specific flags, check the part under +"#if ${CC}" +in Makefile. -Once you are done with the changes in the text editor or other GUI, you can commit the data to payredo using the APIs +commit.c +-------- +The original ledger-cli does not do edits, to make the runtime simple +but since +the database is completely ASCII and unorganized, consequent and +programmatic +writes creates a stress on the CPU and RAM, making it unsuitable for +building +touch friendly GUIs or general clients on top of it without comprosing UX. + +To prevent this, payredu exposes a commit API which can be used by +text editors +and other frontends to validate added content before saving it to +the file. + +Once you are done with the changes in the text editor or other GUI, +you can +commit the data to payredu using the APIs int ledger_commit_text(new_text, new_text_len) int ledger_commit_post(timestamp, comment, comment_len, entries**) -The first variant can be used by text editors where the structure of the parsed text is not understood. +The first variant can be used by text editors where the structure of the +parsed text is not understood. -The second variant can be used when frontend is a GUI or other UI where the user input is controlled. +The second variant can be used when frontend is a GUI or other UI +where the +user input is controlled. RETURN VALUE ------------ Both variants return PARSE_OK if suceeded, and -1 on failure. - -payredo.c +payredu.c --------- Similar to ledger-cli -payredo follows the UNIX style option arguments to make the parsing easy and to combine multiple options +payredu follows the UNIX style option arguments to make the parsing easy +and to combine multiple options parse.c @@ -50,9 +77,13 @@ There are two parsers written for the The parser is written entirely by hand to reduce dependencies. -The `state` variable at any given time holds the information 'what we are trying to parse'. If the `state` has the value `DATE`, it means we are in a condition where we expect DATE to occur such as when starting the parser or when a posting is parsed. +The `state` variable at any given time holds the information 'what we are +trying to parse'. If the `state` has the value `DATE`, it means we are in +a condition where we expect DATE to occur such as when starting the parser +or when a posting is parsed. -There aren't as many states as the ledger format itself is quite minimal. These are currently the states, +There aren't as many states as the ledger format itself is quite minimal. +These are currently the states, DATE COMMENT @@ -60,5 +91,3 @@ ENTRY_WHO ENTRY_AMOUNT ENTRY_END - - diff --git a/Makefile b/Makefile @@ -1,18 +1,34 @@ -GENERAL_FLAGS=-fPIC +GENERAL_FLAGS = # -fPIC + GUI_LDFLAGS=-lglfw -lGL -LDFLAGS:=$(GENERAL_FLAGS) -lm -CFLAGS:=$(GENERAL_FLAGS) -O0 -I. -g -Werror #-Wpedantic -export LD_LIBRARY_PATH=. -.DEFAULT_GOAL=payredu +CFLAGS:=${GENERAL_FLAGS} -I. +LDFLAGS:=${GENERAL_FLAGS} -CC=gcc +# if ${CC} +${CC}_CFLAGS := -O0 -g -Wpedantic -Werror -lm +${CC}_out := -o +${CC}_exe_out := ${${CC}_out} +# else cl +cl_CFLAGS := /nologo /WX /W3 /D_CRT_SECURE_NO_WARNINGS +cl_exe_ext := .exe +cl_out := /Fo: +cl_exe_out := /Fe: -payredu: payredu.c libbook.a - $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $> -L. -lbook +# consolidate +CFLAGS += ${${CC}_CFLAGS} +out := ${${CC}_out} -.o: .c - $(CC) $(CFLAGS) -c $> -o $@ +# gmake hack +^ ?= ${.ALLSRC} + +export LD_LIBRARY_PATH=. + +payredu${${CC}_exe_ext}: payredu.c libbook.a + ${CC} ${${CC}_exe_out}$@ ${CFLAGS} ${LDFLAGS} $^ + +.c.o: + ${CC} ${CFLAGS} -c $^ ${out}$@ bal: #ledger -f october-2023.txt bal @@ -22,10 +38,10 @@ balance: balance.c ledger.h hot: hot.c libbook.so libbook.a: book.o account.o - ar cr $@ $> + ar cr $@ $^ libbook.so: book.o account.o - $(CC) -shared -Wl,-soname,$@ -o $@ $> + ${CC} -shared -Wl,-soname,$@ -o $@ $^ refresh: git ls-files | entr sh hot.sh @@ -35,5 +51,7 @@ format: include tests/tests.mk +RM?=rm -f + clean: - -rm *.so *.o hotbook libbook.a libbook.so $(TESTS) + -${RM} payredu payredu.exe *.a *.so *.o *.obj hot hot.exe $(TESTS) diff --git a/README b/README @@ -3,43 +3,52 @@ payredu பேரேடு ====== -payredu is a cross-platform frontend to ledger(pta) with emphasis on simplicity. payredu means ledger in Tamil. It is written in c99 and works on top of nuklear making it lightweight and fast. +payredu is a cross-platform frontend to ledger(pta) with emphasis on +simplicity. payredu means ledger in Tamil. It is written in c99 and +works on top of nuklear making it lightweight and fast. -NOTE: The quality of the software is beta in the least, it's still in development. +NOTE: The quality of the software is beta in the least, it's still +in development. To build and run, $ make - $ ./payredu + $ ./payredu -f ledger.dat Why payredu when ledger-cli exists? ------------------------------------ -ledger-cli by itself is pretty lightweight but it has a handful of dependencies and features which I don't particularly care about. +ledger-cli by itself is pretty lightweight but it has a handful of +dependencies and features which I don't particularly care about. -payredu follows a very suckless approach to ledger and does NOT provide some of the advanced features ledger provides. +payredu follows a very suckless approach to ledger and does NOT provide +some of the advanced features ledger provides. -It should be noted that payredu is usually faster than ledger-cli as it does not provide some of the niceties that ledger-cil provides. +It should be noted that payredu is usually faster than ledger-cli as it +does not provide some of the niceties that ledger-cil provides. Goals ----- - Compact as possible -- Limited regex, ^,$ and * -- BSD style arguments -- Native Windows support +- Useful regex queries (^ $ *) +- UNIX style arguments +- Native MSVC support (Windows, ReactOS) - csv, emacs export/import Non-Goals --------- - Python support - Elaborate regex -- limited REPL - XML support -Build ------ +Other Build Targets +------------------- +MSVC under Windows, + + C:/payredu> make CC=cl + +Experimental GUI with hot reload (Linux only), - make hot + $ make hot -will generate 'hot' which can be used to test the hot-reload functionality with GUI for development. This is may be removed in feature to provide only a CUI interface. diff --git a/book.c b/book.c @@ -9,7 +9,9 @@ #include "common.h" #include "strn.h" +#ifndef _WIN32 #include <unistd.h> +#endif #define _XOPEN_SOURCE #include <time.h> @@ -121,11 +123,11 @@ ledger_parse_data(char *text, size_t text_len) memset(denom_list, 0, 256); char* denomptr = NULL; setvbuf(stdout, NULL, _IONBF, 0); + LedgerParseStates state = DATE; size_t line_no = 1; size_t current_column = 1; time_t t = time(NULL); - // printf("1|"); size_t i = 0; // TODO it may be possible to push these to the tree itself, explore the possibility // these act as temporary register until we push back the entry to a tree @@ -140,6 +142,10 @@ ledger_parse_data(char *text, size_t text_len) hold_amount = LONG_MAX; + setvbuf(stdout, NULL, _IONBF, 0); + // TODO it may be possible to push these to the tree itself, explore the possibility + // these act as temporary register until we push back the entry to a tree + while (i < text_len) { char c = text[i]; /* \n identifies an entry done in ledger */ @@ -193,7 +199,7 @@ ledger_parse_data(char *text, size_t text_len) if (isdigit(c)) { // try to parse a date time_t tn = ledger_timestamp_from_ledger_date(text + i); - warningf("%.*s: %ld ", 10, text + i, tn); + warningf("%.*s: %ld ", 10, text + i, (long)tn); // date is expected to have the form DD/MM/YYYY (10) i += 10; if (tn == (time_t) - 1) goto ledger_parse_error_handle; @@ -212,7 +218,7 @@ ledger_parse_data(char *text, size_t text_len) comment.len++; } warningf("Comment: %.*s", comment.len, - comment); + comment.str); state = ENTRY_START; } break; @@ -375,7 +381,7 @@ ledger_read_file(const char* filename, time_t date_start, time_t date_end) { entry->from = &me; entry->to = (Entity*)malloc(sizeof(Entity)); entry->to->name = (char*)malloc(sizeof(char*) * 20); - strcpy(entry->to->name, "Man"); + strncpy(entry->to->name, "Man", 3); } return new_list; } @@ -387,5 +393,6 @@ void warning("\n=======| Startality |=======\n"); ledger_parse_data(data, data_len); warning("\n========| Fatality |========\n"); + return NULL; } diff --git a/payredu.c b/payredu.c @@ -1,8 +1,11 @@ #include <stdio.h> #include <stdlib.h> -#include <dlfcn.h> +// TODO enable this for GUI +//#include <dlfcn.h> #include <signal.h> +#ifndef _WIN32 #include <unistd.h> +#endif #include <book.h> @@ -11,15 +14,42 @@ int should_exit = 0; -void sig_handle() { +void +sig_handle() { printf("Reloaded\n"); system("date"); should_exit = 1; } +typedef void* (*module_main_func)(const char*, size_t); + +#define die(...) \ + do { printf(__VA_ARGS__); \ + exit(-1); } while(0); + int main(int argc, char* argv[]) { - FILE* in = fopen("october-2023.txt", "r"); + FILE* in = NULL; + char* file_to_open = NULL; + // FIXME this is for debugging, payredu will not open any file by default + if (argc == 1) file_to_open = "october-2023.txt"; + else for (int i = 1; i < argc; i++) + if (argv[i][0] == '-' && strlen(argv[i]) == 2) { + switch (argv[i][1]) { + case 'f': + file_to_open = argv[++i]; + break; + default: + printf("unknown flag: %s\n", argv[i]); + break; + } + } else die("invalid argument: %s", argv[i]); + + if (file_to_open == NULL) + die("require a file to open, pass -f <filename>"); + + in = fopen(file_to_open, "r"); + char* data = (char*)malloc(2048 * sizeof(char)); size_t data_size = 0; size_t c_read = 0; diff --git a/strn.h b/strn.h @@ -3,7 +3,7 @@ int natoi(char* str, size_t len) { int final = 0; - int i = 0; + size_t i = 0; // ignore leading zeroes while(i < len && str[i] == '0') i++; for(;i < len; i++) {