]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/mail/head.c
bhyvectl(8): Normalize the man page date
[FreeBSD/FreeBSD.git] / usr.bin / mail / head.c
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1980, 1993
5  *      The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31
32 #ifndef lint
33 #if 0
34 static char sccsid[] = "@(#)head.c      8.2 (Berkeley) 4/20/95";
35 #endif
36 #endif /* not lint */
37 #include <sys/cdefs.h>
38 __FBSDID("$FreeBSD$");
39
40 #include "rcv.h"
41 #include "extern.h"
42
43 /*
44  * Mail -- a mail program
45  *
46  * Routines for processing and detecting headlines.
47  */
48
49 /*
50  * See if the passed line buffer is a mail header.
51  * Return true if yes.  Note the extreme pains to
52  * accommodate all funny formats.
53  */
54 int
55 ishead(char linebuf[])
56 {
57         struct headline hl;
58         char parbuf[BUFSIZ];
59
60         if (strncmp(linebuf, "From ", 5) != 0)
61                 return (0);
62         parse(linebuf, &hl, parbuf);
63         if (hl.l_date == NULL) {
64                 fail(linebuf, "No date field");
65                 return (0);
66         }
67         if (!isdate(hl.l_date)) {
68                 fail(linebuf, "Date field not legal date");
69                 return (0);
70         }
71         /*
72          * I guess we got it!
73          */
74         return (1);
75 }
76
77 void
78 fail(const char *linebuf __unused, const char *reason __unused)
79 {
80
81         /*
82         if (value("debug") == NULL)
83                 return;
84         fprintf(stderr, "\"%s\"\nnot a header because %s\n", linebuf, reason);
85         */
86 }
87
88 /*
89  * Split a headline into its useful components.
90  * Copy the line into dynamic string space, then set
91  * pointers into the copied line in the passed headline
92  * structure.  Actually, it scans.
93  */
94 void
95 parse(char line[], struct headline *hl, char pbuf[])
96 {
97         char *cp, *sp;
98         char word[LINESIZE];
99
100         hl->l_from = NULL;
101         hl->l_tty = NULL;
102         hl->l_date = NULL;
103         cp = line;
104         sp = pbuf;
105         /*
106          * Skip over "From" first.
107          */
108         cp = nextword(cp, word);
109         /*
110          * Check for missing return-path.
111          */
112         if (isdate(cp)) {
113                 hl->l_date = copyin(cp, &sp);
114                 return;
115         }
116         cp = nextword(cp, word);
117         if (strlen(word) > 0)
118                 hl->l_from = copyin(word, &sp);
119         if (cp != NULL && strncmp(cp, "tty", 3) == 0) {
120                 cp = nextword(cp, word);
121                 hl->l_tty = copyin(word, &sp);
122         }
123         if (cp != NULL)
124                 hl->l_date = copyin(cp, &sp);
125 }
126
127 /*
128  * Copy the string on the left into the string on the right
129  * and bump the right (reference) string pointer by the length.
130  * Thus, dynamically allocate space in the right string, copying
131  * the left string into it.
132  */
133 char *
134 copyin(char *src, char **space)
135 {
136         char *cp, *top;
137
138         top = cp = *space;
139         while ((*cp++ = *src++) != '\0')
140                 ;
141         *space = cp;
142         return (top);
143 }
144
145 /*
146  * Test to see if the passed string is a ctime(3) generated
147  * date string as documented in the manual.  The template
148  * below is used as the criterion of correctness.
149  * Also, we check for a possible trailing time zone using
150  * the tmztype template.
151  *
152  * If the mail file is created by Sys V (Solaris), there are
153  * no seconds in the time. If the mail is created by another
154  * program such as imapd, it might have timezone as
155  * <-|+>nnnn (-0800 for instance) at the end.
156  */
157
158 /*
159  * 'A'  An upper case char
160  * 'a'  A lower case char
161  * ' '  A space
162  * '0'  A digit
163  * 'O'  A digit or space
164  * 'p'  A punctuation char
165  * 'P'  A punctuation char or space
166  * ':'  A colon
167  * 'N'  A new line
168  */
169
170 static char *date_formats[] = {
171         "Aaa Aaa O0 00:00:00 0000",        /* Mon Jan 01 23:59:59 2001 */
172         "Aaa Aaa O0 00:00:00 AAA 0000",    /* Mon Jan 01 23:59:59 PST 2001 */
173         "Aaa Aaa O0 00:00:00 0000 p0000",  /* Mon Jan 01 23:59:59 2001 -0800 */
174         "Aaa Aaa O0 00:00 0000",           /* Mon Jan 01 23:59 2001 */
175         "Aaa Aaa O0 00:00 AAA 0000",       /* Mon Jan 01 23:59 PST 2001 */
176         "Aaa Aaa O0 00:00 0000 p0000",     /* Mon Jan 01 23:59 2001 -0800 */
177         NULL
178 };
179
180 int
181 isdate(char date[])
182 {
183         int i;
184
185         for(i = 0; date_formats[i] != NULL; i++) {
186                 if (cmatch(date, date_formats[i]))
187                         return (1);
188         }
189         return (0);
190 }
191
192 /*
193  * Match the given string (cp) against the given template (tp).
194  * Return 1 if they match, 0 if they don't
195  */
196 int
197 cmatch(char *cp, char *tp)
198 {
199
200         while (*cp != '\0' && *tp != '\0')
201                 switch (*tp++) {
202                 case 'a':
203                         if (!islower((unsigned char)*cp++))
204                                 return (0);
205                         break;
206                 case 'A':
207                         if (!isupper((unsigned char)*cp++))
208                                 return (0);
209                         break;
210                 case ' ':
211                         if (*cp++ != ' ')
212                                 return (0);
213                         break;
214                 case '0':
215                         if (!isdigit((unsigned char)*cp++))
216                                 return (0);
217                         break;
218                 case 'O':
219                         if (*cp != ' ' && !isdigit((unsigned char)*cp))
220                                 return (0);
221                         cp++;
222                         break;
223                 case 'p':
224                         if (!ispunct((unsigned char)*cp++))
225                                 return (0);
226                         break;
227                 case 'P':
228                         if (*cp != ' ' && !ispunct((unsigned char)*cp))
229                                 return (0);
230                         cp++;
231                         break;
232                 case ':':
233                         if (*cp++ != ':')
234                                 return (0);
235                         break;
236                 case 'N':
237                         if (*cp++ != '\n')
238                                 return (0);
239                         break;
240                 }
241         if (*cp != '\0' || *tp != '\0')
242                 return (0);
243         return (1);
244 }
245
246 /*
247  * Collect a liberal (space, tab delimited) word into the word buffer
248  * passed.  Also, return a pointer to the next word following that,
249  * or NULL if none follow.
250  */
251 char *
252 nextword(char *wp, char *wbuf)
253 {
254         int c;
255
256         if (wp == NULL) {
257                 *wbuf = '\0';
258                 return (NULL);
259         }
260         while ((c = *wp++) != '\0' && c != ' ' && c != '\t') {
261                 *wbuf++ = c;
262                 if (c == '"') {
263                         while ((c = *wp++) != '\0' && c != '"')
264                                 *wbuf++ = c;
265                         if (c == '"')
266                                 *wbuf++ = c;
267                         else
268                                 wp--;
269                 }
270         }
271         *wbuf = '\0';
272         for (; c == ' ' || c == '\t'; c = *wp++)
273                 ;
274         if (c == '\0')
275                 return (NULL);
276         return (wp - 1);
277 }