#include #include #include #include #include #include #include #include #include #include #include #include #include "blaze822.h" #include "blaze822_priv.h" char * blaze822_home_file(char *basename) { static char path[PATH_MAX]; static char *homedir; static char *profile; if (!profile) profile = getenv("MBLAZE"); if (profile) { snprintf(path, sizeof path, "%s/%s", profile, basename); return path; } if (!homedir) homedir = getenv("HOME"); if (homedir && !*homedir) homedir = 0; if (!homedir) { struct passwd *pw = getpwuid(getuid()); if (pw) homedir = pw->pw_dir; } if (!homedir) return "/dev/null/homeless"; snprintf(path, sizeof path, "%s/.mblaze/%s", homedir, basename); return path; } char * blaze822_seq_open(char *file) { if (!file) file = getenv("MAILSEQ"); if (!file) file = blaze822_home_file("seq"); char *map; off_t len; int r = slurp(file, &map, &len); if (r != 0) { fprintf(stderr, "could not read sequence '%s': %s\n", file, strerror(r)); return 0; } return map; } static void *msgnums; struct msgnum { char *file; long pos; }; int msgnumorder(const void *a, const void *b) { struct msgnum *ia = (struct msgnum *)a; struct msgnum *ib = (struct msgnum *)b; return strcmp(ia->file, ib->file); } long blaze822_seq_find(char *file) { struct msgnum key, **result; key.file = file; if (!(result = tfind(&key, &msgnums, msgnumorder))) return 0; return (*result)->pos; } int blaze822_seq_load(char *map) { char *s, *t; long line; for (s = map, line = 0; s; s = t+1) { t = strchr(s, '\n'); if (!t) break; line++; while (*s && iswsp(*s)) s++; char *e = t; while (s < e && isfws(*(e-1))) e--; char *f = strndup(s, e-s); if (!f) return -1; struct msgnum key; key.file = f; if (!tfind(&key, &msgnums, msgnumorder)) { struct msgnum *c = malloc(sizeof (struct msgnum)); c->file = f; c->pos = line; tsearch(c, &msgnums, msgnumorder); } } return 0; } char * blaze822_seq_cur(void) { static char b[PATH_MAX]; char *override = getenv("MAILDOT"); if (override) return override; char *curlink = getenv("MAILCUR"); if (!curlink) curlink = blaze822_home_file("cur"); ssize_t r = readlink(curlink, b, sizeof b - 1); if (r < 0) return 0; b[r] = 0; return b; } int blaze822_seq_setcur(char *s) { if (strcmp(s, "/dev/stdin") == 0) return 0; char *override = getenv("MAILDOT"); if (override) return 0; char curtmplink[PATH_MAX]; char *curlink = getenv("MAILCUR"); if (!curlink) curlink = blaze822_home_file("cur"); if (snprintf(curtmplink, sizeof curtmplink, "%s-", curlink) >= PATH_MAX) return -1; // truncation if (unlink(curtmplink) < 0 && errno != ENOENT) return -1; if (symlink(s, curtmplink) < 0) return -1; if (rename(curtmplink, curlink) < 0) return -1; return 0; } static char * parse_relnum(char *a, long cur, long start, long last, long *out) { long base; char *b; if (strcmp(a, "+") == 0) a = ".+1"; else if (strcmp(a, ".-") == 0) a = ".-1"; else if (strcmp(a, ".") == 0) a = ".+0"; if (*a == '$') { a++; base = last; } else if (*a == '.') { a++; base = cur; } else if (*a == '-') { base = last + 1; } else if (*a == '+') { base = start; } else { base = 0; } long d; if (strchr(":=_^", *a)) { d = 0; b = a; } else { errno = 0; d = strtol(a, &b, 10); if (errno != 0) { perror("strtol"); exit(2); } } *out = base + d; if (*out <= 0) *out = 1; if (*out > last) *out = last; return b; } static int parse_thread(char *map, long a, long *starto, long *stopo) { char *s, *t; long line; long start = 0, stop = 0, state = 0; for (s = map, line = 0; s; s = t+1) { t = strchr(s, '\n'); if (!t) break; line++; if (!iswsp(*s)) { if (state == 0) { start = line; } else if (state == 1) { stop = line - 1; state = 2; break; } } if (line == a) state = 1; while (*s && iswsp(*s)) s++; } if (state == 1) { stop = line; state = 2; } if (state == 2) { *starto = start; *stopo = stop; return 0; } return 1; } static int parse_subthread(char *map, long a, long *stopo) { char *s, *t; long line; long stop = 0; int minindent = -1; for (s = map, line = 0; s; s = t+1) { t = strchr(s, '\n'); if (!t) { minindent = -1; break; } line++; int indent = 0; while (*s && iswsp(*s)) { s++; indent++; } if (line == a) minindent = indent; if (line > a && indent <= minindent) { stop = line - 1; break; } } if (line < a) return 1; if (minindent == -1) stop = line; *stopo = stop; return 0; } static int parse_parent(char *map, long *starto, long *stopo) { char *s, *t; long line; long previndent[256] = { 0 }; for (s = map, line = 0; s; s = t+1) { t = strchr(s, '\n'); if (!t) break; line++; long indent = 0; while (*s && iswsp(*s)) { s++; indent++; } if (indent > 255) indent = 255; previndent[indent] = line; if (line == *starto) { if (previndent[indent-1]) { *starto = *stopo = previndent[indent-1]; return 0; } else { return 1; } } } return 1; } static int parse_range(char *map, char *a, long *start, long *stop, long cur, long lines) { *start = 0; *stop = 1; while (*a && *a != ':' && *a != '=' && *a != '_' && *a != '^') { char *b = parse_relnum(a, cur, 0, lines, start); if (a == b) return 1; a = b; } if (*start == 0) *start = strchr("=^_", *a) ? cur : 1; while (*a == '^') { a++; if (parse_parent(map, start, stop)) return 2; } if (*a == ':') { a++; if (!*a) { *stop = lines; } else { char *b = parse_relnum(a, cur, *start, lines, stop); if (a == b) return 1; } } else if (*a == '=') { return parse_thread(map, *start, start, stop); } else if (*a == '_') { return parse_subthread(map, *start, stop); } else if (!*a) { *stop = *start; } else { return 1; } return 0; } void find_cur(char *map, struct blaze822_seq_iter *iter) { char *s, *t; long cur = 0; const char *curfile = blaze822_seq_cur(); iter->lines = 0; for (s = map; s; s = t+1) { t = strchr(s, '\n'); if (!t) break; while (*s == ' ' || *s == '\t') s++; iter->lines++; if (!cur && curfile && strncmp(s, curfile, strlen(curfile)) == 0 && (s[strlen(curfile)] == '\n' || s[strlen(curfile)] == ' ' || s[strlen(curfile)] == '\t')) iter->cur = iter->lines; } } char * blaze822_seq_next(char *map, char *range, struct blaze822_seq_iter *iter) { if (strcmp(range, ".") == 0) { if (!iter->lines && !iter->start) { iter->lines = 1; find_cur(map, iter); iter->start = iter->stop = iter->line = iter->cur + 1; char *cur = blaze822_seq_cur(); return cur ? strdup(cur) : 0; } else { return 0; } } if (!map) return 0; if (!iter->lines) // count total lines find_cur(map, iter); if (!iter->s) { int ret = parse_range(map, range, &iter->start, &iter->stop, iter->cur, iter->lines); if (ret == 1) { fprintf(stderr, "can't parse range: %s\n", range); return 0; } else if (ret == 2) { fprintf(stderr, "message not found for specified range: %s\n", range); return 0; } iter->s = map; iter->line = 1; } if (!iter->s) return 0; while (iter->line < iter->start) { char *t = strchr(iter->s, '\n'); if (!t) return 0; iter->line++; iter->s = t + 1; } if (iter->line > iter->stop) { iter->start = iter->stop = 0; // reset iteration return 0; } char *t = strchr(iter->s, '\n'); if (!t) return 0; iter->cur = iter->line; iter->line++; char *r = strndup(iter->s, t-iter->s); iter->s = t + 1; return r; } int mystrverscmp(const char *, const char *); static int mailsort(const struct dirent **a, const struct dirent **b) { return mystrverscmp((*a)->d_name, (*b)->d_name); } static long iterdir(char *dir, void (*cb)(char *)) { struct dirent **namelist; int n; char sub[PATH_MAX]; snprintf(sub, sizeof sub, "%s/cur", dir); char *m = "/cur"; n = scandir(sub, &namelist, 0, mailsort); if (n == -1 && (errno == ENOENT || errno == ENOTDIR)) { m = ""; n = scandir(dir, &namelist, 0, mailsort); } if (n == -1) { if (errno == ENOTDIR) cb(dir); return 1; } long i = 0; for (i = 0; i < n; i++) { if (namelist[i]->d_name[0] != '.' && MAIL_DT(namelist[i]->d_type)) { snprintf(sub, sizeof sub, "%s%s/%s", dir, m, namelist[i]->d_name); cb(sub); } free(namelist[i]); } free(namelist); return i; } long blaze822_loop(int argc, char *argv[], void (*cb)(char *)) { char *line = 0; size_t linelen = 0; ssize_t rd; long i = 0; if (argc == 0) { while ((rd = getdelim(&line, &linelen, '\n', stdin)) != -1) { if (line[rd-1] == '\n') line[rd-1] = 0; cb(line); i++; } free(line); return i; } char *map = 0; int map_opened = 0; int j = 0; for (i = 0; i < argc; i++) { if (strchr(argv[i], '/')) { // a file name j += iterdir(argv[i], cb); } else if (strcmp(argv[i], "-") == 0) { if (isatty(0)) { fprintf(stderr, "mblaze: warning: - now means " "read mail text from standard input, " "use .- to refer to previous mail\n"); } cb("/dev/stdin"); j++; } else { if (!map_opened) { map = blaze822_seq_open(0); map_opened = 1; } struct blaze822_seq_iter iter = { 0 }; while ((line = blaze822_seq_next(map, argv[i], &iter))) { cb(line); free(line); j++; } } } free(map); return j; } long blaze822_loop1(char *arg, void (*cb)(char *)) { char *args[] = { arg }; return blaze822_loop(1, args, cb); }