summaryrefslogtreecommitdiffstats
path: root/checklist.c
diff options
context:
space:
mode:
authorMattias Andrée <maandree@kth.se>2021-04-06 16:48:40 +0200
committerMattias Andrée <maandree@kth.se>2021-04-06 16:48:40 +0200
commit8898f95ea9346d1c40916648a8127339a89adf83 (patch)
tree6e375d1b139bc7a86b3bf1cf857ad08a343f87a1 /checklist.c
parentFix warnings (diff)
downloadpdatools-8898f95ea9346d1c40916648a8127339a89adf83.tar.gz
pdatools-8898f95ea9346d1c40916648a8127339a89adf83.tar.bz2
pdatools-8898f95ea9346d1c40916648a8127339a89adf83.tar.xz
Begin working on checklist
Signed-off-by: Mattias Andrée <maandree@kth.se>
Diffstat (limited to 'checklist.c')
-rw-r--r--checklist.c205
1 files changed, 205 insertions, 0 deletions
diff --git a/checklist.c b/checklist.c
new file mode 100644
index 0000000..5d7e261
--- /dev/null
+++ b/checklist.c
@@ -0,0 +1,205 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+/* TODO Add support for comments (indent the next line in the file with a tab) */
+
+USAGE("");
+
+
+struct entry {
+ struct entry *next;
+ struct entry *prev;
+ struct entry *in;
+ struct entry *out;
+ struct entry *flat_next;
+ char status;
+ char text[];
+};
+
+
+static struct entry *
+load(const char *path)
+{
+ struct entry *entry, *ret = NULL, *tail = NULL, *parent = NULL;
+ FILE *fp;
+ char *line = NULL;
+ size_t size = 0, lineno = 0, indent = 0, i;
+ ssize_t len;
+
+ fp = fopen(path, "r");
+ if (!fp) {
+ if (errno == ENOENT)
+ return NULL;
+ eprintf("fopen %s \"r\":", path);
+ }
+
+ while ((len = getdelim(&line, &size, '\n', fp)) >= 0) {
+ lineno += 1;
+ if (len && line[len - 1] == '\n')
+ line[--len] = '\0';
+ for (i = 0; line[i] == ' '; i++);
+ if (i == (size_t)len)
+ eprintf("line %zu in %s is malformatted 1\n", lineno, path);
+ if (i > indent + (size_t)!!ret)
+ eprintf("line %zu in %s is malformatted 2 %zu > %zu + %i\n", lineno, path, i, indent, !!ret);
+ entry = ecalloc(1, offsetof(struct entry, text) + (size_t)len - i);
+ if (!ret)
+ ret = entry;
+ else
+ tail->flat_next = entry;
+ entry->status = line[i];
+ if (entry->status != '_' && /* unmarked, displayed as [ ] */
+ entry->status != 'X' && /* done */
+ entry->status != '-' && /* not now */
+ entry->status != '/' && /* partially done */
+ entry->status != '!' && /* important */
+ entry->status != '?') /* maybe */
+ eprintf("line %zu in %s is malformatted\n", lineno, path);
+ stpcpy(entry->text, &line[i + 1]);
+ if (i == indent) {
+ entry->prev = tail;
+ if (tail)
+ tail->next = entry;
+ entry->out = parent;
+ } else if (i > indent) {
+ indent += 1;
+ entry->out = tail;
+ tail->in = entry;
+ parent = entry->out;
+ } else {
+ while (--indent > i)
+ parent = parent->out;
+ entry->prev = parent;
+ parent->next = entry;
+ parent = parent->out;
+ entry->out = parent;
+ }
+ tail = entry;
+ }
+
+ if (ferror(fp))
+ eprintf("getdelim %s:", path);
+
+ fclose(fp);
+ free(line);
+ return ret;
+}
+
+
+static struct entry *
+get_next(struct entry *entry, size_t *indentp)
+{
+ if (entry->in) {
+ ++*indentp;
+ return entry->in;
+ }
+ if (entry->next)
+ return entry->next;
+ while (entry->out) {
+ entry = entry->out;
+ --*indentp;
+ if (entry->next)
+ return entry->next;
+ }
+ return NULL;
+}
+
+
+static int
+save(const char *path, const char *tmppath, struct entry *head)
+{
+#define SPACES " "
+
+ int fd, saved_errno;
+ size_t indent = 0, n, off;
+ ssize_t r;
+
+ fd = open(tmppath, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (fd < 0)
+ return -1;
+
+ for (; head; head = get_next(head, &indent)) {
+ for (n = indent; n; n -= (size_t)r) {
+ r = write(fd, SPACES, indent < sizeof(SPACES) - 1 ? indent : sizeof(SPACES) - 1);
+ if (r < 0)
+ goto fail;
+ }
+ if (write(fd, &head->status, 1) < 1)
+ goto fail;
+ n = strlen(head->text);
+ for (off = 0; off < n; off += (size_t)r) {
+ r = write(fd, &head->text[off], n - off);
+ if (r < 0)
+ goto fail;
+ }
+ if (write(fd, &(char){'\n'}, 1) < 1)
+ goto fail;
+ }
+
+ if (fsync(fd)) {
+ fail:
+ saved_errno = errno;
+ close(fd);
+ errno = saved_errno;
+ return -1;
+ }
+ if (close(fd) || rename(tmppath, path))
+ return -1;
+
+ return 0;
+
+#undef SPACES
+}
+
+
+static char *
+get_path(void)
+{
+ struct passwd *user;
+ size_t homelen;
+ char *ret;
+
+ errno = 0;
+ user = getpwuid(getuid());
+ if (!user)
+ eprintf("getpwuid: %s\n", errno ? strerror(errno) : "user does not exist");
+ if (!user->pw_dir || !*user->pw_dir)
+ eprintf("user does not have a home directory\n");
+ if (access(user->pw_dir, R_OK | W_OK | X_OK))
+ eprintf("access %s R_OK|W_OK|X_OK:", user->pw_dir);
+
+ homelen = strlen(user->pw_dir);
+ if (homelen && user->pw_dir[homelen - 1] == '/')
+ homelen -= 1;
+
+ ret = emalloc(homelen + sizeof("/.var/lib/pdatools/checklist"));
+ memcpy(ret, user->pw_dir, homelen);
+ memcpy(&ret[homelen], "/.var/lib/pdatools/checklist", sizeof("/.var/lib/pdatools/checklist"));
+ return ret;
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ char *path, *tmppath;
+ struct entry *head, *node;
+ size_t indent;
+
+ NOFLAGS(argc);
+
+ path = get_path();
+ tmppath = emalloc(strlen(path) + 2);
+ stpcpy(stpcpy(tmppath, path), "~");
+ head = load(path);
+
+ /* TODO */
+ indent = 0;
+ for (node = head; node; node = get_next(node, &indent))
+ printf("%*.s[%c] %s\n", (int)indent * 2, "", node->status, node->text);
+
+ for (node = NULL; free(node), head; node = head, head = head->flat_next);
+ free(path);
+ free(tmppath);
+ return 0;
+}