aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHugo Hörnquist <hugo@lysator.liu.se>2023-08-22 21:28:55 +0200
committerHugo Hörnquist <hugo@lysator.liu.se>2023-08-22 21:28:55 +0200
commit3ce865f4859587a1bf9a5ae16e791b7b58c29ebd (patch)
tree2b0b337e1511fb61a111acd9c690e48923ff2562
parentsön 20 aug 2023 22:07:23 CEST (diff)
downloadwiki-public-3ce865f4859587a1bf9a5ae16e791b7b58c29ebd.tar.gz
wiki-public-3ce865f4859587a1bf9a5ae16e791b7b58c29ebd.tar.xz
tis 22 aug 2023 21:28:55 CEST
-rw-r--r--wiki.c440
1 files changed, 440 insertions, 0 deletions
diff --git a/wiki.c b/wiki.c
new file mode 100644
index 0000000..b4e6b65
--- /dev/null
+++ b/wiki.c
@@ -0,0 +1,440 @@
+#include <errno.h>
+#include <glob.h>
+#include <libgen.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+#include <pwd.h>
+
+// #define WIKIROOT "~/wiki/"
+#define DATE_FORMAT "%a %d %b. %Y %H:%M:%S %Z"
+
+#if __STDC_VERSION__ >= 202311L
+#else
+#define typeof __typeof__
+#endif
+
+/** Calculate length of a static array */
+#define ARR_LEN(arr) ((size_t) (sizeof(arr) / sizeof((arr)[0])))
+#define ARRAY(x, ...) ((typeof(x)[]) {x, __VA_ARGS__})
+/* Call a procedure with a list of strings */
+#define CALL(proc, ...) proc(ARR_LEN(ARRAY(__VA_ARGS__)), ARRAY(__VA_ARGS__))
+
+typedef char **strlist;
+
+/**
+ * Collect all wikis under WIKIROOT.
+ * @param buf
+ * Location where the wikis should be stored.
+ * @returns -1 on failure (and print an error to stderr)
+ * Number of found wikis otherwise (if 0 a message will also be
+ * printed to stdout).
+ */
+int gather_wikis(const char *wiki_root, strlist *buf);
+
+/** Error callback for glob. */
+int errfunc(const char *path, int err);
+
+int commit(const char *msg);
+
+int ammend(const char *msg);
+
+
+/**
+ * Dispatch a git command through a shellout.
+ * @param argc - length of argv
+ * @param argv - arguments appended to the base git command.
+ * The base git command is configured thruogh the variable `cmdline_base`
+ */
+int git(size_t argc, const char *argv[]);
+
+/**
+ * Base command when calling git
+ */
+static char *cmdline_base[] = {
+ "git",
+ "-c", "color.status=always",
+ "-c", "color.ui=always",
+};
+
+
+
+/**
+ * Compare a fixed string to an aribtary string.
+ */
+bool cmp(const char *fix, const char *var) {
+ return strncmp(fix, var, strlen(fix)) == 0;
+}
+
+/**
+ * Collects the globaly available wikis, and print a pretty list of them
+ */
+void print_available_wikis();
+
+/** Print help string for the program to stdout */
+void print_help();
+
+/**
+ * Find the directory to search for wikis in.
+ * Returns a string which must be freed.
+ * */
+char *get_wikiroot();
+
+/*
+ * Concatenate all the strings in arr into a newly allocated string.
+ */
+char *strconcat(int count, const char *arr[]);
+
+/**
+ * Primary dispatcher of commands.
+ * @param wiki_root - Directory in which to find the given wiki
+ * @param wiki - Which wiki to operate on
+ * @param command - what to do
+ * First member should probably either be the name of a git
+ * subcommand, or one of our special commands.
+ * Remaining member will be used as arguments to that subcommand.
+ */
+void wiki_do(const char *wiki_root,
+ const char *wiki,
+ const char *command[]);
+
+int main (int argc, char *argv[]) {
+
+ /* At most every other argument can be a requested wiki */
+ strlist wiki_list = malloc(sizeof(*wiki_list) * argc / 2);
+ int wiki_list_ptr = 0;
+
+ int arg = 1;
+ for (; arg < argc; arg++) {
+ if (cmp("--wiki", argv[arg]) || cmp("-w", argv[arg])) {
+ /* realpath */
+ char *buf = realpath(argv[++arg], NULL);
+ if (buf == NULL) {
+ printf("Failed opening %s: %s\n", argv[arg - 1], strerror(errno));
+ } else {
+ wiki_list[wiki_list_ptr++] = buf;
+ }
+ continue;
+ } else
+ if (cmp("--list", argv[arg]) || cmp("-l", argv[arg])) {
+ print_available_wikis();
+ exit(0);
+ } else
+ if (cmp("--help", argv[arg]) || cmp("-l", argv[arg]) || cmp("-?", argv[arg])) {
+ print_help();
+ exit(0);
+ } else {
+ break;
+ }
+ }
+
+ char *wiki_root;
+
+ if (wiki_list_ptr == 0) {
+ free(wiki_list);
+ wiki_root = get_wikiroot();
+ gather_wikis(wiki_root, &wiki_list);
+ }
+
+ free(wiki_root);
+
+ /* If no further arguments are given, edit the given wiki */
+#if 0
+ if (arg == argc) {
+ char *wiki;
+ strlist wikis = NULL;
+ if (wiki_list_ptr != 0) {
+ wiki = wiki_list[0];
+ } else {
+ int wiki_count = gather_wikis(&wikis);
+ if (wiki_count < 0) {
+ exit(1);
+ }
+ if (wiki_count == 0) {
+ fprintf(stderr, "No wikis found\n");
+ }
+ wiki = wikis[0];
+ }
+ // TODO
+ // glob("${WIKIROOT}/${wiki}/index.*")
+ // execp("vim", "vim",
+ if (wikis) free(wikis);
+ } else {
+ /* If further arguments are given */
+ const char *cmd = argv[arg];
+ strlist final_list;
+ int wiki_count;
+ if (wiki_list_ptr != 0) {
+ final_list = wiki_list;
+ wiki_count = wiki_list_ptr;
+ } else {
+ free(wiki_list);
+ wiki_count = gather_wikis(&final_list);
+ if (wiki_count < 0) {
+ /* TODO handle error */
+ }
+ }
+
+ for (int i = 0; i < wiki_count; i++) {
+ /* TODO args */
+ wiki_do(wiki_root, final_list[i], args);
+ }
+ }
+#endif
+
+ free(wiki_list);
+
+}
+
+int gather_wikis(const char *wiki_root, strlist *wiki_list) {
+
+ int ret = 0;
+
+ glob_t pglob = { 0 };
+
+ ret = glob(CALL(strconcat, wiki_root, "/*"), GLOB_TILDE,
+ &errfunc, &pglob);
+
+ switch (ret) {
+ case 0:
+ break;
+ case GLOB_NOSPACE:
+ fprintf(stderr, "Glob ran out of space\n");
+ ret = -1;
+ goto gather_wikis_end;
+ case GLOB_ABORTED:
+ fprintf(stderr, "Glob was aborted\n");
+ ret = -1;
+ goto gather_wikis_end;
+ case GLOB_NOMATCH:
+ printf("No Wikis found\n");
+ goto gather_wikis_end;
+ default:
+ fprintf(stderr, "Unknown glob error. errno: %s\n",
+ strerror(errno));
+ ret = -1;
+ goto gather_wikis_end;
+ }
+
+ *wiki_list = malloc(sizeof(*wiki_list) * pglob.gl_matchc);
+
+ int idx = 0;
+ for (int i = 0; i < pglob.gl_matchc; i++) {
+ if (strstr(pglob.gl_pathv[i], "html") != NULL) {
+ (*wiki_list)[idx++] = basename(pglob.gl_pathv[i]);
+ }
+ }
+
+ ret = idx;
+
+gather_wikis_end:
+ return ret;
+}
+
+/**
+ * Error callback for glob.
+ * Simply print the error and continue.
+ */
+int errfunc(const char *path, int err) {
+ fprintf(stderr, "Failed opening %s: %s\n",
+ path,
+ strerror(err));
+ return 0;
+}
+
+int commit_ammend (const char* action, const char *msg) {
+ char *buf = NULL;
+
+ if (msg == NULL) {
+
+ time_t now = time(NULL);
+
+ struct tm result;
+ if (localtime_r(&now, &result) == NULL) {
+ msg = "Now";
+ }
+ buf = malloc(1024);
+ /* TODO check result */
+ strftime(buf, 1024, DATE_FORMAT, &result);
+ msg = buf;
+ }
+
+
+ /* "add" has type (char (*)[4]) (pointer to array of length 4.
+ * This just forces the type... */
+ CALL(git, (const char *) "add", "-A");
+ CALL(git, (const char *) "commit", "-m", msg);
+
+ if (buf) free(buf);
+
+ return 0;
+}
+
+int git(size_t argc, const char *argv[]) {
+ pid_t pid;
+
+ int ret;
+
+ switch (pid = fork()) {
+ case -1:
+ break;
+ case 0: /* child */
+
+ size_t cmdline_len = ARR_LEN(cmdline_base) + argc;
+
+ char **commandline = malloc((cmdline_len + 1) * sizeof(*commandline));
+
+ for (size_t i = 0; i < ARR_LEN(cmdline_base); i++) {
+ commandline[i] = cmdline_base[i];
+ }
+
+ for (size_t i = 0; i < argc; i++) {
+ commandline[i + ARR_LEN(cmdline_base)] = commandline[i];
+ }
+
+ commandline[cmdline_len] = NULL;
+
+ ret = execvp("git", commandline);
+ if (ret)
+ break;
+
+ fprintf(stderr, "Exec ");
+ for (size_t i = 0; i < cmdline_len; i++) {
+ fprintf(stderr, " %s", commandline[i]);
+ }
+ fprintf(stderr, " failed: %s\n", strerror(errno));
+ exit(1);
+
+ break;
+ default: /* parent */
+ break;
+ }
+
+ return ret;
+}
+
+void print_available_wikis() {
+ printf(
+ "Available Wikis:\n"
+ "================\n");
+ strlist wikis;
+ char *root = get_wikiroot();
+ int wiki_count = gather_wikis(root, &wikis);
+ free(root);
+ if (wiki_count < 0) {
+ return;
+ }
+ for (int i = 0; i < wiki_count; i++) {
+ printf("%i. %s\n", i, wikis[i]);
+ free(wikis[i]);
+ }
+ free(wikis);
+}
+
+void print_help() {
+ printf(
+ "Wiki helper. Usage:\n"
+ "--help | -h :: Display this help\n"
+ "--list | -l :: Show available wikis\n"
+ "--wiki | -w <wiki> :: specify a specific wiki for operation\n"
+ "\n"
+ "Default acts on all wikis.\n"
+ "\n"
+ "wi commit [msg] :: Create a git commit on specified wikis\n"
+ "wi ammend [msg] :: Change last commit\n"
+ "wi g [msg] :: wi commit [msg]; wi push\n"
+ "wi [git-command] :: run git commands on wikis\n");
+}
+
+
+void wiki_do(const char *wiki_root,
+ const char *wiki,
+ const char *command[]) {
+
+ if (wiki == NULL) return;
+ char *chdir_dest;
+ if (wiki[0] == '/') {
+ chdir_dest = strdup(wiki);
+ } else {
+ chdir_dest = CALL(strconcat, wiki_root, "/", wiki);
+ }
+
+ if (chdir(chdir_dest)) {
+ printf("failed chdir to %s : %s\n",
+ chdir_dest,
+ strerror(errno));
+ exit(1);
+ }
+
+ free(chdir_dest);
+
+ if (cmp("commit", command[0])) {
+ // (command + 1)
+ } else
+ if (cmp("ammend", command[0])) {
+ // (command + 1)
+ } else
+ if (cmp("grep", command[0])) {
+ // (command + 1)
+ } else
+ if (cmp("go", command[0]) || cmp("g", command[0])) {
+ // (command + 1)
+ } else
+ if (cmp("echo", command[0])) {
+ // (command + 1)
+ } else {
+ // (command + 1)
+ }
+}
+
+char *get_wikiroot() {
+ {
+ const char *result = getenv("WIKIROOT");;
+ if (result != NULL) {
+ return strdup(result);
+ }
+ }
+
+ const char *home = getenv("HOME");
+ struct passwd *pw;
+ if (home == NULL) {
+ pw = getpwuid(geteuid());
+ if (pw == NULL) {
+ home = "/";
+ } else {
+ home = pw->pw_dir;
+ }
+ }
+
+ return CALL(strconcat, home, "/wiki");
+}
+
+char *strconcat(int count, const char *arr[]) {
+ size_t allocated = 100;
+ size_t used = 0;
+ char *dest = malloc(allocated);
+ for (int i = 0; i < count; i++) {
+ size_t len = strlen(arr[i]);
+ if (used + len >= allocated) {
+ allocated <<= 2;
+ char *tmp = realloc(dest, allocated);
+ /* TODO should the old buffer be freed? */
+ if (tmp == NULL) {
+ fprintf(stderr, "Realloc failed\n");
+ exit(1);
+ }
+ dest = tmp;
+ }
+
+ memcpy(dest + used, arr[i], len);
+ used += len;
+ }
+
+ dest[used] = '\0';
+
+ return dest;
+}