diff options
Diffstat (limited to 'checklist.c')
| -rw-r--r-- | checklist.c | 205 |
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; +} |
