aboutsummaryrefslogtreecommitdiff
path: root/pm-fmtdb.c
diff options
context:
space:
mode:
authorMitchell Riedstra <mitch@riedstra.dev>2023-02-04 22:02:09 -0500
committerMitchell Riedstra <mitch@riedstra.dev>2023-02-04 22:23:21 -0500
commite6bb137aa20133589079d1378ecef8f7f1e9a84f (patch)
tree3b662ec202d3032b13ac85142532519e79929da7 /pm-fmtdb.c
parentcdda89a3c76fef80f29951389df88ed23d052d52 (diff)
downloadpm-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.c277
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);
+}