xref: /original-bsd/usr.bin/mail/head.c (revision 2b713f00)
1ef77d8e9Sdist /*
2ef77d8e9Sdist  * Copyright (c) 1980 Regents of the University of California.
313f94090Sbostic  * All rights reserved.
413f94090Sbostic  *
5*2b713f00Sbostic  * %sccs.include.redist.c%
6ef77d8e9Sdist  */
7ef77d8e9Sdist 
88cab6285Sbostic #ifndef lint
9*2b713f00Sbostic static char sccsid[] = "@(#)head.c	5.7 (Berkeley) 06/01/90";
108cab6285Sbostic #endif /* not lint */
11068bc5fdSkas 
12068bc5fdSkas #include "rcv.h"
13068bc5fdSkas 
14068bc5fdSkas /*
15068bc5fdSkas  * Mail -- a mail program
16068bc5fdSkas  *
17068bc5fdSkas  * Routines for processing and detecting headlines.
18068bc5fdSkas  */
19068bc5fdSkas 
20068bc5fdSkas /*
21068bc5fdSkas  * See if the passed line buffer is a mail header.
22068bc5fdSkas  * Return true if yes.  Note the extreme pains to
23068bc5fdSkas  * accomodate all funny formats.
24068bc5fdSkas  */
25068bc5fdSkas ishead(linebuf)
26068bc5fdSkas 	char linebuf[];
27068bc5fdSkas {
28068bc5fdSkas 	register char *cp;
29068bc5fdSkas 	struct headline hl;
30068bc5fdSkas 	char parbuf[BUFSIZ];
31068bc5fdSkas 
32068bc5fdSkas 	cp = linebuf;
33a1e0f4b8Sedward 	if (*cp++ != 'F' || *cp++ != 'r' || *cp++ != 'o' || *cp++ != 'm' ||
34a1e0f4b8Sedward 	    *cp++ != ' ')
35068bc5fdSkas 		return (0);
36a1e0f4b8Sedward 	parse(linebuf, &hl, parbuf);
37068bc5fdSkas 	if (hl.l_from == NOSTR || hl.l_date == NOSTR) {
38068bc5fdSkas 		fail(linebuf, "No from or date field");
39068bc5fdSkas 		return (0);
40068bc5fdSkas 	}
41068bc5fdSkas 	if (!isdate(hl.l_date)) {
42068bc5fdSkas 		fail(linebuf, "Date field not legal date");
43068bc5fdSkas 		return (0);
44068bc5fdSkas 	}
45068bc5fdSkas 	/*
46068bc5fdSkas 	 * I guess we got it!
47068bc5fdSkas 	 */
48068bc5fdSkas 	return (1);
49068bc5fdSkas }
50068bc5fdSkas 
51a1e0f4b8Sedward /*ARGSUSED*/
52068bc5fdSkas fail(linebuf, reason)
53068bc5fdSkas 	char linebuf[], reason[];
54068bc5fdSkas {
55068bc5fdSkas 
56a1e0f4b8Sedward 	/*
57a1e0f4b8Sedward 	if (value("debug") == NOSTR)
58068bc5fdSkas 		return;
59068bc5fdSkas 	fprintf(stderr, "\"%s\"\nnot a header because %s\n", linebuf, reason);
60a1e0f4b8Sedward 	*/
61068bc5fdSkas }
62068bc5fdSkas 
63068bc5fdSkas /*
64068bc5fdSkas  * Split a headline into its useful components.
65068bc5fdSkas  * Copy the line into dynamic string space, then set
66068bc5fdSkas  * pointers into the copied line in the passed headline
67068bc5fdSkas  * structure.  Actually, it scans.
68068bc5fdSkas  */
69068bc5fdSkas parse(line, hl, pbuf)
70068bc5fdSkas 	char line[], pbuf[];
71a1e0f4b8Sedward 	register struct headline *hl;
72068bc5fdSkas {
73a1e0f4b8Sedward 	register char *cp;
74068bc5fdSkas 	char *sp;
75068bc5fdSkas 	char word[LINESIZE];
76068bc5fdSkas 
77068bc5fdSkas 	hl->l_from = NOSTR;
78068bc5fdSkas 	hl->l_tty = NOSTR;
79068bc5fdSkas 	hl->l_date = NOSTR;
80068bc5fdSkas 	cp = line;
81068bc5fdSkas 	sp = pbuf;
82068bc5fdSkas 	/*
83a1e0f4b8Sedward 	 * Skip over "From" first.
84068bc5fdSkas 	 */
85068bc5fdSkas 	cp = nextword(cp, word);
86a1e0f4b8Sedward 	cp = nextword(cp, word);
87a1e0f4b8Sedward 	if (*word)
88068bc5fdSkas 		hl->l_from = copyin(word, &sp);
89a1e0f4b8Sedward 	if (cp != NOSTR && cp[0] == 't' && cp[1] == 't' && cp[2] == 'y') {
90a1e0f4b8Sedward 		cp = nextword(cp, word);
91068bc5fdSkas 		hl->l_tty = copyin(word, &sp);
92a1e0f4b8Sedward 	}
93068bc5fdSkas 	if (cp != NOSTR)
94068bc5fdSkas 		hl->l_date = copyin(cp, &sp);
95068bc5fdSkas }
96068bc5fdSkas 
97068bc5fdSkas /*
98068bc5fdSkas  * Copy the string on the left into the string on the right
99068bc5fdSkas  * and bump the right (reference) string pointer by the length.
100068bc5fdSkas  * Thus, dynamically allocate space in the right string, copying
101068bc5fdSkas  * the left string into it.
102068bc5fdSkas  */
103068bc5fdSkas char *
104068bc5fdSkas copyin(src, space)
105a1e0f4b8Sedward 	register char *src;
106068bc5fdSkas 	char **space;
107068bc5fdSkas {
108a1e0f4b8Sedward 	register char *cp;
109a1e0f4b8Sedward 	char *top;
110068bc5fdSkas 
111a1e0f4b8Sedward 	top = cp = *space;
112a1e0f4b8Sedward 	while (*cp++ = *src++)
113a1e0f4b8Sedward 		;
114068bc5fdSkas 	*space = cp;
115068bc5fdSkas 	return (top);
116068bc5fdSkas }
117068bc5fdSkas 
118068bc5fdSkas /*
119068bc5fdSkas  * Test to see if the passed string is a ctime(3) generated
120068bc5fdSkas  * date string as documented in the manual.  The template
121068bc5fdSkas  * below is used as the criterion of correctness.
122068bc5fdSkas  * Also, we check for a possible trailing time zone using
123552a793fSedward  * the tmztype template.
124068bc5fdSkas  */
125068bc5fdSkas 
126552a793fSedward /*
127552a793fSedward  * 'A'	An upper case char
128552a793fSedward  * 'a'	A lower case char
129552a793fSedward  * ' '	A space
130552a793fSedward  * '0'	A digit
131552a793fSedward  * 'O'	An optional digit or space
132552a793fSedward  * ':'	A colon
133552a793fSedward  * 'N'	A new line
134552a793fSedward  */
135552a793fSedward char ctype[] = "Aaa Aaa O0 00:00:00 0000";
136552a793fSedward char tmztype[] = "Aaa Aaa O0 00:00:00 AAA 0000";
137068bc5fdSkas 
138068bc5fdSkas isdate(date)
139068bc5fdSkas 	char date[];
140068bc5fdSkas {
141068bc5fdSkas 
142552a793fSedward 	return cmatch(date, ctype) || cmatch(date, tmztype);
143068bc5fdSkas }
144068bc5fdSkas 
145068bc5fdSkas /*
146a1e0f4b8Sedward  * Match the given string (cp) against the given template (tp).
147068bc5fdSkas  * Return 1 if they match, 0 if they don't
148068bc5fdSkas  */
149a1e0f4b8Sedward cmatch(cp, tp)
150068bc5fdSkas 	register char *cp, *tp;
151a1e0f4b8Sedward {
152068bc5fdSkas 
153a1e0f4b8Sedward 	while (*cp && *tp)
154068bc5fdSkas 		switch (*tp++) {
155552a793fSedward 		case 'a':
156a1e0f4b8Sedward 			if (!islower(*cp++))
157a1e0f4b8Sedward 				return 0;
158068bc5fdSkas 			break;
159552a793fSedward 		case 'A':
160a1e0f4b8Sedward 			if (!isupper(*cp++))
161a1e0f4b8Sedward 				return 0;
162068bc5fdSkas 			break;
163552a793fSedward 		case ' ':
164a1e0f4b8Sedward 			if (*cp++ != ' ')
165a1e0f4b8Sedward 				return 0;
166068bc5fdSkas 			break;
167552a793fSedward 		case '0':
168a1e0f4b8Sedward 			if (!isdigit(*cp++))
169a1e0f4b8Sedward 				return 0;
170068bc5fdSkas 			break;
171552a793fSedward 		case 'O':
172a1e0f4b8Sedward 			if (*cp != ' ' && !isdigit(*cp))
173a1e0f4b8Sedward 				return 0;
174a1e0f4b8Sedward 			cp++;
175068bc5fdSkas 			break;
176552a793fSedward 		case ':':
177a1e0f4b8Sedward 			if (*cp++ != ':')
178a1e0f4b8Sedward 				return 0;
179068bc5fdSkas 			break;
180552a793fSedward 		case 'N':
181a1e0f4b8Sedward 			if (*cp++ != '\n')
182a1e0f4b8Sedward 				return 0;
183068bc5fdSkas 			break;
184068bc5fdSkas 		}
185a1e0f4b8Sedward 	if (*cp || *tp)
186a1e0f4b8Sedward 		return 0;
187068bc5fdSkas 	return (1);
188068bc5fdSkas }
189068bc5fdSkas 
190068bc5fdSkas /*
191068bc5fdSkas  * Collect a liberal (space, tab delimited) word into the word buffer
192068bc5fdSkas  * passed.  Also, return a pointer to the next word following that,
193068bc5fdSkas  * or NOSTR if none follow.
194068bc5fdSkas  */
195068bc5fdSkas char *
196068bc5fdSkas nextword(wp, wbuf)
197a1e0f4b8Sedward 	register char *wp, *wbuf;
198068bc5fdSkas {
199068bc5fdSkas 	register c;
200068bc5fdSkas 
201a1e0f4b8Sedward 	if (wp == NOSTR) {
202a1e0f4b8Sedward 		*wbuf = 0;
203a1e0f4b8Sedward 		return (NOSTR);
204a1e0f4b8Sedward 	}
205a1e0f4b8Sedward 	while ((c = *wp++) && c != ' ' && c != '\t') {
206a1e0f4b8Sedward 		*wbuf++ = c;
207a1e0f4b8Sedward 		if (c == '"') {
208a1e0f4b8Sedward  			while ((c = *wp++) && c != '"')
209a1e0f4b8Sedward  				*wbuf++ = c;
210a1e0f4b8Sedward  			if (c == '"')
211a1e0f4b8Sedward  				*wbuf++ = c;
212a1e0f4b8Sedward 			else
213a1e0f4b8Sedward 				wp--;
214a1e0f4b8Sedward  		}
215a1e0f4b8Sedward 	}
216a1e0f4b8Sedward 	*wbuf = '\0';
217a1e0f4b8Sedward 	for (; c == ' ' || c == '\t'; c = *wp++)
218a1e0f4b8Sedward 		;
219a1e0f4b8Sedward 	if (c == 0)
220a1e0f4b8Sedward 		return (NOSTR);
221a1e0f4b8Sedward 	return (wp - 1);
222068bc5fdSkas }
223