/* * 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 #include #include #include #include #include #include #if __APPLE__ #include "compat/reallocarray.c" #endif #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 ]"); 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); }