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