summaryrefslogtreecommitdiffstats
path: root/sm.c
diff options
context:
space:
mode:
authorErik K <erikk@previousplan.org>2022-05-13 17:24:26 +0000
committerErik K <erikk@previousplan.org>2022-05-13 17:24:26 +0000
commitb2fada1dd19211d71e04557653d08e697134a6ce (patch)
tree063b6d1280193046321db1e53506be5b72d2220f /sm.c
initial commit
Diffstat (limited to 'sm.c')
-rw-r--r--sm.c442
1 files changed, 442 insertions, 0 deletions
diff --git a/sm.c b/sm.c
new file mode 100644
index 0000000..d4064c7
--- /dev/null
+++ b/sm.c
@@ -0,0 +1,442 @@
+#include <sys/wait.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "util.h"
+
+#define MAIN
+#include "config.h"
+#include "plugins.h"
+
+#define MIN(A, B) (A < B ? A : B)
+
+typedef struct {
+ char argv[10][CLEN];
+ int argc;
+} Cmd;
+
+int cat(const char *, FILE *);
+
+static int dbdel(const char *);
+static Document *dbfind(const char *);
+static void dbread(const char *);
+static void dbwrite(const char *);
+static void doautoexport(const char *);
+static void cmdsplit(Cmd *, char *);
+static void cfgread(const char *);
+static void expandlink(Document *d);
+static void expandpath(char *, size_t);
+static int pladddoc(const char *, Document *);
+
+static int add(const char *);
+static int edit(const char *);
+static void list(const char *);
+
+static Document **docdb;
+static int ndocdb;
+static int autoedit;
+static int autoexport;
+
+int
+cat(const char *in, FILE *outf)
+{
+ char buf[BUFSIZ];
+ size_t ret;
+ FILE *inf;
+
+ if ((inf = fopen(in, "r")) == NULL) {
+ fprintf(stderr, "%s: %s\n", in, strerror(errno));
+ return 0;
+ }
+ while ((ret = fread(buf, 1, sizeof(buf), inf))) {
+ if (!fwrite(buf, ret, 1, outf)) {
+ fprintf(stderr, "fwrite: %s\n", strerror(errno));
+ exit(1);
+ }
+ }
+ if (ferror(inf)) {
+ fprintf(stderr, "fread: %s\n", strerror(errno));
+ exit(1);
+ }
+ return 1;
+}
+
+
+static int
+dbdel(const char *filename)
+{
+ char path[512];
+ Document *d;
+ int i, j;
+ int di;
+
+ if ((d = dbfind(filename)) == NULL) {
+ fprintf(stderr, "%s: no such file\n", filename);
+ return 0;
+ }
+
+ for (i = 0; i < NPLUGINS; i++) {
+ for (j = 0; j < plugins[i]->nusedby; j++) {
+ if (plugins[i]->usedby[j] != d)
+ continue;
+ if (j + 1 < plugins[i]->nusedby) {
+ memmove(&plugins[i]->usedby[j], &plugins[i]->usedby[j +
+ 1], (plugins[i]->nusedby - j - 1) *
+ sizeof(*plugins[i]->usedby));
+ }
+ plugins[i]->usedby = xrealloc(plugins[i]->usedby,
+ --plugins[i]->nusedby *
+ sizeof(*plugins[i]->usedby));
+ }
+ }
+
+ for (di = 0; di < ndocdb; di++) {
+ if (docdb[di] == d)
+ break;
+ }
+ if (di == ndocdb)
+ return 0;
+ if (di + 1 < ndocdb) {
+ memmove(&docdb[di], &docdb[di + 1], (ndocdb - di - 1) *
+ sizeof(*docdb));
+ }
+ docdb = xrealloc(docdb, --ndocdb * sizeof(*docdb));
+
+ snprintf(path, sizeof(path), "%s/%s", kcontentdir, filename);
+ unlink(path);
+ free(d);
+ return 1;
+}
+
+static Document *
+dbfind(const char *filename)
+{
+ int i;
+
+ for (i = 0; i < ndocdb; i++) {
+ if (!strcmp(docdb[i]->filename, filename)) {
+ return docdb[i];
+ }
+ }
+
+ return NULL;
+}
+
+static void
+dbread(const char *path)
+{
+ FILE *db;
+ char buf[1024], *field, *p;
+ Document *d;
+ int i;
+
+ if ((db = fopen(path, "r")) == NULL)
+ return;
+ while (xfgets(buf, sizeof(buf), db)) {
+ if (buf[0] == '\0')
+ continue;
+ d = xmalloc(sizeof(*d));
+ for (i = 0, p = buf; (field = strsep(&p, "\t")); i++) {
+ switch (i) {
+ case 0: my_strlcpy(d->filename, field, sizeof(d->filename)); break;
+ case 1: my_strlcpy(d->title, field, sizeof(d->title)); break;
+ case 2: d->creat = strtoll(field, NULL, 10); break;
+ case 3: d->mod = strtoll(field, NULL, 10); break;
+ default: pladddoc(field, d);
+ }
+ }
+ if (i > 3) {
+ expandlink(d);
+ docdb = xrealloc(docdb, ++ndocdb * sizeof(*docdb));
+ docdb[ndocdb-1] = d;
+ } else {
+ free(d);
+ }
+ }
+ fclose(db);
+}
+
+static void
+dbwrite(const char *path)
+{
+ FILE *db;
+ int i, j, k;
+
+ db = xfopen(path, "w");
+ for (i = 0; i < ndocdb; i++) {
+ fprintf(db, "%s\t%s\t%ld\t%ld", docdb[i]->filename,
+ docdb[i]->title, docdb[i]->creat,
+ docdb[i]->mod);
+ for (j = 0; j < NPLUGINS; j++) {
+ for (k = 0; k < plugins[j]->nusedby; k++) {
+ if (plugins[j]->usedby[k] == docdb[i]) {
+ fprintf(db, "\t%s", plugins[j]->name);
+ break;
+ }
+ }
+ }
+ fputs("\n", db);
+ }
+ fclose(db);
+}
+
+static void
+doautoexport(const char *filename)
+{
+ Document *d;
+ int i, j;
+
+ if ((d = dbfind(filename)) == NULL)
+ return;
+ for (i = 0; i < NPLUGINS; i++) {
+ for (j = 0; j < plugins[i]->nusedby; j++) {
+ if (plugins[i]->usedby[j] == d) {
+ plugins[i]->export(plugins[i]->usedby,
+ plugins[i]->nusedby);
+ }
+ }
+ }
+}
+
+static void
+cmdsplit(Cmd *cmd, char *line)
+{
+ char *p, *arg;
+
+ p = line;
+ cmd->argc = 0;
+ while (cmd->argc < LEN(cmd->argv) && (arg = strsep(&p, " "))) {
+ if (!arg[0])
+ continue;
+ my_strlcpy(cmd->argv[cmd->argc++], arg, sizeof(cmd->argv[cmd->argc]));
+ }
+}
+
+static void
+cfgread(const char *path)
+{
+ char buf[CLEN], *end, *value, *store;
+ int ispath;
+ FILE *cfg;
+
+ cfg = xfopen(path, "r");
+ while (xfgets(buf, sizeof(buf), cfg)) {
+ if (buf[0] == '#' || !buf[0])
+ continue;
+ if ((value = strchr(buf, ' ')) == NULL || value == buf)
+ continue;
+ *value++ = '\0';
+ for (end = value + strlen(value) - 1; end >= value
+ && isspace(*end); *end-- = '\0') {
+ continue;
+ }
+ if ((store = storepointer(buf, &ispath))) {
+ my_strlcpy(store, value, CLEN);
+ if (ispath)
+ expandpath(store, CLEN);
+ }
+ }
+ fclose(cfg);
+}
+
+static void
+expandlink(Document *d)
+{
+ int i, j;
+
+ for (i = j = 0; j + 1 < sizeof(d->link) && klinkpreset[i]; i++) {
+ if (klinkpreset[i] == '%') {
+ my_strlcpy(&d->link[j], d->filename, sizeof(d->link) - j);
+ j += MIN(sizeof(d->link) - j - 1, strlen(d->filename));
+ } else {
+ d->link[j++] = klinkpreset[i];
+ }
+ }
+ d->link[j] = '\0';
+}
+
+static void
+expandpath(char *str, size_t max)
+{
+ const char *home = getenv("HOME");
+ char newpath[CLEN];
+
+ if (home == NULL)
+ return;
+ if (str[0] == '~') {
+ snprintf(newpath, sizeof(newpath), "%s%s", home, str + 1);
+ my_strlcpy(str, newpath, max);
+ }
+}
+
+/* pladddoc -- e.g -- pluginadddoc */
+static int
+pladddoc(const char *plname, Document *d)
+{
+ int i, j;
+
+ for (i = 0; i < NPLUGINS; i++) {
+ if (strcmp(plugins[i]->name, plname) != 0)
+ continue;
+ for (j = 0; j < plugins[i]->nusedby; j++) {
+ if (plugins[i]->usedby[j] == d)
+ goto next;
+ }
+ plugins[i]->usedby = xrealloc(plugins[i]->usedby,
+ ++plugins[i]->nusedby *
+ sizeof(*plugins[i]->usedby));
+ plugins[i]->usedby[plugins[i]->nusedby-1] = d;
+next:
+ }
+}
+
+
+static int
+add(const char *filename)
+{
+ Document *d;
+ char title[128];
+
+ if ((d = dbfind(filename))) {
+ fprintf(stderr, "%s: file exists\n", filename);
+ return 0;
+ }
+
+ xfputs("Title: ", stdout);
+ fflush(stdout);
+ if (!xfgets(title, sizeof(title), stdin))
+ return 0;
+
+ d = xmalloc(sizeof(*d));
+ my_strlcpy(d->filename, filename, sizeof(d->filename));
+ my_strlcpy(d->title, title, sizeof(d->title));
+ d->creat = d->mod = time(NULL);
+
+ expandlink(d);
+ docdb = xrealloc(docdb, ++ndocdb * sizeof(*docdb));
+ docdb[ndocdb-1] = d;
+
+ return 1;
+}
+
+static int
+edit(const char *filename)
+{
+ Document *d;
+ char path[512];
+ pid_t pid;
+
+ if ((d = dbfind(filename)) == NULL) {
+ fprintf(stderr, "%s: no such file\n", filename);
+ return 0;
+ }
+ snprintf(path, sizeof(path), "%s/%s", kcontentdir, filename);
+ switch ((pid = fork())) {
+ case -1:
+ fprintf(stderr, "fork: %s\n", strerror(errno));
+ exit(1);
+ case 0:
+ execl("/bin/sh", "sh", "-c", "$EDITOR \"$1\"", "sm-edit", path, NULL);
+ exit(1);
+ }
+ waitpid(pid, NULL, 0);
+ d->mod = time(NULL);
+ return 1;
+}
+
+static void
+list(const char *filename)
+{
+ char path[512];
+ FILE *f;
+ int i;
+
+ if (filename) {
+ snprintf(path, sizeof(path), "%s/%s", kcontentdir, filename);
+ cat(path, stdout);
+ } else for (i = 0; i < ndocdb; i++) {
+ printf("%-15s: %s\n", docdb[i]->filename, docdb[i]->title);
+ }
+}
+
+
+
+int
+main(int argc, char *argv[])
+{
+ char *cfgpath = "sm.conf";
+ char linebuf[CLEN];
+ Document *d;
+ Cmd cmd;
+ int i;
+
+ if (argc >= 2)
+ cfgpath = argv[1];
+
+ cfgread(cfgpath);
+ DUMPCFG();
+
+ if (!strcasecmp(kautoedit, "yes") || !strcasecmp(kautoedit, "true"))
+ autoedit = 1;
+ if (!strcasecmp(kautoexport, "yes") || !strcasecmp(kautoexport, "true"))
+ autoexport = 1;
+
+ dbread(kdbloc);
+ for (;;) {
+ xfputs("[sm-" VERSION "] ", stdout);
+ fflush(stdout);
+ if (!xfgets(linebuf, sizeof(linebuf), stdin))
+ break;
+ cmdsplit(&cmd, linebuf);
+ if (cmd.argc == 0)
+ continue;
+ for (i = 0; i < NPLUGINS; i++) {
+ if (!strcmp(plugins[i]->name, cmd.argv[0])) {
+ plugins[i]->export(plugins[i]->usedby, plugins[i]->nusedby);
+ break;
+ }
+ }
+ if (i != NPLUGINS)
+ continue;
+ if (!strcmp(cmd.argv[0], "quit") || !strcmp(cmd.argv[0], "exit"))
+ break;
+ if (!strcmp(cmd.argv[0], "list")) {
+ list(cmd.argc >= 2 ? cmd.argv[1] : NULL);
+ } else if (!strcmp(cmd.argv[0], "save")) {
+ dbwrite(kdbloc);
+ } else if (cmd.argc < 2) {
+ fprintf(stderr, "invalid command/needs more parameters\n");
+ } else if (!strcmp(cmd.argv[0], "add")) {
+ if (add(cmd.argv[1]))
+ dbwrite(kdbloc);
+ if (autoedit)
+ goto editcmd;
+ } else if (!strcmp(cmd.argv[0], "edit")) {
+editcmd:
+ if (edit(cmd.argv[1])) {
+ doautoexport(cmd.argv[1]);
+ dbwrite(kdbloc);
+ }
+ } else if (!strcmp(cmd.argv[0], "rm")) {
+ if (dbdel(cmd.argv[1]))
+ dbwrite(kdbloc);
+ } else if (cmd.argc < 3) {
+ fprintf(stderr, "invalid command/needs more parameters\n");
+ } else if (!strcmp(cmd.argv[0], "set")) {
+ if ((d = dbfind(cmd.argv[1])) == NULL) {
+ fprintf(stderr, "%s: no such file\n", cmd.argv[1]);
+ continue;
+ }
+ if (pladddoc(cmd.argv[2], d))
+ dbwrite(kdbloc);
+ }
+ }
+ fputc('\n', stdout);
+ return 0;
+}