/* 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; }