commit 34d5c65e4c9ded5e7ebe8552fc73e015089b0cb9
parent 475634600bc5adfaa6bc087a707b9eb6d0f2a795
Author: Bharatvaj Hemanth <bharatvaj@yahoo.com>
Date: Sun, 19 Nov 2023 02:11:22 +0530
Add basic ledger file parsing in book.c
Add strn.h to parse integers with in between '.' and ',' (basic
localization support)
Diffstat:
8 files changed, 378 insertions(+), 60 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -1,5 +1,6 @@
balance
hot
+hotbook
*.so
*.o
tags
diff --git a/Makefile b/Makefile
@@ -26,5 +26,9 @@ hotbook: hotbook.c libbook.so
refresh:
git ls-files | entr sh hot.sh
+format:
+ indent -psl -pal --use-tabs -ts4 -br -brs -ce -cli0 book.c
+
clean:
- -rm $$(cat .gitignore)
+ -rm *.so *.o hotbook libbook.so
+
diff --git a/balance.c b/balance.c
@@ -38,8 +38,8 @@ void* module_main(struct nk_context* ctx, int width, int height) {
float size[] = {0.10, 0.70, 0.30};
nk_layout_row(ctx, NK_DYNAMIC, 0, 3, size);
nk_layout_row_begin(ctx, NK_STATIC, 25, 1);
- nk_layout_row_push(ctx, 40);
- nk_label(ctx, "500", NK_TEXT_LEFT);
+ //nk_layout_row_push(ctx, 40);
+ //nk_label(ctx, "500", NK_TEXT_LEFT);
nk_layout_row_end(ctx);
int len = 12;
@@ -52,8 +52,8 @@ void* module_main(struct nk_context* ctx, int width, int height) {
printf("%s\n", name);
edit_entry(i);
}
- nk_layout_row_push(ctx, 0.3f);
- nk_label(ctx, "500", NK_TEXT_LEFT);
+ //nk_layout_row_push(ctx, 0.3f);
+ //nk_label(ctx, "500", NK_TEXT_LEFT);
nk_layout_row_end(ctx);
}
diff --git a/book.c b/book.c
@@ -2,7 +2,11 @@
#include <stddef.h>
#include <stdlib.h>
#include <inttypes.h>
+#include <ctype.h>
#include "common.h"
+#include "strn.h"
+
+#include <unistd.h>
#include <GLFW/glfw3.h>
#define NK_INCLUDE_FIXED_TYPES
@@ -23,84 +27,373 @@
#include "book.h"
-float your_text_width_calculation(nk_handle handle, float height, const char *text, int len) {
- return 10.0f;
-}
+#define warning(STR,...) \
+ fprintf(stdout, "\033[31m"STR"\033[0m", __VA_ARGS__);
+
+typedef struct {
+ char *str;
+ size_t len;
+} vstr_t;
+
+size_t tree_depth = 4;
+
+struct map_tree;
-char* tags[100];
+struct map_tree {
+ vstr_t *value;
+ size_t children_cap;
+ size_t children_len;
+ struct map_tree *children;
+};
+
+typedef struct map_tree map_tree_t;
+
+vstr_t tags[100] = { 0 };
+
+size_t tags_size = 0;
/*
-char* tags = {
- "Expenses:Auto:Gas",
- "Liabilities:MasterCard",
- "Assets:Credit"
- };
-*/
+ * char* tags = { "Expenses:Auto:Gas", "Liabilities:MasterCard",
+ * "Assets:Credit" };
+ */
/**
*
-read the file | parse for newlines
-2023/10/24 - 5, fptr + 0, fptr + 6, fptr + 9
-Entry* entry = get_entry("2023/10/24");
+ read the file | parse for newlines
+ 2023/10/24 - 5, fptr + 0, fptr + 6, fptr + 9
+ Entry* entry = get_entry("2023/10/24");
-skip \n
-read until date, and read until from
+ skip \n
+ read until date, and read until from
-**/
+ **/
// commodity max size 256
// commodity name max size 4
-char commodity_list[256][4];
+char commodity_list[256][8];
+
+map_tree_t *rootp = NULL;
typedef struct {
- time_t date; // the date associated with the entry
- char* from;
- size_t from_len;
- short from_denom;
- long from_amount;
- char* to;
- size_t to_len;
- short to_denom;
- long to_amount;
-} LedgerEntry;
+ vstr_t *denom;
+ int amount;
+} LedgerValue;
+typedef struct {
+ vstr_t *reg;
+ LedgerValue val;
+} LedgerRecord;
-time_t ledger_timestamp_from_ledger_date(const char* date_str) {
- // coneverts string 'YYYY-MM-DD' to unix timestamp
+typedef struct {
+ time_t date; // the date associated with the entry
+ vstr_t comment; // comment associated with the entry
+ LedgerRecord **records;
+} LedgerEntry;
+
+time_t ledger_timestamp_from_ledger_date(char *date_str)
+{
+ // converts string 'YYYY-MM-DD' to unix timestamp
// date_str should be exactly 10 characters
- assert(strlen(date_str) == 10);
- struct tm tm;
- tm.tm_year = 2023;//strtoi(date_str, date_str + 10, 0, 0, 3);
- printf("%d", tm.tm_year);
- return 0;
+ // printf("%.*s\n", 4, date_str);
+ struct tm tm = { 0 };
+ tm.tm_year = natoi(date_str, 4) - 1900;
+ tm.tm_mon = natoi(date_str + 5, 2) - 1;
+ tm.tm_mday = natoi(date_str + 8, 2);
+ // warning("tm_year %d, tm_mon: %d, tm_mday: %d", natoi(date_str, 4),
+ // tm.tm_mon, tm.tm_mday);
+ return mktime(&tm);
+}
+
+const char *states_str[] = {
+ "DATE",
+ "COMMENT",
+ "ENTRY START",
+ "ENTRY SPACE",
+ "ENTRY WHO",
+ "ENTRY DENOM",
+ "ENTRY AMOUNT",
+ "ENTRY END",
+};
+
+typedef enum {
+ DATE,
+ COMMENT,
+ ENTRY_START, // entry starts after a comment
+ ENTRY_SPACE,
+ ENTRY_WHO,
+ ENTRY_DENOM,
+ ENTRY_AMOUNT,
+ ENTRY_END, // finish up entry if encountering any \n\n or \n text_len == i or text_len == i, otherwise set state to ENTRY_SPACE
+} LedgerParseStates;
+
+map_tree_t *account_search(map_tree_t *children, char *acc, size_t acc_size)
+{
+ if (children->children == NULL)
+ return children;
+ vstr_t *rk = children->value;
+ if (rk != NULL && acc_size == rk->len
+ && (strncmp(acc, rk->str, acc_size) == 0)) {
+ return children;
+ }
+ for (size_t i = 0; i < children->children_len; i++) {
+ vstr_t *val = children->children[i].value;
+ if (val != NULL && acc_size == val->len
+ && (strncmp(acc, val->str, acc_size) == 0)) {
+ return children->children + i;
+ }
+ }
+ // when the search is exhausted and nothing is found,
+ // return the previously allocated child
+ // TODO if len < cap allocate memory
+ map_tree_t *child_to_return =
+ children->children + children->children_len;
+ children->children_len++;
+ return child_to_return;
}
-void ledger_parse_data(const char* text, size_t text_len) {
+int account_add(map_tree_t **rootp, char *acc, size_t acc_size)
+{
+ size_t records_needed = tree_depth * 4;
+ if (*rootp == NULL) {
+ *rootp = malloc(sizeof(map_tree_t));
+ }
+ if ((*rootp)->children == NULL) {
+ (*rootp)->children =
+ (map_tree_t *) calloc(records_needed, sizeof(map_tree_t));
+ (*rootp)->children_cap = records_needed;
+ }
+ size_t i = 0;
+ while (i < acc_size) {
+ if (acc[i] == ':' || i + 1 == acc_size) {
+ size_t j = i + 1;
+ map_tree_t *current_node =
+ account_search(*rootp, acc, j);
+ assert(current_node != NULL);
+ if (current_node->value == NULL) {
+ // current_node->value is NULL when the search fails
+ // we have to set the value now
+ // TODO maybe save vstrs in a pool and use them
+ vstr_t *vstr =
+ (vstr_t *) malloc(sizeof(vstr_t));
+ vstr->str = acc;
+ vstr->len = j;
+ current_node->value = vstr;
+ printf("%d %.*s\n", j, j, acc);
+ } else {
+ printf("Present already= %d %.*s\n", j, j, acc);
+ }
+ if (i + 1 != acc_size) {
+ return account_add(&(*rootp)->children, acc + j,
+ acc_size - j);
+ }
+ }
+ i++;
+ }
+ return -1;
+}
+
+size_t tab_acc = 0;
+
+void walk_it (map_tree_t* rootp)
+{
+ if (rootp == NULL)
+ return;
+ vstr_t *val = rootp->value;
+ if (val != NULL) {
+ for (size_t i = 0; i < tab_acc; i++) {
+ printf("\t");
+ }
+ printf("-|%.*s|-\n", val->len, val->str);
+ }
+ tab_acc++;
+ if (rootp->children == NULL)
+ return;
+ for (int i = 0; i < rootp->children_len; i++) {
+ printf("|", i);
+ walk_it(rootp->children + i);
+ }
+ tab_acc--;
+}
+
+void ledger_parse_data(char *text, size_t text_len)
+{
+ setvbuf(stdout, NULL, _IONBF, 0);
+ LedgerParseStates state = DATE;
+ size_t line_no = 1;
+ size_t current_column = 1;
time_t t = time(NULL);
- for(int c = 0; c < text_len; c++) {
- switch(text[c]) {
- case ';':
- printf("#");
- break;
+ // 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
+ time_t hold_date;
+ vstr_t hold_comment = { 0 };
+ vstr_t hold_register = { 0 };
+ size_t hold_denom_id = { 0 };
+ short n_count = 0;
+
+ while (i < text_len) {
+ char c = text[i];
+ // we use \n to identify entry done in ledger
+ //
+ switch (c) {
+ case '\n':
+ case '\r':
+ line_no++;
+ n_count++;
+ printf("\n%d| ", line_no);
+ switch (state) {
+ case ENTRY_WHO:
+ case ENTRY_END:
+ // push the entries to stack or somethin
+ printf("case ENTRY_END: '%c', prev: '%c'\n", c, text[i-1]);
+ if (text[i - 1] == '\n') {
+ printf("----- Entry End Marked -----\n");
+ state = DATE;
+ } else {
+ state = ENTRY_WHO;
+ }
+ break;
+ case COMMENT:
+ state = ENTRY_WHO;
+ break;
+ case ENTRY_DENOM:
+ warning("%s\n", "denom not found, setting state WHO");
+ state = ENTRY_WHO;
+ break;
+ case ENTRY_AMOUNT:
+ goto ledger_parse_error_handle;
+ break;
+ }
+ i++;
+ continue;
case ' ':
- printf(" ");
+ case '\t':
+ n_count = 0;
+ i++;
+ continue;
break;
- case ' ':
- printf(" ");
+ }
+ n_count = 0;
+ // next state
+ switch (state) {
+ case DATE:
+ if (isdigit(c)) {
+ // try to parse a date
+ time_t tn = ledger_timestamp_from_ledger_date(text + i);
+ warning("date str: %.*s\n", 10, text + i);
+ warning("date: %ld\n", tn);
+ // date is expected to have the form DD/MM/YYYY (10)
+ i += 10;
+ if (tn == (time_t) - 1) goto ledger_parse_error_handle;
+ state = COMMENT;
+ }
break;
- case '\n':
- printf(",\n");
+ case COMMENT:
+ if (isalnum(c)) {
+ // we hit alphanumerical after whitespace
+ size_t comment_len = 0;
+ vstr_t comment = {
+ .str = text + i,
+ .len = 0
+ };
+ while (i < text_len && *(text + i) != '\n') {
+ i++;
+ comment_len++;
+ }
+ comment.len = comment_len;
+ warning("Comment: %.*s\n", comment_len,
+ comment);
+ state = ENTRY_WHO;
+ }
+ break;
+ case ENTRY_SPACE:
+ {
+ size_t original_i = i;
+ while (i < text_len && isspace(text[i])) i++;
+ int wsc = i - original_i;
+ warning("i: %ld, Spaces: %d\n", i, wsc);
+ if (wsc < 2) {
+ goto ledger_parse_error_handle;
+ }
+ state = ENTRY_WHO;
+ }
break;
- default:
- printf("*");
+ case ENTRY_WHO:
+ {
+ // add this to register
+ size_t who_len = i;
+ vstr_t who = {
+ .str = text + i,
+ .len = 0
+ };
+ while (i < text_len) {
+ switch (text[i]) {
+ case '\n':
+ case '\r':
+ goto ledger_who_parsed;
+ break;
+ case '\t':
+ case ' ':
+ goto ledger_who_parsed;
+ break;
+ default:
+ i++;
+ break;
+ }
+ }
+ledger_who_parsed:
+ who_len = i - who_len;
+ printf("parsed: i=%d\n", i);
+ account_add(&rootp, who.str, who_len);
+ warning("i=%d, Who: %.*s\n", i, who_len, who);
+ state = ENTRY_DENOM;
+ // add to tags here
+ }
break;
+ case ENTRY_DENOM:
+ {
+ warning("denom-i: %d\n", i + 1);
+ size_t denom_len = i;
+ vstr_t denom = {
+ .str = text + i,
+ .len = 0
+ };
+ while (i < text_len && !isdigit(*(text + i)))
+ i++;
+ state = ENTRY_AMOUNT;
+ denom_len = i - denom_len;
+ denom.len = denom_len;
+ warning("len: %d, denom: %.*s, i: %d\n", denom_len, denom_len, denom.str, i);
+ }
+ break;
+ case ENTRY_AMOUNT:
+ {
+ warning("amount-i: %d\n", i + 1);
+ char *amount = text + i;
+ size_t amount_len = i;
+ char _c = *(text + i);
+ while (i < text_len && (isdigit(_c) || _c == '.' || _c == ',')) {
+ i++;
+ _c = *(text + i);
+ }
+ state = ENTRY_END;
+ amount_len = i - amount_len;
+ warning("%d> len: %d, amount: %.*s\n", i, amount_len, amount_len, amount);
+ }
}
}
+ printf("read complete\n");
return;
+ledger_parse_error_handle:
+ warning("Parse failed at line %ld(%d)\n, Expected %s, got '%c'",
+ line_no, i, states_str[state], text[i]);
}
-void* module_main(const char* data, size_t data_len) {
- printf("%s\n", data);
-
+void *module_main(char *data, size_t data_len)
+{
+ // printf("%s\n", data);
+ printf("\n=======| Startality |=======\n");
ledger_parse_data(data, data_len);
+ printf("\n========| Fatality |========\n");
}
diff --git a/hot.c b/hot.c
@@ -56,7 +56,8 @@ int main(int argc, char* argv[]) {
ctx = nk_glfw3_init(win, NK_GLFW3_INSTALL_CALLBACKS);
/***********************/
//nk_init_fixed(ctx, calloc(1, MAX_MEMORY), MAX_MEMORY, &font);
- {struct nk_font_atlas *atlas;
+ {
+ struct nk_font_atlas *atlas;
nk_glfw3_font_stash_begin(&atlas);
struct nk_font *droid = nk_font_atlas_add_from_file(atlas, "./Ubuntu-Medium.ttf", 14, 0);
nk_glfw3_font_stash_end();
@@ -64,7 +65,7 @@ int main(int argc, char* argv[]) {
}
while(1) {
void* module = dlopen("./libbalance.so", RTLD_NOW);
- while(module == NULL){
+ while(module == NULL) {
fprintf(stderr, "Failed to load module. (%s)\n", dlerror());
fprintf(stderr, "Press return to try again.\n");
getchar();
diff --git a/hotbook b/hotbook
Binary files differ.
diff --git a/hotbook.c b/hotbook.c
@@ -23,7 +23,8 @@ void sig_handle() {
typedef void* (*module_main_func)(const char*, size_t);
int main(int argc, char* argv[]) {
- signal(SIGQUIT, sig_handle); while(1) {
+ signal(SIGQUIT, sig_handle);
+ //while(1) {
void* module = dlopen("./libbook.so", RTLD_NOW);
while(module == NULL){
fprintf(stderr, "Failed to load module. (%s)\n", dlerror());
@@ -36,18 +37,20 @@ int main(int argc, char* argv[]) {
char* data = (char*)malloc(2048 * sizeof(char));
size_t data_size = 0;
size_t c_read = 0;
- while((c_read = fread(data, 1, BUFFER_SIZE, in)) != 0) {
+ while((c_read = fread(data + data_size + 0, 1, BUFFER_SIZE, in)) != 0) {
data_size += c_read;
}
if (ferror(in)) fprintf(stderr, "Error reading file\n");
+ fprintf(stdout, "Startig loop\n");
module_main(data, data_size);
+
while(should_exit == 0) {
sleep(1);
}
should_exit = 0;
dlclose(module);
fprintf(stderr, "Continue?\n");
- }
+ //}
return 0;
}
diff --git a/strn.h b/strn.h
@@ -0,0 +1,16 @@
+#ifndef _STRN_H
+#define _STRN_H
+
+inline int natoi(char* str, size_t len) {
+ int final = 0;
+ int i = 0;
+ // ignore leading zeroes
+ while(i < len && str[i] == '0') i++;
+ for(;i < len; i++) {
+ final *= 10;
+ final += str[i] - '0';
+ }
+ return final;
+}
+
+#endif