Implement the sendmail -t command-line option: parse the message itself
to obtain the recipient addresses.

--- a/dma.8
+++ b/dma.8
@@ -39,7 +39,7 @@
 .Nd DragonFly Mail Agent
 .Sh SYNOPSIS
 .Nm
-.Op Fl DiO
+.Op Fl DiOt
 .Op Fl A Ar mode
 .Op Fl b Ar mode
 .Op Fl f Ar sender
@@ -102,6 +102,8 @@
 .It Fl r Ar sender
 Same as
 .Fl f .
+.It Fl t
+Parse the message to obtain the recipient addresses.
 .El
 .Sh CONFIGURATION
 .Nm
--- a/dma.c
+++ b/dma.c
@@ -77,6 +77,7 @@
 struct config *config;
 static const char *username;
 static uid_t uid;
+static FILE *scanfile;
 static struct strlist seenmsg[16][16];
 
 static int open_locked(const char *, int);
@@ -397,11 +398,307 @@
 }
 
 static int
+parse_header_line(struct queue *queue, const char *sender, const char *line)
+{
+	size_t len;
+	const char *p;
+	enum dma_header_state st;
+	char buf[1000];
+	size_t pos;
+	int yet;
+
+	p = line;
+	if (!strncasecmp(p, "To:", 3) || !strncasecmp(p, "Cc:", 3))
+		p += 3;
+	else if (!strncasecmp(line, "Bcc:", 4))
+		p += 4;
+	else
+		return (0);
+
+	yet = 1;
+	for (st = DHT_START, pos = 0; yet; p++) {
+		if (pos >= sizeof(buf)) {
+			warnx("Name or address too long");
+			return (-1);
+		}
+		switch (st) {
+			case DHT_START:
+				switch (*p) {
+					case '\0':
+						yet = 0;
+						break;
+
+					case ' ':
+					case '\t':
+						break;
+
+					case '<':
+						st = DHT_ADDR;
+						break;
+
+					case '"':
+						st = DHT_QUOTE_NAME_OR_ADDR;
+						break;
+
+					case '>':
+					case ',':
+					case ';':
+						return (-1);
+
+					case '\\':
+						p++;
+						if (*p == '\0') {
+							warnx("Backslash followed by nothing");
+							return (-1);
+						}
+						/* FALLTHROUGH */
+					default:
+						buf[pos++] = *p;
+						st = DHT_NAME_OR_ADDR;
+						break;
+				}
+				break;
+
+			case DHT_NAME_OR_ADDR:
+				switch (*p) {
+					case '\0':
+						yet = 0;
+						/* FALLTHROUGH */
+					case ',':
+					case ';':
+						buf[pos] = '\0';
+						while (pos > 0 &&
+						    (buf[pos - 1] == ' ' ||
+						     buf[pos - 1] == '\t'))
+							buf[--pos] = '\0';
+						if (pos == 0)
+							errx(1, "invalid empty recipient");
+						if (add_recp(queue, buf, sender, 1) != 0)
+							errx(1, "invalid recipient `%s'\n", buf);
+						pos = 0;
+						st = DHT_START;
+						break;
+
+					case ':':
+						pos = 0;
+						st = DHT_START;
+						break;
+
+					case '<':
+						pos = 0;
+						st = DHT_ADDR;
+						break;
+
+					case '"':
+						st = DHT_QUOTE_NAME_OR_ADDR;
+						break;
+
+					case '\\':
+						p++;
+						if (*p == '\0') {
+							warnx("Backslash followed by nothing");
+							return (-1);
+						}
+						/* FALLTHROUGH */
+					default:
+						buf[pos++] = *p;
+						break;
+				}
+				break;
+
+			case DHT_QUOTE_NAME_OR_ADDR:
+				switch (*p) {
+					case '\0':
+						warnx("Unterminated quoted string in address");
+						return (-1);
+						break;
+
+					case '"':
+						st = DHT_NAME_OR_ADDR;
+						break;
+
+					case '\\':
+						p++;
+						if (*p == '\0') {
+							warnx("Backslash followed by nothing");
+							return (-1);
+						}
+						/* FALLTHROUGH */
+					default:
+						buf[pos++] = *p;
+						break;
+				}
+				break;
+
+			case DHT_ADDR:
+				switch (*p) {
+					case '\0':
+						yet = 0;
+						/* FALLTHROUGH */
+					case '>':
+						buf[pos] = '\0';
+						while (pos > 0 &&
+						    (buf[pos - 1] == ' ' ||
+						     buf[pos - 1] == '\t'))
+							buf[--pos] = '\0';
+						if (pos == 0)
+							errx(1, "invalid empty recipient");
+						if (add_recp(queue, buf, sender, 1) != 0)
+							errx(1, "invalid recipient `%s'\n", buf);
+						st = DHT_END;
+						break;
+
+					case '"':
+						st = DHT_QUOTE_ADDR;
+						break;
+
+					case '\\':
+						p++;
+						if (*p == '\0') {
+							warnx("Backslash followed by nothing");
+							return (-1);
+						}
+						/* FALLTHROUGH */
+					default:
+						buf[pos++] = *p;
+						break;
+				}
+				break;
+
+			case DHT_QUOTE_ADDR:
+				switch (*p) {
+					case '\0':
+						warnx("Unterminated quoted string in address");
+						return (-1);
+						break;
+
+					case '"':
+						st = DHT_ADDR;
+						break;
+
+					case '\\':
+						p++;
+						if (*p == '\0') {
+							warnx("Backslash followed by nothing");
+							return (-1);
+						}
+						/* FALLTHROUGH */
+					default:
+						buf[pos++] = *p;
+						break;
+				}
+				break;
+
+			case DHT_END:
+				switch (*p) {
+					case '\0':
+						yet = 0;
+						break;
+
+					case ' ':
+					case '\t':
+						break;
+
+					case ',':
+					case ';':
+						pos = 0;
+						st = DHT_START;
+						break;
+
+					default:
+						warnx("Garbage after address");
+						return (-1);
+				}
+				break;
+
+			default:
+				errx(1, "Internal error: parse_header_line(): unexpected state %d", st);
+		}
+	}
+	return (0);
+}
+
+static int
+build_header_line(struct queue *queue, const char *sender, const char *line, int *inheader)
+{
+	static char *buf = NULL;
+	static size_t pos = 0, alloc = 0;
+	char *nbuf;
+	size_t len, nalloc;
+
+	len = strlen(line);
+	while (len > 0 && (line[len - 1] == '\r' || line[len - 1] == '\n'))
+		len--;
+	if (pos > 0 && (len == 0 || (line[0] != ' ' && line[0] != '\t'))) {
+		if (parse_header_line(queue, sender, buf) == -1)
+			warnx("Could not extract recipients from: %s", buf);
+		pos = 0;
+	}
+	if (len == 0) {
+		*inheader = 0;
+		return (0);
+	}
+
+	/* Always reserve +1 for the null terminator. */
+	if (pos + len + 1 > alloc) {
+		nalloc = alloc + BUF_SIZE;
+		while (pos + len + 1 > nalloc)
+			nalloc += BUF_SIZE;
+		nbuf = realloc(buf, nalloc);
+		if (nbuf == NULL) {
+			warnx("Could not allocate %lu bytes of memory",
+			    (unsigned long)nalloc);
+			return (-1);
+		}
+		buf = nbuf;
+		alloc = nalloc;
+	}
+	memcpy(buf + pos, line, len);
+	pos += len;
+	buf[pos] = '\0';
+	return (0);
+}
+
+static int
+scanmail(struct queue *queue, const char *sender)
+{
+	struct queue qtmp;
+	FILE *fp;
+	int inheader;
+	char line[1000];
+	size_t n;
+
+	if (gentempf(&qtmp) != 0)
+		return (-1);
+	if ((fp = fdopen(qtmp.mailfd, "r+")) == NULL)
+		return (-1);
+	inheader = 1;
+	while (inheader && fgets(line, sizeof(line), stdin) != NULL) {
+		if (build_header_line(queue, sender, line, &inheader) == -1)
+			return (-1);
+		if (fputs(line, fp) == EOF)
+			return (-1);
+	}
+	if (ferror(stdin) || ferror(fp))
+		return (-1);
+	while ((n = fread(line, 1, sizeof(line), stdin)) > 0)
+		if (fwrite(line, n, 1, fp) != 1)
+			return (-1);
+	if (ferror(stdin) || ferror(fp))
+		return (-1);
+	if (fseek(fp, 0, SEEK_SET) != 0)
+		return (-1);
+	scanfile = fp;
+	return (0);
+}
+
+static int
 readmail(struct queue *queue, const char *sender, int nodot)
 {
 	char line[1000];	/* by RFC2822 */
 	size_t linelen;
 	int error;
+	FILE *infile;
 
 	error = snprintf(line, sizeof(line), "\
 Received: from %s (uid %d)\n\
@@ -419,8 +716,12 @@
 	if (write(queue->mailfd, line, error) != error)
 		return (-1);
 
-	while (!feof(stdin)) {
-		if (fgets(line, sizeof(line), stdin) == NULL)
+	if (scanfile != NULL)
+		infile = scanfile;
+	else
+		infile = stdin;
+	while (!feof(infile)) {
+		if (fgets(line, sizeof(line), infile) == NULL)
 			break;
 		linelen = strlen(line);
 		if (linelen == 0 || line[linelen - 1] != '\n') {
@@ -434,6 +735,8 @@
 	}
 	if (fsync(queue->mailfd) != 0)
 		return (-1);
+	if (scanfile != NULL)
+		fclose(scanfile);
 	return (0);
 }
 
@@ -1165,14 +1468,14 @@
 	struct queue queue;
 	struct queue lqueue;
 	int i, ch;
-	int nodot = 0, doqueue = 0, showq = 0;
+	int nodot = 0, doqueue = 0, showq = 0, headerrecips = 0;
 
 	atexit(deltmp);
 	LIST_INIT(&queue.queue);
 	snprintf(tag, 254, "dma");
 
 	opterr = 0;
-	while ((ch = getopt(argc, argv, "A:b:B:C:d:Df:F:h:iL:N:no:O:q:r:R:UV:vX:")) != -1) {
+	while ((ch = getopt(argc, argv, "A:b:B:C:d:Df:F:h:iL:N:no:O:q:r:R:tUV:vX:")) != -1) {
 		switch (ch) {
 		case 'A':
 			/* -AX is being ignored, except for -A{c,m} */
@@ -1214,6 +1517,10 @@
 			doqueue = 1;
 			break;
 
+		case 't':
+			headerrecips = 1;
+			break;
+
 		/* Ignored options */
 		case 'B':
 		case 'C':
@@ -1286,12 +1593,19 @@
 			errx(1, "invalid recipient `%s'\n", argv[i]);
 	}
 
-	if (LIST_EMPTY(&queue.queue))
+	if (LIST_EMPTY(&queue.queue) && !headerrecips)
 		errx(1, "no recipients");
 
 	if (gentempf(&queue) != 0)
 		err(1, "create temp file");
 
+	if (headerrecips) {
+		if (scanmail(&queue, sender) != 0)
+			err(1, "scan message for recipients");
+		if (LIST_EMPTY(&queue.queue))
+			errx(1, "no recipients");
+	}
+
 	if (preparespool(&queue, sender) != 0)
 		err(1, "creating spools (1)");
 
--- a/dma.h
+++ b/dma.h
@@ -142,6 +142,14 @@
 };
 SLIST_HEAD(authusers, authuser);
 
+enum dma_header_state {
+	DHT_START,
+	DHT_NAME_OR_ADDR, DHT_QUOTE_NAME_OR_ADDR,
+	DHT_ADDR, DHT_QUOTE_ADDR,
+	DHT_END,
+	DHT_FINAL
+};
+
 extern struct aliases aliases;
 
 extern char neterr[BUF_SIZE];
