xref: /original-bsd/usr.bin/mail/head.c (revision 27393bdf)
1 /*
2  * Copyright (c) 1980, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)head.c	8.2 (Berkeley) 04/20/95";
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  * Yuck.  If the mail file is created by Sys V (Solaris),
143  * there are no seconds in the time...
144  */
145 char SysV_ctype[] = "Aaa Aaa O0 00:00 0000";
146 char SysV_tmztype[] = "Aaa Aaa O0 00:00 AAA 0000";
147 
148 int
149 isdate(date)
150 	char date[];
151 {
152 
153 	return cmatch(date, ctype) || cmatch(date, tmztype)
154 	    || cmatch(date, SysV_tmztype) || cmatch(date, SysV_ctype);
155 }
156 
157 /*
158  * Match the given string (cp) against the given template (tp).
159  * Return 1 if they match, 0 if they don't
160  */
161 int
162 cmatch(cp, tp)
163 	register char *cp, *tp;
164 {
165 
166 	while (*cp && *tp)
167 		switch (*tp++) {
168 		case 'a':
169 			if (!islower(*cp++))
170 				return 0;
171 			break;
172 		case 'A':
173 			if (!isupper(*cp++))
174 				return 0;
175 			break;
176 		case ' ':
177 			if (*cp++ != ' ')
178 				return 0;
179 			break;
180 		case '0':
181 			if (!isdigit(*cp++))
182 				return 0;
183 			break;
184 		case 'O':
185 			if (*cp != ' ' && !isdigit(*cp))
186 				return 0;
187 			cp++;
188 			break;
189 		case ':':
190 			if (*cp++ != ':')
191 				return 0;
192 			break;
193 		case 'N':
194 			if (*cp++ != '\n')
195 				return 0;
196 			break;
197 		}
198 	if (*cp || *tp)
199 		return 0;
200 	return (1);
201 }
202 
203 /*
204  * Collect a liberal (space, tab delimited) word into the word buffer
205  * passed.  Also, return a pointer to the next word following that,
206  * or NOSTR if none follow.
207  */
208 char *
209 nextword(wp, wbuf)
210 	register char *wp, *wbuf;
211 {
212 	register c;
213 
214 	if (wp == NOSTR) {
215 		*wbuf = 0;
216 		return (NOSTR);
217 	}
218 	while ((c = *wp++) && c != ' ' && c != '\t') {
219 		*wbuf++ = c;
220 		if (c == '"') {
221  			while ((c = *wp++) && c != '"')
222  				*wbuf++ = c;
223  			if (c == '"')
224  				*wbuf++ = c;
225 			else
226 				wp--;
227  		}
228 	}
229 	*wbuf = '\0';
230 	for (; c == ' ' || c == '\t'; c = *wp++)
231 		;
232 	if (c == 0)
233 		return (NOSTR);
234 	return (wp - 1);
235 }
236