xref: /original-bsd/usr.bin/mail/head.c (revision 3a296e00)
1 /*
2  * Copyright (c) 1980 Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)head.c	5.8 (Berkeley) 06/26/92";
10 #endif /* not lint */
11 
12 #include "rcv.h"
13 #include "extern.h"
14 
15 /*
16  * Mail -- a mail program
17  *
18  * Routines for processing and detecting headlines.
19  */
20 
21 /*
22  * See if the passed line buffer is a mail header.
23  * Return true if yes.  Note the extreme pains to
24  * accomodate all funny formats.
25  */
26 int
27 ishead(linebuf)
28 	char linebuf[];
29 {
30 	register char *cp;
31 	struct headline hl;
32 	char parbuf[BUFSIZ];
33 
34 	cp = linebuf;
35 	if (*cp++ != 'F' || *cp++ != 'r' || *cp++ != 'o' || *cp++ != 'm' ||
36 	    *cp++ != ' ')
37 		return (0);
38 	parse(linebuf, &hl, parbuf);
39 	if (hl.l_from == NOSTR || hl.l_date == NOSTR) {
40 		fail(linebuf, "No from or date field");
41 		return (0);
42 	}
43 	if (!isdate(hl.l_date)) {
44 		fail(linebuf, "Date field not legal date");
45 		return (0);
46 	}
47 	/*
48 	 * I guess we got it!
49 	 */
50 	return (1);
51 }
52 
53 /*ARGSUSED*/
54 void
55 fail(linebuf, reason)
56 	char linebuf[], reason[];
57 {
58 
59 	/*
60 	if (value("debug") == NOSTR)
61 		return;
62 	fprintf(stderr, "\"%s\"\nnot a header because %s\n", linebuf, reason);
63 	*/
64 }
65 
66 /*
67  * Split a headline into its useful components.
68  * Copy the line into dynamic string space, then set
69  * pointers into the copied line in the passed headline
70  * structure.  Actually, it scans.
71  */
72 void
73 parse(line, hl, pbuf)
74 	char line[], pbuf[];
75 	register struct headline *hl;
76 {
77 	register char *cp;
78 	char *sp;
79 	char word[LINESIZE];
80 
81 	hl->l_from = NOSTR;
82 	hl->l_tty = NOSTR;
83 	hl->l_date = NOSTR;
84 	cp = line;
85 	sp = pbuf;
86 	/*
87 	 * Skip over "From" first.
88 	 */
89 	cp = nextword(cp, word);
90 	cp = nextword(cp, word);
91 	if (*word)
92 		hl->l_from = copyin(word, &sp);
93 	if (cp != NOSTR && cp[0] == 't' && cp[1] == 't' && cp[2] == 'y') {
94 		cp = nextword(cp, word);
95 		hl->l_tty = copyin(word, &sp);
96 	}
97 	if (cp != NOSTR)
98 		hl->l_date = copyin(cp, &sp);
99 }
100 
101 /*
102  * Copy the string on the left into the string on the right
103  * and bump the right (reference) string pointer by the length.
104  * Thus, dynamically allocate space in the right string, copying
105  * the left string into it.
106  */
107 char *
108 copyin(src, space)
109 	register char *src;
110 	char **space;
111 {
112 	register char *cp;
113 	char *top;
114 
115 	top = cp = *space;
116 	while (*cp++ = *src++)
117 		;
118 	*space = cp;
119 	return (top);
120 }
121 
122 /*
123  * Test to see if the passed string is a ctime(3) generated
124  * date string as documented in the manual.  The template
125  * below is used as the criterion of correctness.
126  * Also, we check for a possible trailing time zone using
127  * the tmztype template.
128  */
129 
130 /*
131  * 'A'	An upper case char
132  * 'a'	A lower case char
133  * ' '	A space
134  * '0'	A digit
135  * 'O'	An optional digit or space
136  * ':'	A colon
137  * 'N'	A new line
138  */
139 char ctype[] = "Aaa Aaa O0 00:00:00 0000";
140 char tmztype[] = "Aaa Aaa O0 00:00:00 AAA 0000";
141 
142 int
143 isdate(date)
144 	char date[];
145 {
146 
147 	return cmatch(date, ctype) || cmatch(date, tmztype);
148 }
149 
150 /*
151  * Match the given string (cp) against the given template (tp).
152  * Return 1 if they match, 0 if they don't
153  */
154 int
155 cmatch(cp, tp)
156 	register char *cp, *tp;
157 {
158 
159 	while (*cp && *tp)
160 		switch (*tp++) {
161 		case 'a':
162 			if (!islower(*cp++))
163 				return 0;
164 			break;
165 		case 'A':
166 			if (!isupper(*cp++))
167 				return 0;
168 			break;
169 		case ' ':
170 			if (*cp++ != ' ')
171 				return 0;
172 			break;
173 		case '0':
174 			if (!isdigit(*cp++))
175 				return 0;
176 			break;
177 		case 'O':
178 			if (*cp != ' ' && !isdigit(*cp))
179 				return 0;
180 			cp++;
181 			break;
182 		case ':':
183 			if (*cp++ != ':')
184 				return 0;
185 			break;
186 		case 'N':
187 			if (*cp++ != '\n')
188 				return 0;
189 			break;
190 		}
191 	if (*cp || *tp)
192 		return 0;
193 	return (1);
194 }
195 
196 /*
197  * Collect a liberal (space, tab delimited) word into the word buffer
198  * passed.  Also, return a pointer to the next word following that,
199  * or NOSTR if none follow.
200  */
201 char *
202 nextword(wp, wbuf)
203 	register char *wp, *wbuf;
204 {
205 	register c;
206 
207 	if (wp == NOSTR) {
208 		*wbuf = 0;
209 		return (NOSTR);
210 	}
211 	while ((c = *wp++) && c != ' ' && c != '\t') {
212 		*wbuf++ = c;
213 		if (c == '"') {
214  			while ((c = *wp++) && c != '"')
215  				*wbuf++ = c;
216  			if (c == '"')
217  				*wbuf++ = c;
218 			else
219 				wp--;
220  		}
221 	}
222 	*wbuf = '\0';
223 	for (; c == ' ' || c == '\t'; c = *wp++)
224 		;
225 	if (c == 0)
226 		return (NOSTR);
227 	return (wp - 1);
228 }
229