summary refs log tree commit diff
diff options
context:
space:
mode:
authorChristian Neukirchen <chneukirchen@gmail.com>2016-07-10 20:27:27 +0200
committerChristian Neukirchen <chneukirchen@gmail.com>2016-07-10 20:27:27 +0200
commit30f0686fb2d332cdc0ced15ac2c04094a127783a (patch)
tree33f3e088d2583e4c4708076a074eaab96e431ab1
downloadmblaze-30f0686fb2d332cdc0ced15ac2c04094a127783a.tar.gz
mblaze-30f0686fb2d332cdc0ced15ac2c04094a127783a.tar.xz
mblaze-30f0686fb2d332cdc0ced15ac2c04094a127783a.zip
various code that may end up as a mail client
-rw-r--r--blaze822.c373
-rw-r--r--fastls.c97
-rw-r--r--flags.c48
-rw-r--r--foo822.c368
-rw-r--r--next.c44
5 files changed, 930 insertions, 0 deletions
diff --git a/blaze822.c b/blaze822.c
new file mode 100644
index 0000000..11b5285
--- /dev/null
+++ b/blaze822.c
@@ -0,0 +1,373 @@
+// memmem
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+
+//          WSP            =  SP / HTAB
+#define iswsp(c)  (((c) == ' ' || (c) == '\t'))
+
+#define bufsiz 4096
+
+static long
+parse_posint(char **s, size_t minn, size_t maxn)
+{
+        long n;
+        char *end;
+
+        errno = 0;
+        n = strtol(*s, &end, 10);
+        if (errno) {
+//                perror("strtol");
+		return -1;
+        }
+        if (n < (long)minn || n > (long)maxn) {
+//                fprintf(stderr, "number outside %zd <= n < %zd\n", minn, maxn);
+		return -1;
+        }
+        *s = end;
+        return n;
+}
+
+time_t
+parse_date(char *s) {
+	struct tm tm;
+	int c;
+
+#if 0
+#define i4(m) (s[0] && (s[0]|0x20) == m[0] && \
+               s[1] && (s[1]|0x20) == m[1] && \
+               s[2] && (s[2]|0x20) == m[2] && \
+               s[3] && (s[3]|0x20) == m[3] && (s = s+4) )
+
+#define i3(m) (s[0] && (s[0]|0x20) == m[0] && \
+               s[1] && (s[1]|0x20) == m[1] && \
+               s[2] && (s[2]|0x20) == m[2] && (s = s+3) )
+#endif
+
+#define i4(m) (((uint32_t) m[0]<<24 | m[1]<<16 | m[2]<<8 | m[3]) == \
+	       ((uint32_t) s[0]<<24 | s[1]<<16 | s[2]<<8 | s[3] | 0x20202020) \
+	       && (s += 4))
+
+#define i3(m) (((uint32_t) m[0]<<24 | m[1]<<16 | m[2]<<8) == \
+	       ((uint32_t) s[0]<<24 | s[1]<<16 | s[2]<<8 | 0x20202000) \
+	       && (s += 3))
+
+	while (iswsp(*s))
+		s++;
+	if (i4("mon,") || i4("tue,") || i4("wed,") || i4("thu,") ||
+	    i4("fri,") || i4("sat,") || i4("sun,"))
+		while (iswsp(*s))
+			s++;
+
+	if ((c = parse_posint(&s, 1, 31)) < 0) goto fail;
+	tm.tm_mday = c;
+
+	while (iswsp(*s))
+		s++;
+	
+	if      (i3("jan")) tm.tm_mon = 0;
+	else if (i3("feb")) tm.tm_mon = 1;
+	else if (i3("mar")) tm.tm_mon = 2;
+	else if (i3("apr")) tm.tm_mon = 3;
+	else if (i3("may")) tm.tm_mon = 4;
+	else if (i3("jul")) tm.tm_mon = 5;
+	else if (i3("jun")) tm.tm_mon = 6;
+	else if (i3("aug")) tm.tm_mon = 7;
+	else if (i3("sep")) tm.tm_mon = 8;
+	else if (i3("oct")) tm.tm_mon = 9;
+	else if (i3("nov")) tm.tm_mon = 10;
+	else if (i3("dec")) tm.tm_mon = 11;
+	else goto fail;
+
+	while (iswsp(*s))
+		s++;
+	
+	if ((c = parse_posint(&s, 1000, 9999)) > 0) {
+		tm.tm_year = c - 1900;
+	} else if ((c = parse_posint(&s, 0, 49)) > 0) {
+		tm.tm_year = c + 100;
+	} else if ((c = parse_posint(&s, 50, 99)) > 0) {
+		tm.tm_year = c;
+	} else goto fail;
+
+	while (iswsp(*s))
+		s++;
+
+	if ((c = parse_posint(&s, 0, 24)) < 0) goto fail;
+	tm.tm_hour = c;
+	if (*s++ != ':') goto fail;
+	if ((c = parse_posint(&s, 0, 59)) < 0) goto fail;
+	tm.tm_min = c;
+	if (*s++ == ':') {
+		if ((c = parse_posint(&s, 0, 61)) < 0) goto fail;
+		tm.tm_sec = c;
+	}
+
+	while (iswsp(*s))
+		s++;
+
+	if (*s == '+' || *s == '-') {
+		int neg = (*s == '-');
+		s++;
+		if ((c = parse_posint(&s, 0, 10000)) < 0) goto fail;
+		if (neg) {
+                        tm.tm_hour += c / 100;
+                        tm.tm_min  += c % 100;
+		} else {
+                        tm.tm_hour -= c / 100;
+                        tm.tm_min  -= c % 100;
+                }
+	}
+
+	tm.tm_isdst = -1;
+
+	time_t r = mktime(&tm);
+	return r;
+
+fail:
+	return -1;
+}
+
+char *
+parse_addr(char *s, char **dispo, char **addro)
+{
+	static char disp[1024];
+	static char addr[1024];
+//	char *disp = disp+sizeof disp;
+//	char *addr = addr+sizeof addr;
+	char *c, *e;
+
+	printf("RAW : |%s|\n", s);
+	
+	while (iswsp(*s))
+		s++;
+	
+	c = disp;
+	e = disp + sizeof disp;
+
+	*disp = 0;
+	*addr = 0;
+
+	while (*s) {
+		if (*s == '<') {
+			char *c = addr;
+			char *e = addr + sizeof addr;
+
+			s++;
+			while (*s && c < e && *s != '>')
+				*c++ = *s++;
+			if (*s == '>')
+				s++;
+			*c = 0;
+		} else if (*s == '"') {
+			s++;
+			while (*s && c < e && *s != '"')
+				*c++ = *s++;
+			if (*s == '"')
+				s++;
+		} else if (*s == '(') {
+			s++;
+
+			if (!*addr) {   // assume: user@host (name)
+				*c-- = 0;
+				while (c > disp && iswsp(*c))
+					*c-- = 0;
+				strcpy(addr, disp);
+				c = disp;
+				*c = 0;
+			}
+
+			while (*s && c < e && *s != ')')
+				*c++ = *s++;
+			if (*s == ')')
+				s++;
+		} else if (*s == ',') {
+			s++;
+			break;
+		} else {
+			*c++ = *s++;
+		}
+	}
+
+	*c-- = 0;
+	// strip trailing ws
+	while (c > disp && iswsp(*c))
+		*c-- = 0;
+
+	if (*disp && !*addr && strchr(disp, '@')) {
+		// just mail address was given
+		strcpy(addr, disp);
+		*disp = 0;
+	}
+
+	printf("DISP :: |%s|\n", disp);
+	printf("ADDR :: |%s|\n", addr);
+
+	if (dispo) *dispo = disp;
+	if (addro) *addro = addr;
+
+	return s;
+}
+
+void
+blaze822(char *file)
+{
+	int fd;
+	ssize_t rd;
+	char *buf;
+	ssize_t bufalloc;
+	ssize_t used;
+	char *end;
+
+	fd = open(file, O_RDONLY);
+	if (fd < 0) {
+		perror("open");
+		return;
+	}
+
+	buf = malloc(3);
+	buf[0] = '\n';
+	buf[1] = '\n';
+	buf[2] = '\n';
+	bufalloc = 3;
+	used = 3;
+
+	while (1) {
+		bufalloc += bufsiz;
+		buf = realloc(buf, bufalloc);
+
+		rd = read(fd, buf+used, bufalloc-used);
+		if (rd == 0) {
+			end = buf+used;
+			break;
+		}
+		if (rd < 0)
+			exit(-1);
+
+		if ((end = memmem(buf-1+used, rd+1, "\n\n", 2)) ||
+		    (end = memmem(buf-3+used, rd+3, "\r\n\r\n", 4))) {
+			used += rd;
+			break;
+		}
+
+		used += rd;
+	}
+	close(fd);
+
+	end++;
+	*end = 0;   // dereferencing *end is safe
+
+	char *s;
+	for (s = buf; s < end; s++) {
+		if (*s == 0)   // sanitize nul bytes in headers
+			*s = ' ';
+
+		if (*s == '\r') {
+			if (*(s+1) == '\n') {
+				*s++ = '\n';
+			} else {
+				*s = ' ';
+			}
+		}
+
+		if (iswsp(*s)) {
+			// change prior \n to spaces
+			int j;
+			for (j = 1; s - j >= buf && *(s-j) == '\n'; j++)
+				*(s-j) = ' ';
+		}
+
+		if (*s == '\n') {
+			s++;
+			if (iswsp(*s)) {
+				*(s-1) = ' ';
+			} else {			
+				*(s-1) = 0;
+				if (*(s-2) == '\n')   // ex-crlf
+					*(s-2) = 0;
+				while (s < end && *s != ':') {
+					*s = tolower(*s);
+					s++;
+				}
+			}
+		}
+	}
+
+	buf[0] = 0;
+
+/*
+	for (s = buf; s < end; ) {
+		printf("%s\n", s);
+		s += strlen(s) + 1;
+	}
+*/
+	char *v;
+	if ((v = memmem(buf, end-buf, "\0from:", 6))) {
+		printf("FROM : %s\n", v+6);
+		parse_addr(v+6, 0, 0);
+	}
+	if ((v = memmem(buf, end-buf, "\0to:", 4))) {
+		printf("TO : %s\n", v+4);
+		char *a = v+4;
+		char *disp, *addr;
+		do {
+			a = parse_addr(a, &disp, &addr);
+			printf("DISP: |%s|  ADDR: |%s|\n", disp, addr);
+		} while (*a);
+	}
+	if ((v = memmem(buf, end-buf, "\0date:", 6))) {
+		printf("DATE : %s\n", v+6);
+		time_t t = parse_date(v+6);
+		if (t != -1)
+			printf("DATE :: %s", ctime(&t));
+		else
+			fprintf(stderr, "invalid date: %s\n", v+6);
+	}
+	if ((v = memmem(buf, end-buf, "\0subject:", 9))) {
+		printf("SUBJECT : %s\n", v+9);
+	}
+	
+	free(buf);
+
+	printf("used: %d %d\n", used, end-buf);
+}
+
+int
+main(int argc, char *argv[]) {
+	char *s;
+	
+	char *line = 0;
+	size_t linelen = 0;
+	int read;
+
+	int i = 0;
+
+	if (argc == 1 || (argc == 2 && strcmp(argv[1], "-") == 0)) {
+		while ((read = getdelim(&line, &linelen, '\n', stdin)) != -1) {
+			if (line[read-1] == '\n') line[read-1] = 0;
+			fprintf(stderr, "%s\n", line);
+			blaze822(line);
+			i++;
+		}
+	} else {
+		for (i = 1; i < argc; i++) {
+			fprintf(stderr, "%s\n", argv[i]);
+			blaze822(argv[i]);
+		}
+		i--;
+	}
+
+	printf("%d mails scanned\n", i);
+
+	return 0;
+}
diff --git a/fastls.c b/fastls.c
new file mode 100644
index 0000000..c1111c3
--- /dev/null
+++ b/fastls.c
@@ -0,0 +1,97 @@
+#define _GNU_SOURCE
+#include <dirent.h>     /* Defines DT_* constants */
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+
+#define handle_error(msg)					\
+	do { perror(msg); exit(EXIT_FAILURE); } while (0)
+
+struct linux_dirent64 {
+	ino64_t        d_ino;    /* 64-bit inode number */
+	off64_t        d_off;    /* 64-bit offset to next structure */
+	unsigned short d_reclen; /* Size of this dirent */
+	unsigned char  d_type;   /* File type */
+	char           d_name[]; /* Filename (null-terminated) */
+};
+
+
+#define BUF_SIZE 1024000
+
+void flagsort(char *s) {
+        int i, j;
+	if (!s[0])
+		return;
+	// adapted insertion sort from http://stackoverflow.com/a/2789530
+        for (i = 1; s[i]; i++) {
+                char t = s[i];
+                for (j = i; j >= 1 && t < s[j-1]; j--)
+                        s[j] = s[j-1];
+                s[j] = t;
+        }
+}
+
+int
+main(int argc, char *argv[])
+{
+	int fd, nread;
+	char buf[BUF_SIZE];
+	struct linux_dirent64 *d;
+	int bpos;
+	char d_type;
+	
+	fd = open(argc > 1 ? argv[1] : ".", O_RDONLY | O_DIRECTORY);
+	if (fd == -1)
+		handle_error("open");
+	
+	while (1) {
+		nread = syscall(SYS_getdents64, fd, buf, BUF_SIZE);
+		if (nread == -1)
+			handle_error("getdents64");
+		
+		if (nread == 0)
+			break;
+
+		printf("--------------- nread=%d ---------------\n", nread);
+		printf("inode#    file type  d_reclen  d_off   d_name\n");
+		for (bpos = 0; bpos < nread;) {
+			d = (struct linux_dirent64 *) (buf + bpos);
+			if (d->d_type != DT_REG)
+				goto next;
+			if (d->d_name[0] == '.')
+				goto next;
+			char *flags = strstr(d->d_name, ":2,");
+			if (!flags)
+				goto next;
+//			if (strncmp(d->d_name, "1467836925_0.1439.juno,U=87588,FMD5=7e33429f656f1", strlen("1467836925_0.1439.juno,U=87588,FMD5=7e33429f656f1")-1))
+//				goto next;
+//			printf("%s\n", d->d_name);
+			flagsort(flags+3);
+			printf("%s\n", d->d_name);
+//			if (!strchr(flags, 'S'))
+//				printf("%s\n", d->d_name);
+
+//			    goto next;
+
+/*
+			printf("%-10s ", (d_type == DT_REG) ?  "regular" :
+			    (d_type == DT_DIR) ?  "directory" :
+			    (d_type == DT_FIFO) ? "FIFO" :
+			    (d_type == DT_SOCK) ? "socket" :
+			    (d_type == DT_LNK) ?  "symlink" :
+			    (d_type == DT_BLK) ?  "block dev" :
+			    (d_type == DT_CHR) ?  "char dev" : "???");
+			printf("%4d %10lld  %s\n", d->d_reclen,
+			    (long long) d->d_off, d->d_name);
+*/
+next:
+			bpos += d->d_reclen;
+		}
+	}
+
+	exit(EXIT_SUCCESS);
+}
diff --git a/flags.c b/flags.c
new file mode 100644
index 0000000..fd66951
--- /dev/null
+++ b/flags.c
@@ -0,0 +1,48 @@
+#include <string.h>
+#include <stdio.h>
+#include <stdint.h>
+
+int
+main(int argc, char *argv[])
+{
+	int i;
+	unsigned int j;
+	char *f, *e;
+
+	int8_t flagmod[255] = { 0 };
+
+	for (i = 1; i < argc; i++) {
+		if (*argv[i] == '+') {
+			f = argv[i] + 1;
+			while (*f)
+				flagmod[(int)*f++]++;
+			continue;
+		} else if (*argv[i] == '-') {
+			f = argv[i] + 1;
+			while (*f)
+				flagmod[(int)*f++]--;
+			continue;
+		}
+
+		int8_t flags[255] = { 0 };
+		char flagstr[255];
+		f = e = strstr(argv[i], ":2,");
+
+		if (!f)
+			continue;
+		f += 3;
+		while (*f)
+			flags[(int)*f++] = 1;
+
+		*e = 0;
+		f = flagstr;
+		for (j = 0; j < sizeof flags; j++)
+			if (flags[j] + flagmod[j])
+				*f++ = j;
+		*f = 0;
+
+		printf("%s:2,%s\n", argv[i], flagstr);
+	}
+
+	return 0;
+}
diff --git a/foo822.c b/foo822.c
new file mode 100644
index 0000000..0db82dd
--- /dev/null
+++ b/foo822.c
@@ -0,0 +1,368 @@
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#undef getc
+#define getc getc_unlocked
+
+#ifndef __GLIBC__
+#include "/home/chris/src/musl/src/internal/stdio_impl.h"
+#endif
+
+FILE *input;
+
+enum {
+	EOL = -2,
+	EOH = -3
+};
+
+
+//          WSP            =  SP / HTAB
+#define iswsp(c)  (((c) == ' ' || (c) == '\t'))
+
+// CRLF is \n, LF is \n, just CR is ' ',
+// end of file is EOF, end of line is EOL, end of header is EOH,
+// CRLFCR is EOH, CRLFCRLF is EOH, CRCR is EOH
+int
+mgetch()
+{
+	int c, d;
+
+	switch(c = getc(input)) {
+	case EOF:
+		return EOF;
+	case '\r':
+		d = getc(input);
+		if (d != '\n') {
+			// stray CR
+			ungetc(d, input);
+			return ' ';
+		}
+		/* fallthru */
+	case '\n':
+		d = getc(input);
+		if (d == '\r') {
+			d = getc(input);
+			if (d != '\n' && d != '\r')
+				return d;
+		}
+		if (d == '\n' || d == '\r') {
+			return EOH;
+		} else if (iswsp(d)) {
+			// line continuation
+			return ' ';
+		} else {
+			// end of line
+			ungetc(d, input);
+			return EOL;
+		}
+	default:
+		return c;
+	}
+}
+
+char *
+strip(char *t)
+{
+	char *b, *e;
+	for (b = t; *b && iswsp(*b); b++);
+	for (e = b; *e && !iswsp(*e); e++);
+	*e = 0;
+	return b;
+}
+
+void skipws() {
+	int c;
+	do {
+		c = getc(input);
+	} while (c != EOF && iswsp(c));
+	ungetc(c, input);
+}
+
+char *
+parse_header()
+{
+	static char hdr[1024];
+	static const char *hdrend = hdr + sizeof hdr;
+	int c;
+
+	char *s = hdr;
+	*s = 0;
+	while ((c = mgetch()) > 0 && s < hdrend) {
+		if (c < 0)
+			return 0;
+		if (c == ':') {
+			*s = 0;
+			skipws();
+			return strip(hdr);
+		}
+		*s++ = tolower(c);
+	}
+	return 0;
+}
+
+char *
+parse_value()
+{
+	static char value[1024];
+	static const char *valend = value + sizeof value;
+
+	int c;
+	char *s = value;
+
+	*s = 0;
+
+	while ((c = mgetch()) != EOF && s < valend) {
+		if (c == EOH) {
+			ungetc('\n', input);
+			return value;
+		}
+		if (c == EOL) {
+			*s = 0;
+			return value;
+		}
+		*s++ = c;
+	}
+	return value;
+}
+
+int
+parse_tok(char **t)
+{
+	static char tok[16];
+	static const char *tokend = tok + sizeof tok;
+
+	int c;
+
+	char *s = tok;
+	*s = 0;
+	*t = 0;
+
+	c = mgetch();
+	while (iswsp(c))
+		c = mgetch();
+	while (c > 0 && s < tokend) {
+//		printf("%c/%d ", c, c);
+		if (iswsp(c)) {
+			skipws();
+			*s = 0;
+			*t = tok;
+			break;
+		}
+		else
+			*s++ = c;
+		c = mgetch();
+	}
+	if (c == EOL) {
+		*s = 0;
+		*t = tok;
+	}
+	if (t)
+		*t = strip(tok);
+
+//	printf("TOK |%s|", tok);
+	if (c < 0)
+		return c;
+	else {
+		return 1;
+	}
+}
+
+static long
+parse_posint(char *s, size_t minn, size_t maxn)
+{
+        long n;
+        char *end;
+
+        errno = 0;
+        n = strtol(s, &end, 10);
+        if (errno || *end || n < (long)minn || n > (long)maxn)
+		return -1;
+        return n;
+}
+
+int
+parse_date(time_t *r)
+{
+	char *t;
+	int c;
+
+	struct tm tm;
+	
+	if ((c = parse_tok(&t)) < 0) goto fail;
+	if (strlen(t) == 4 && t[3] == ',')  // ignore day of week
+		if ((c = parse_tok(&t)) < 0) goto fail;
+	
+	if ((c = parse_posint(t, 1, 31)) < 0) goto fail;
+	tm.tm_mday = c;
+
+	if ((c = parse_tok(&t)) < 0) goto fail;
+	// convert to switch
+	if      (strcmp("Jan", t) == 0) tm.tm_mon = 0;
+	else if (strcmp("Feb", t) == 0) tm.tm_mon = 1;
+	else if (strcmp("Mar", t) == 0) tm.tm_mon = 2;
+	else if (strcmp("Apr", t) == 0) tm.tm_mon = 3;
+	else if (strcmp("May", t) == 0) tm.tm_mon = 4;
+	else if (strcmp("Jun", t) == 0) tm.tm_mon = 5;
+	else if (strcmp("Jul", t) == 0) tm.tm_mon = 6;
+	else if (strcmp("Aug", t) == 0) tm.tm_mon = 7;
+	else if (strcmp("Sep", t) == 0) tm.tm_mon = 8;
+	else if (strcmp("Oct", t) == 0) tm.tm_mon = 9;
+	else if (strcmp("Nov", t) == 0) tm.tm_mon = 10;
+	else if (strcmp("Dec", t) == 0) tm.tm_mon = 11;
+	else goto fail;
+
+	if ((c = parse_tok(&t)) < 0) goto fail;
+	if ((c = parse_posint(t, 1000, 9999)) > 0) {
+		tm.tm_year = c - 1900;
+	} else if ((c = parse_posint(t, 0, 49)) > 0) {
+		tm.tm_year = c + 100;
+	} else if ((c = parse_posint(t, 50, 99)) > 0) {
+		tm.tm_year = c;
+	} else goto fail;
+
+	if ((c = parse_tok(&t)) < 0)
+		if (!t) goto fail;
+	if (strlen(t) == 8 && t[2] == ':' && t[5] == ':') {
+		t[2] = t[5] = 0;
+		if ((c = parse_posint(t, 0, 24)) < 0) goto fail;
+		tm.tm_hour = c;
+		if ((c = parse_posint(t+3, 0, 59)) < 0) goto fail;
+		tm.tm_min = c;
+		if ((c = parse_posint(t+6, 0, 61)) < 0) goto fail;
+		tm.tm_sec = c;
+	} else if (strlen(t) == 5 && t[2] == ':') {
+		t[2] = 0;
+		if ((c = parse_posint(t, 0, 24)) < 0) goto fail;
+		tm.tm_hour = c;
+		if ((c = parse_posint(t+3, 0, 59)) < 0) goto fail;
+		tm.tm_min = c;
+		tm.tm_sec = 0;
+	} else {
+		goto fail;
+	}
+
+//	if ((c = parse_tok(&t)) > 0) goto fail;   // expect EOL, EOH, EOF
+	c = parse_tok(&t);
+	if (t && strlen(t) == 5 && (t[0] == '+' || t[0] == '-')) {
+		if ((c = parse_posint(t+1, 0, 10000)) < 0) goto fail;
+                if (t[0] == '+') {
+                        tm.tm_hour -= c / 100;
+                        tm.tm_min  -= c % 100;
+                } else {
+                        tm.tm_hour += c / 100;
+                        tm.tm_min  += c % 100;
+                }
+	}
+	// TODO: parse obs-zone?
+
+	tm.tm_isdst = -1;
+
+	*r = timegm(&tm);
+	return 0;
+
+fail:
+	*r = -1;
+	/* slurp rest of line and leave it */
+        while ((c = mgetch()) > 0)
+		;
+	return c;
+}
+
+int decode_rfc2047 (char *str, char *dst, size_t dstlen);
+
+
+int catmain() {
+	int c;
+
+	input = stdin;
+	while ((c = mgetch())) {
+		if (c > 0)
+			putchar(c);
+		else
+			printf("[%d]\n", c);
+	}
+
+	return 0;
+}
+
+int tmain() {
+	char *s;
+
+	input = stdin;
+	while ((s = parse_header())) {
+		if (strcmp(s, "date") == 0) {
+			time_t t;
+			if (parse_date(&t) >= 0)
+				printf("%s :: (%ld) %s", s, t, ctime(&t));
+			else
+				printf("date : OOPS\n");
+		} else if (strcmp(s, "subject") == 0) {
+			char *v = parse_value();
+			char buf[2048];
+			decode_rfc2047(v, buf, sizeof buf);
+			printf("%s : %s\n", s, v);
+			printf("%s :: %s\n", s, buf);
+		} else {
+			char *v = parse_value();
+			printf("%s : %s\n", s, v);
+		}
+	}
+
+	return 0;
+}
+
+int main() {
+	char *s;
+	
+	char *line = 0;
+	size_t linelen = 0;
+	int read;
+
+	int i = 0;
+	
+	static char buf[50000]; /* buf must survive until stdout is closed */
+
+	while ((read = getdelim(&line, &linelen, '\n', stdin)) != -1) {
+		if (line[read-1] == '\n') line[read-1] = 0;
+
+		printf("%s\n", line);
+
+		FILE *f = fopen(line, "r");
+		if (!f) {
+			perror("fopen");
+			continue;
+		}
+		setvbuf ( f , buf , _IOFBF , sizeof(buf) );
+		i++;
+
+		input = f;
+		while ((s = parse_header())) {
+			if (strcmp(s, "date") == 0) {
+				time_t t;
+				if (parse_date(&t) >= 0)
+					printf("%s :: (%ld) %s", s, t, ctime(&t));
+				else
+					printf("date : OOPS\n");
+			} else if (strcmp(s, "subject") == 0 ||
+			    strcmp(s, "from") == 0 ||
+			    strcmp(s, "to") == 0) {
+				char *v = parse_value();
+				char buf[2048];
+				if (decode_rfc2047(v, buf, sizeof buf))
+					printf("%s :: %s\n", s, buf);
+				else
+					printf("%s : %s\n", s, v);
+			} else
+				parse_value();  // and ignore it
+		}
+		fclose(f);
+	}
+
+	printf("%d mails scanned\n", i);
+
+	return 0;
+}
diff --git a/next.c b/next.c
new file mode 100644
index 0000000..f82e437
--- /dev/null
+++ b/next.c
@@ -0,0 +1,44 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+int
+main(int argc, char *argv[])
+{
+	int fd;
+	struct stat st;
+
+	fd = open("map", O_RDONLY);
+	if (!fd)
+		exit(101);
+
+	fstat(fd, &st);
+	char *map = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+	if (!map)
+		exit(102);
+
+	char *s = strstr(map, argv[1]);
+	if (!argv[1][0] || !s) {
+		// default to first line
+		s = map;
+	} else {
+		while (*s && *s != '\n')
+			s++;
+		s++;
+	}
+	char *t = s;
+	while (*t && *t != '\n')
+		t++;
+	if (!*t)
+		exit(1);
+	t++;
+	write(1, s, t-s);
+
+	return 0;
+}