| 1 | # |
| 2 | |
| 3 | #include "rcv.h" |
| 4 | |
| 5 | /* |
| 6 | * Mail -- a mail program |
| 7 | * |
| 8 | * Routines for processing and detecting headlines. |
| 9 | */ |
| 10 | |
| 11 | static char *SccsId = "@(#)head.c 2.1 %G%"; |
| 12 | |
| 13 | /* |
| 14 | * See if the passed line buffer is a mail header. |
| 15 | * Return true if yes. Note the extreme pains to |
| 16 | * accomodate all funny formats. |
| 17 | */ |
| 18 | |
| 19 | ishead(linebuf) |
| 20 | char linebuf[]; |
| 21 | { |
| 22 | register char *cp; |
| 23 | struct headline hl; |
| 24 | char parbuf[BUFSIZ]; |
| 25 | |
| 26 | cp = linebuf; |
| 27 | if (!isname("From ", cp, 5)) |
| 28 | return(0); |
| 29 | parse(cp, &hl, parbuf); |
| 30 | if (hl.l_from == NOSTR || hl.l_date == NOSTR) { |
| 31 | fail(linebuf, "No from or date field"); |
| 32 | return(0); |
| 33 | } |
| 34 | if (!isdate(hl.l_date)) { |
| 35 | fail(linebuf, "Date field not legal date"); |
| 36 | return(0); |
| 37 | } |
| 38 | |
| 39 | /* |
| 40 | * I guess we got it! |
| 41 | */ |
| 42 | |
| 43 | return(1); |
| 44 | } |
| 45 | |
| 46 | fail(linebuf, reason) |
| 47 | char linebuf[], reason[]; |
| 48 | { |
| 49 | |
| 50 | if (1 /*value("debug") == NOSTR*/) |
| 51 | return; |
| 52 | fprintf(stderr, "\"%s\"\nnot a header because %s\n", linebuf, reason); |
| 53 | } |
| 54 | |
| 55 | /* |
| 56 | * Split a headline into its useful components. |
| 57 | * Copy the line into dynamic string space, then set |
| 58 | * pointers into the copied line in the passed headline |
| 59 | * structure. Actually, it scans. |
| 60 | */ |
| 61 | |
| 62 | parse(line, hl, pbuf) |
| 63 | char line[], pbuf[]; |
| 64 | struct headline *hl; |
| 65 | { |
| 66 | register char *cp, *dp; |
| 67 | char *sp; |
| 68 | char word[LINESIZE]; |
| 69 | |
| 70 | hl->l_from = NOSTR; |
| 71 | hl->l_tty = NOSTR; |
| 72 | hl->l_date = NOSTR; |
| 73 | cp = line; |
| 74 | sp = pbuf; |
| 75 | |
| 76 | /* |
| 77 | * Skip the first "word" of the line, which should be "From" |
| 78 | * anyway. |
| 79 | */ |
| 80 | |
| 81 | cp = nextword(cp, word); |
| 82 | dp = nextword(cp, word); |
| 83 | if (!equal(word, "")) |
| 84 | hl->l_from = copyin(word, &sp); |
| 85 | if (isname(dp, "tty", 3)) { |
| 86 | cp = nextword(dp, word); |
| 87 | hl->l_tty = copyin(word, &sp); |
| 88 | if (cp != NOSTR) |
| 89 | hl->l_date = copyin(cp, &sp); |
| 90 | } |
| 91 | else |
| 92 | if (dp != NOSTR) |
| 93 | hl->l_date = copyin(dp, &sp); |
| 94 | } |
| 95 | |
| 96 | /* |
| 97 | * Copy the string on the left into the string on the right |
| 98 | * and bump the right (reference) string pointer by the length. |
| 99 | * Thus, dynamically allocate space in the right string, copying |
| 100 | * the left string into it. |
| 101 | */ |
| 102 | |
| 103 | char * |
| 104 | copyin(src, space) |
| 105 | char src[]; |
| 106 | char **space; |
| 107 | { |
| 108 | register char *cp, *top; |
| 109 | register int s; |
| 110 | |
| 111 | s = strlen(src); |
| 112 | cp = *space; |
| 113 | top = cp; |
| 114 | strcpy(cp, src); |
| 115 | cp += s + 1; |
| 116 | *space = cp; |
| 117 | return(top); |
| 118 | } |
| 119 | |
| 120 | /* |
| 121 | * See if the two passed strings agree in the first n characters. |
| 122 | * Return true if they do, gnu. |
| 123 | */ |
| 124 | |
| 125 | isname(as1, as2, acount) |
| 126 | char *as1, *as2; |
| 127 | { |
| 128 | register char *s1, *s2; |
| 129 | register count; |
| 130 | |
| 131 | s1 = as1; |
| 132 | s2 = as2; |
| 133 | count = acount; |
| 134 | if (count > 0) |
| 135 | do |
| 136 | if (*s1++ != *s2++) |
| 137 | return(0); |
| 138 | while (--count); |
| 139 | return(1); |
| 140 | } |
| 141 | |
| 142 | /* |
| 143 | * See if the two passed strings agree in the first n characters. |
| 144 | * Return true if they do, ignoring case. |
| 145 | */ |
| 146 | |
| 147 | icisname(as1, as2, acount) |
| 148 | char *as1, *as2; |
| 149 | { |
| 150 | register char *s1, *s2; |
| 151 | register count; |
| 152 | |
| 153 | s1 = as1; |
| 154 | s2 = as2; |
| 155 | count = acount; |
| 156 | if (count > 0) |
| 157 | do |
| 158 | if (raise(*s1++) != raise(*s2++)) |
| 159 | return(0); |
| 160 | while (--count); |
| 161 | return(1); |
| 162 | } |
| 163 | |
| 164 | /* |
| 165 | * Test to see if the passed string is a ctime(3) generated |
| 166 | * date string as documented in the manual. The template |
| 167 | * below is used as the criterion of correctness. |
| 168 | * Also, we check for a possible trailing time zone using |
| 169 | * the auxtype template. |
| 170 | */ |
| 171 | |
| 172 | #define L 1 /* A lower case char */ |
| 173 | #define S 2 /* A space */ |
| 174 | #define D 3 /* A digit */ |
| 175 | #define O 4 /* An optional digit or space */ |
| 176 | #define C 5 /* A colon */ |
| 177 | #define N 6 /* A new line */ |
| 178 | #define U 7 /* An upper case char */ |
| 179 | |
| 180 | char ctypes[] = {U,L,L,S,U,L,L,S,O,D,S,D,D,C,D,D,C,D,D,S,D,D,D,D,0}; |
| 181 | char tmztypes[] = {U,L,L,S,U,L,L,S,O,D,S,D,D,C,D,D,C,D,D,S,U,U,U,S,D,D,D,D,0}; |
| 182 | |
| 183 | isdate(date) |
| 184 | char date[]; |
| 185 | { |
| 186 | register char *cp; |
| 187 | |
| 188 | cp = date; |
| 189 | if (cmatch(cp, ctypes)) |
| 190 | return(1); |
| 191 | return(cmatch(cp, tmztypes)); |
| 192 | } |
| 193 | |
| 194 | /* |
| 195 | * Match the given string against the given template. |
| 196 | * Return 1 if they match, 0 if they don't |
| 197 | */ |
| 198 | |
| 199 | cmatch(str, temp) |
| 200 | char str[], temp[]; |
| 201 | { |
| 202 | register char *cp, *tp; |
| 203 | register int c; |
| 204 | |
| 205 | cp = str; |
| 206 | tp = temp; |
| 207 | while (*cp != '\0' && *tp != 0) { |
| 208 | c = *cp++; |
| 209 | switch (*tp++) { |
| 210 | case L: |
| 211 | if (c < 'a' || c > 'z') |
| 212 | return(0); |
| 213 | break; |
| 214 | |
| 215 | case U: |
| 216 | if (c < 'A' || c > 'Z') |
| 217 | return(0); |
| 218 | break; |
| 219 | |
| 220 | case S: |
| 221 | if (c != ' ') |
| 222 | return(0); |
| 223 | break; |
| 224 | |
| 225 | case D: |
| 226 | if (!isdigit(c)) |
| 227 | return(0); |
| 228 | break; |
| 229 | |
| 230 | case O: |
| 231 | if (c != ' ' && !isdigit(c)) |
| 232 | return(0); |
| 233 | break; |
| 234 | |
| 235 | case C: |
| 236 | if (c != ':') |
| 237 | return(0); |
| 238 | break; |
| 239 | |
| 240 | case N: |
| 241 | if (c != '\n') |
| 242 | return(0); |
| 243 | break; |
| 244 | } |
| 245 | } |
| 246 | if (*cp != '\0' || *tp != 0) |
| 247 | return(0); |
| 248 | return(1); |
| 249 | } |
| 250 | |
| 251 | /* |
| 252 | * Collect a liberal (space, tab delimited) word into the word buffer |
| 253 | * passed. Also, return a pointer to the next word following that, |
| 254 | * or NOSTR if none follow. |
| 255 | */ |
| 256 | |
| 257 | char * |
| 258 | nextword(wp, wbuf) |
| 259 | char wp[], wbuf[]; |
| 260 | { |
| 261 | register char *cp, *cp2; |
| 262 | |
| 263 | if ((cp = wp) == NOSTR) { |
| 264 | copy("", wbuf); |
| 265 | return(NOSTR); |
| 266 | } |
| 267 | cp2 = wbuf; |
| 268 | while (!any(*cp, " \t") && *cp != '\0') |
| 269 | *cp2++ = *cp++; |
| 270 | *cp2 = '\0'; |
| 271 | while (any(*cp, " \t")) |
| 272 | cp++; |
| 273 | if (*cp == '\0') |
| 274 | return(NOSTR); |
| 275 | return(cp); |
| 276 | } |
| 277 | |
| 278 | /* |
| 279 | * Test to see if the character is an ascii alphabetic. |
| 280 | */ |
| 281 | |
| 282 | isalpha(c) |
| 283 | { |
| 284 | register int ch; |
| 285 | |
| 286 | ch = raise(c); |
| 287 | return(ch >= 'A' && ch <= 'Z'); |
| 288 | } |
| 289 | |
| 290 | /* |
| 291 | * Test to see if the character is an ascii digit. |
| 292 | */ |
| 293 | |
| 294 | isdigit(c) |
| 295 | { |
| 296 | return(c >= '0' && c <= '9'); |
| 297 | } |
| 298 | |
| 299 | /* |
| 300 | * Copy str1 to str2, return pointer to null in str2. |
| 301 | */ |
| 302 | |
| 303 | char * |
| 304 | copy(str1, str2) |
| 305 | char *str1, *str2; |
| 306 | { |
| 307 | register char *s1, *s2; |
| 308 | |
| 309 | s1 = str1; |
| 310 | s2 = str2; |
| 311 | while (*s1) |
| 312 | *s2++ = *s1++; |
| 313 | *s2 = 0; |
| 314 | return(s2); |
| 315 | } |
| 316 | |
| 317 | /* |
| 318 | * Is ch any of the characters in str? |
| 319 | */ |
| 320 | |
| 321 | any(ch, str) |
| 322 | char *str; |
| 323 | { |
| 324 | register char *f; |
| 325 | register c; |
| 326 | |
| 327 | f = str; |
| 328 | c = ch; |
| 329 | while (*f) |
| 330 | if (c == *f++) |
| 331 | return(1); |
| 332 | return(0); |
| 333 | } |
| 334 | |
| 335 | /* |
| 336 | * Convert lower case letters to upper case. |
| 337 | */ |
| 338 | |
| 339 | raise(c) |
| 340 | register int c; |
| 341 | { |
| 342 | if (c >= 'a' && c <= 'z') |
| 343 | c += 'A' - 'a'; |
| 344 | return(c); |
| 345 | } |