diff options
| author | Mitchell Riedstra <mitch@riedstra.dev> | 2023-02-04 22:02:09 -0500 |
|---|---|---|
| committer | Mitchell Riedstra <mitch@riedstra.dev> | 2023-02-04 22:23:21 -0500 |
| commit | e6bb137aa20133589079d1378ecef8f7f1e9a84f (patch) | |
| tree | 3b662ec202d3032b13ac85142532519e79929da7 /pm-fmtdb.c | |
| parent | cdda89a3c76fef80f29951389df88ed23d052d52 (diff) | |
| download | pm-e6bb137aa20133589079d1378ecef8f7f1e9a84f.tar.gz pm-e6bb137aa20133589079d1378ecef8f7f1e9a84f.tar.xz | |
Switch formatting from AWK to a C program.
Due mostly to poor performance, calling out to the shell for time
formatting quickly degrades in performance with a large list, this
solves that problem. Yes, `gawk` has strftime, no, I don't want to use
gawk.
Diffstat (limited to 'pm-fmtdb.c')
| -rw-r--r-- | pm-fmtdb.c | 277 |
1 files changed, 277 insertions, 0 deletions
diff --git a/pm-fmtdb.c b/pm-fmtdb.c new file mode 100644 index 0000000..2683112 --- /dev/null +++ b/pm-fmtdb.c @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2023 Mitchell Riedstra + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + * Small program for quickly formatting the database file, reads it in, + * formats the date and spits it out in reversed order. + * Format is easily adjusted below at compile time. + * + */ +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> + +#define TIME_FMT_CREATED "%H:%M:%S %m.%d.%y" +#define TIME_FMT_UPDATED "%m.%d.%y" + +/* Format string for the dump output */ +#define OUTPUT_FMT "ID: %s Updated: %s Name: %-60s Content %s\n" +/* See the Entry struct below for available fields */ +#define OUTPUT_FMT_ARGS cur->Id, updated, cur->name, cur->content + +/* + * Be careful changing this one, the `pm` shell script will need to be updated + * too. + */ +#define DB_DELIM "::" + +#define TIME_FMT_MAX 256 +#define ENTRY_MAX 1024 + +struct Entry { + char *Id; + time_t created; + time_t updated; + char *name; + char *content; + + struct Entry *next; + struct Entry *prev; +}; + +void +help() +{ + puts("pm-fmtdb [-f <file>]"); + exit(1); +} + +void +die(const char *msg) +{ + perror(msg); + exit(1); +} + +void * +ecalloc(size_t nmemb, size_t size) +{ + void *ret; + ret = calloc(nmemb, size); + if (!ret) { + die("calloc"); + } + return ret; +} + +int +FmtTimestamp(char **dest, time_t t, const char *fmt, size_t bufsize) +{ + struct tm *tm; + char *out = ecalloc(bufsize, sizeof(char *)); + + tm = localtime(&t); + + if (strftime(out, bufsize, fmt, tm) == 0) { + free(out); + return -1; + } + + *dest = out; + return 0; +} + +char ** +charArrAppend(char **arr, size_t curlen, char *s) +{ + arr = reallocarray(arr, curlen+1, sizeof(char **)); + if (!arr) { + die("reallocarray"); + } + + arr[curlen] = s; + return arr; +} + + +char ** +split(char *buf, const char *delim) { + int outlen = 0; + int len = strlen(buf); + char **out = NULL; + int dl = strlen(delim); + int dpos = 0; + + int s = 0; + for (int n = 0; n < len; n++) { + + if (buf[n] == '\n') { + char *str = ecalloc(n-s+1, sizeof(char *)); + strncpy(str, &buf[s], n-s); + out = charArrAppend(out, outlen++, str); + break; + } + + if (dpos+1 == dl && buf[n] == delim[dpos]) { + char *str = ecalloc((n-s)+1, sizeof(char *)); + strncpy(str, &buf[s], n-dl-s+1); + s = n+1; + + out = charArrAppend(out, outlen++, str); + + dpos = 0; + continue; + } + + if (buf[n] == delim[dpos]) { + dpos++; + } else { + dpos = 0; + } + + } + return charArrAppend(out, outlen++, NULL); +} + +struct Entry * +loadEntries(FILE *f) { + int ret; + size_t max = ENTRY_MAX; + char *buf = ecalloc(max, sizeof(char *)); + char **fields; + struct Entry *e = ecalloc(1, sizeof(struct Entry)); + struct Entry *eprev = NULL; + int n; + + for (;;) { + ret = getdelim(&buf, &max, '\n', f); + if (ret == -1 && ret != EOF) { + die("read"); + } + + if (ret == EOF) + break; + + fields = split(buf, DB_DELIM); + char **f = fields; + for (n = 0;*f;f++) { + switch(n) { + case 0: + e->Id = *f; + break; + case 1: + errno = 0; + e->created = strtol(*f, NULL, 10); + if (errno != 0) + perror("bad timestamp: strtol"); + break; + case 2: + errno = 0; + e->updated = strtol(*f, NULL, 10); + if (errno != 0) + perror("bad timestamp: strtol"); + break; + case 3: + e->name = *f; + break; + case 4: + e->content = *f; + } + n++; + } + + eprev = e; + e = ecalloc(1, sizeof(struct Entry)); + eprev->next = e; + e->prev = eprev; + } + + free(buf); buf = NULL; + + return eprev; +} + +char * +strAppend(char *a, char *b) +{ + int alen = strlen(a); + char *n = ecalloc(alen+strlen(b)+1, sizeof(char)); + strcpy(n, a); + strcpy(n+alen, b); + return n; +} + +void +dumpDB(FILE *db) +{ + struct Entry *cur, *last; + last = loadEntries(db); + if (!last) { + puts("Failed to load entries"); + exit(1); + } + + for (cur = last; cur; cur = cur->prev) { + char *updated; + if (FmtTimestamp(&updated, cur->updated, TIME_FMT_UPDATED, + TIME_FMT_MAX) == -1) { + die("FmtTimestamp"); + } + char *created; + if (FmtTimestamp(&created, cur->created, TIME_FMT_CREATED, + TIME_FMT_MAX) == -1) { + die("FmtTimestamp"); + } + + printf(OUTPUT_FMT, OUTPUT_FMT_ARGS); + free(updated); updated = NULL; + } +} + +int +main(int argc, char **argv) { + char *fn = getenv("PASTE_MANAGER_FILE"); + char **a = argv+1; + FILE *f; + + for (;*a;a++) { + if (strcmp(*a, "-f") == 0 && *(++a)) { + fn = *a; + } else { + if (*a) + printf("Unknown option: '%s'\n", *a); + + help(); + } + } + + if (!fn) { + fn = strAppend(getenv("HOME"), "/.pastes"); + } + + if (*fn == '-') { + f = stdin; + } else { + f = fopen(fn, "r"); + if (!f) { + die("open"); + } + } + + dumpDB(f); +} |
