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 | |
| 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.
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | Makefile | 11 | ||||
| -rwxr-xr-x | pm | 22 | ||||
| -rw-r--r-- | pm-fmtdb.c | 277 | ||||
| -rw-r--r-- | readme.md | 8 | ||||
| -rw-r--r-- | strftime.c | 65 |
6 files changed, 293 insertions, 92 deletions
@@ -1,2 +1,2 @@ ses.vim -strftime +/pm-fmtdb @@ -1,10 +1,13 @@ PREFIX ?= /usr/local -all: strftime +CC ?= cc +CFLAGS ?= -D_DEFAULT_SOURCE -std=c99 -pedantic -Wall -O2 -s -install: strftime - install -m 755 strftime $(PREFIX)/bin/strftime +all: pm-fmtdb + +install: pm-fmtdb + install -m 755 pm-fmtdb $(PREFIX)/bin/pm-fmtdb install -m 755 pm $(PREFIX)/bin/pm clean: - rm -f strftime + rm -f pm-fmtdb @@ -21,20 +21,6 @@ PASTE_MANAGER_X_SELECTION="${PASTE_MANAGER_X_SELECTION:-clipboard}" # the only option called elsewhere is `-p` _menu="dmenu -l $PASTE_MANAGER_LINES -i $DMENU_FLAGS" -# This awk script controls how the output is formatted, which, will probably -# be quite unwieldly if you don't have a fuzzy finder patch in dmenu. -#shellcheck disable=SC2016 -awkPrettyPrint=' -BEGIN{ - FS="::"; -} -{ - # "strftime \"%H:%M:%S %m.%d.%y\" " $2 | getline created; - "strftime \"%H:%M:%S %m.%d.%y\" " $3 | getline updated; - printf("ID: %s Updated %s Name: %-60s Content: %s\n", - $1, updated, $4, $5); -}' - genid() { { dd if=/dev/urandom 2>/dev/null | tr -C -d 'a-zA-Z0-9'; echo; } \ | fold -w 8 | sed 1q @@ -76,7 +62,7 @@ insert() { } sel() { - awk "$awkPrettyPrint" < "$PASTE_MANAGER_FILE" \ + pm-fmtdb -f "$PASTE_MANAGER_FILE" \ | $_menu -p "$@" \ | sed -re 's/^ID: ([^ ]*).*$/\1/' } @@ -134,7 +120,7 @@ typeName() { rename() { sel "Item to rename" > "$_fifo" & read -r id < "$_fifo" - item="$(grep "^$id" "$PASTE_MANAGER_FILE" | awk "$awkPrettyPrint" )" + item="$(grep "^$id" "$PASTE_MANAGER_FILE" | pm-fmtdb -f - )" name="$(selectName "$id")" $_menu -p "Currently: ( $name ) New Name" > "$_fifo" & read -r name < "$_fifo" @@ -154,7 +140,7 @@ rename() { update() { sel "Item to update" > "$_fifo" & read -r id < "$_fifo" - item="$(grep "^$id" "$PASTE_MANAGER_FILE" | awk "$awkPrettyPrint" )" + item="$(grep "^$id" "$PASTE_MANAGER_FILE" | pm-fmtdb -f - )" output="$(selectContent "$id")" $_menu -p "Currently: ( $output ) New Content" > "$_fifo" & read -r content < "$_fifo" @@ -174,7 +160,7 @@ update() { delete() { sel "Item to delete" > "$_fifo" & read -r id < "$_fifo" - item="$(grep "^$id" "$PASTE_MANAGER_FILE" | awk "$awkPrettyPrint" )" + item="$(grep "^$id" "$PASTE_MANAGER_FILE" | pm-fmtdb -f - )" echo Item: "$item"' yes no' | $_menu -p "Are you sure?" > "$_fifo" & 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); +} @@ -8,8 +8,8 @@ This is *NOT* for passwords. Do *NOT* use it for them. See my own [`dpw`](https://git.riedstra.dev/mitch/dpw/about/) or [pass](https://www.passwordstore.org/) for a solution there. -Date formatting is a bit of a pain, so there's a `strftime` command line utility -that's bundled, yes `gawk` provides `strftime` but that's not on every system. +Output is controlled via a small c program called `pm-fmtdb`, see the top +of the file for adjusting the format. Works best with `dmenu` fuzzy finder patch. Utilizes list mode by default. @@ -53,8 +53,8 @@ leave it set to clipboard, the default. Edit the source code for further tweaks. The one people may have the most -interest in is the format sent to `dmenu`, that's controlled via the `awk` script -in the `awkPrettyPrint` variable near the top. +interest in is the format sent to `dmenu`, that's controlled via the small c +program 'pm-fmtdb' near the top of the file. ## Bugs diff --git a/strftime.c b/strftime.c deleted file mode 100644 index 70d171b..0000000 --- a/strftime.c +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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. - * - * A fairly basic interface to `strftime`, since dates are apparently difficult - * to handle with regular command line utilites and the POSIX spec for `date` - * is rather lacking. - */ -#include <stdio.h> -#include <stdlib.h> -#include <time.h> -#include <errno.h> - - -void -help() -{ - puts("strftime <fmt> [timestamp]"); - exit(1); -} - -int -main(int argc, char **argv) { - time_t t = time(NULL); - char **a = argv+1; - struct tm *tm; - char *fmt; - char out[256] = {0}; - - if (!*a) { - help(); - } - - fmt = *a; - a++; - - if (*a) { - errno = 0; - t = strtol(*a, NULL, 10); - if (errno != 0) { - perror("strtol"); - exit(1); - } - } - - tm = localtime(&t); - - if (strftime(out, 256, fmt, tm) == 0) { - perror("strftime"); - exit(1); - } - - puts(out); -} |
