BSD 4_3 release
[unix-history] / usr / src / ucb / sendbug / unixtomh.c
CommitLineData
ea7815ad 1#ifndef lint
95f51977
C
2static char SccsId[] = "@(#)unixtomh.c 1.1 5/16/85";
3#endif
ea7815ad
KB
4
5/*
6 * This program copies the mail file in standard unix format
7 * given as $1 to the file $2 in Rand Message Handler format.
8 * The change made is to bracket each message with a line
9 * containing 4 control-A's and to split the From line into
10 * a From: field and a Date: field, with the date in Arpanet
11 * standard format.
12 *
13 * This program is designed to be called from the rand mh program
14 * ``inc''
15 *
16 * Set SENDMAIL if you are running sendmail -- this guarantees that
17 * From: and Date: lines will appear already, and will put the info
18 * in the UNIX-From line into a Received-From: field.
19 */
20
21#include <stdio.h>
22#include <sys/types.h>
23#include <sys/timeb.h>
24#include <ctype.h>
25
26#define SENDMAIL
27
28struct headline {
29 char *l_from; /* The name of the sender */
30 char *l_tty; /* His tty string (if any) */
31 char *l_date; /* The entire date string */
32};
33
34char *savestr(), *copyin(), *copy(), *nextword(), *calloc();
35char *index();
36
37#define NOSTR ((char *) 0)
38#define UUCP /* Undo strange uucp naming */
39
40main(argc, argv)
41 char **argv;
42{
43 char linebuf[BUFSIZ];
44 register int maybe;
45 register FILE *inf, *outf;
46 int inhdr, infld;
47
48 if (argc > 3) {
49 fprintf(stderr, "Usage: unixtomh name1 name2\n");
50 exit(1);
51 }
52 outf = inf = NULL;
53 if (argc < 3)
54 outf = stdout;
55 if (argc < 2)
56 inf = stdin;
57 if (inf == NULL && (inf = fopen(argv[1], "r")) == NULL) {
58 perror(argv[1]);
59 exit(1);
60 }
61 if (outf == NULL && (outf = fopen(argv[2], "w")) == NULL) {
62 perror(argv[2]);
63 exit(1);
64 }
65 maybe = 1;
66 inhdr = 0;
67 infld = 0;
68 while (nullgets(linebuf, BUFSIZ, inf) > 0) {
69 if (maybe && ishead(linebuf)) {
70 fputs("\1\1\1\1\n", outf);
71 inhdr++;
72 dohead(linebuf, inf, outf);
73 continue;
74 }
75 if (strlen(linebuf) == 0) {
76 maybe = 1;
77 inhdr = 0;
78 infld = 0;
79 putc('\n', outf);
80 continue;
81 }
82 else
83 maybe = 0;
84#ifndef SENDMAIL
85 if (inhdr && strcmpn(linebuf, "Date: ", 6) == 0)
86 continue;
87 if (inhdr && strcmpn(linebuf, "From: ", 6) == 0)
88 continue;
89#endif SENDMAIL
90 if (infld && isspace(linebuf[0])) {
91 fputs(linebuf, outf);
92 putc('\n', outf);
93 continue;
94 }
95 if (inhdr && !isspace(linebuf[0])) {
96 char *colp, *sp;
97
98 colp = index(linebuf, ':');
99 sp = index(linebuf, ' ');
100 if (colp == NOSTR || sp == NOSTR || sp < colp) {
101 putc('\n', outf);
102 inhdr = 0;
103 }
104 else
105 infld = 1;
106 }
107 fputs(linebuf, outf);
108 putc('\n', outf);
109 }
110 fputs("\1\1\1\1\n", outf);
111 fflush(outf);
112 if (ferror(outf)) {
113 fprintf(stderr, "unixtomh: write: ");
114 perror(argv[2]);
115 exit(1);
116 }
117 exit(0);
118}
119
120/*
121 * Get a line from the given file descriptor, don't return the
122 * terminating newline.
123 */
124
125nullgets(linebuf, sz, file)
126 char linebuf[];
127 register FILE *file;
128{
129 register char *cp;
130 register int c, cnt;
131
132 cp = linebuf;
133 cnt = sz;
134 do {
135 if (--cnt <= 0) {
136 *cp = 0;
137 return(1);
138 }
139 c = getc(file);
140 *cp++ = c;
141 } while (c != EOF && c != '\n');
142 if (c == EOF && cp == linebuf+1)
143 return(0);
144 *--cp = 0;
145 return(1);
146}
147
148/*
149 * Output the fields extracted from the From line --
150 * From: and Date: Untangle UUCP stuff if appropriate.
151 */
152
153dohead(line, infile, outfile)
154 char line[];
155 register FILE *infile, *outfile;
156{
157 register char *cp;
158 struct headline hl;
159 char parbuf[BUFSIZ];
160#ifdef UUCP
161 char *word();
162 char namebuf[BUFSIZ];
163 char linebuf[BUFSIZ];
164 int first;
165 long curoff;
166#endif UUCP
167
168 parse(line, &hl, parbuf);
169#ifndef SENDMAIL
170 putdate(hl.l_date, outfile);
171#endif SENDMAIL
172#ifdef UUCP
173 if (strcmp(hl.l_from, "uucp") == 0) {
174 strcpy(namebuf, "");
175 first = 1;
176 for (;;) {
177 curoff = ftell(infile);
178 if (fgets(linebuf, BUFSIZ, infile) == NULL)
179 break;
180 if (strcmp(word(1, linebuf), ">From") != 0)
181 break;
182 if (strcmp(word(-3, linebuf), "remote") != 0)
183 break;
184 if (strcmp(word(-2, linebuf), "from") != 0)
185 break;
186 if (first) {
187 strcpy(namebuf, word(-1, linebuf));
188 strcat(namebuf, "!");
189 strcat(namebuf, word(2, linebuf));
190 first = 0;
191 }
192 else {
193 strcpy(rindex(namebuf, '!')+1,
194 word(-1, linebuf));
195 strcat(namebuf, "!");
196 strcat(namebuf, word(2, linebuf));
197 }
198 }
199 fseek(infile, curoff, 0);
200#ifdef SENDMAIL
201 if (!first)
202 fprintf(outfile, "Return-Path: <%s>\n", namebuf);
203#else SENDMAIL
204 if (first)
205 fprintf(outfile, "From: uucp\n");
206 else
207 fprintf(outfile, "From: %s\n", namebuf);
208#endif SENDMAIL
209 return;
210 }
211#endif UUCP
212#ifdef SENDMAIL
213 if (hl.l_from[0] == '<')
214 fprintf(outfile, "Return-Path: %s\n", hl.l_from);
215 else
216 fprintf(outfile, "Return-Path: <%s>\n", hl.l_from);
217#else SENDMAIL
218 fprintf(outfile, "From: %s\n", hl.l_from);
219#endif SENDMAIL
220}
221
222#ifdef UUCP
223
224/*
225 * Return liberal word i from the given string.
226 * The words are numbered 1, 2, 3, . . . from the left
227 * and -1, -2, . . . from the right.
228 */
229
230char *
231word(index, str)
232 char str[];
233{
234 register char *cp;
235 char *secbuf;
236 register int c;
237 static char retbuf[100];
238 char *gword();
239
240 cp = str;
241 if ((c = index) > 0) {
242 while (c-- > 0)
243 cp = gword(cp, retbuf);
244 return(retbuf);
245 }
246 if (c == 0)
247 return("");
248 secbuf = (char *) alloca(strlen(str) + 1);
249 strcpy(secbuf, str);
250 rev(secbuf);
251 cp = word(-index, secbuf);
252 rev(cp);
253 return(cp);
254}
255
256/*
257 * Skip leading blanks in the string, return
258 * first liberal word collected.
259 */
260
261char *
262gword(cp, buf)
263 register char *cp;
264 char buf[];
265{
266 register char *cp2;
267
268 cp2 = buf;
269 while (*cp && any(*cp, " \t\n"))
270 cp++;
271 while (*cp && !any(*cp, " \t\n"))
272 *cp2++ = *cp++;
273 *cp2 = 0;
274 return(cp);
275}
276
277/*
278 * Reverse the characters in the string in place
279 */
280
281rev(str)
282 char str[];
283{
284 register char *cpl, *cpr;
285 register int s;
286
287 s = strlen(str);
288 cpl = str;
289 cpr = &str[s-1];
290 while (cpl < cpr) {
291 s = *cpl;
292 *cpl++ = *cpr;
293 *cpr-- = s;
294 }
295}
296#endif UUCP
297
298/*
299 * Save a string in dynamic space.
300 * This little goodie is needed for
301 * a headline detector in head.c
302 */
303
304char *
305savestr(str)
306 char str[];
307{
308 register char *top;
309
310 top = calloc(strlen(str) + 1, 1);
311 if (top == NOSTR) {
312 fprintf(stderr, "unixtomh: Ran out of memory\n");
313 exit(1);
314 }
315 copy(str, top);
316 return(top);
317}
318
319/*
320 * See if the passed line buffer is a mail header.
321 * Return true if yes. Note the extreme pains to
322 * accomodate all funny formats.
323 */
324
325ishead(linebuf)
326 char linebuf[];
327{
328 register char *cp;
329 struct headline hl;
330 char parbuf[BUFSIZ];
331
332 cp = linebuf;
333 if (!isname("From ", cp, 5))
334 return(0);
335 parse(cp, &hl, parbuf);
336 if (hl.l_from == NOSTR || hl.l_date == NOSTR) {
337 fail(linebuf, "No from or date field");
338 return(0);
339 }
340 if (!isdate(hl.l_date)) {
341 fail(linebuf, "Date field not legal date");
342 return(0);
343 }
344
345 /*
346 * I guess we got it!
347 */
348
349 return(1);
350}
351
352fail(linebuf, reason)
353 char linebuf[], reason[];
354{
355 return;
356}
357
358/*
359 * Split a headline into its useful components.
360 * Copy the line into dynamic string space, then set
361 * pointers into the copied line in the passed headline
362 * structure. Actually, it scans.
363 */
364
365parse(line, hl, pbuf)
366 char line[], pbuf[];
367 struct headline *hl;
368{
369 register char *cp, *dp;
370 char *sp;
371 char word[BUFSIZ];
372
373 hl->l_from = NOSTR;
374 hl->l_tty = NOSTR;
375 hl->l_date = NOSTR;
376 cp = line;
377 sp = pbuf;
378
379 /*
380 * Skip the first "word" of the line, which should be "From"
381 * anyway.
382 */
383
384 cp = nextword(cp, word);
385 dp = nextword(cp, word);
386 if (word[0] != 0)
387 hl->l_from = copyin(word, &sp);
388 if (isname(dp, "tty", 3)) {
389 cp = nextword(dp, word);
390 hl->l_tty = copyin(word, &sp);
391 if (cp != NOSTR)
392 hl->l_date = copyin(cp, &sp);
393 }
394 else
395 if (dp != NOSTR)
396 hl->l_date = copyin(dp, &sp);
397}
398
399/*
400 * Copy the string on the left into the string on the right
401 * and bump the right (reference) string pointer by the length.
402 * Thus, dynamically allocate space in the right string, copying
403 * the left string into it.
404 */
405
406char *
407copyin(src, space)
408 char src[];
409 char **space;
410{
411 register char *cp, *top;
412 register int s;
413
414 s = strlen(src);
415 cp = *space;
416 top = cp;
417 strcpy(cp, src);
418 cp += s + 1;
419 *space = cp;
420 return(top);
421}
422
423/*
424 * See if the two passed strings agree in the first n characters.
425 * Return true if they do, gnu.
426 */
427
428isname(as1, as2, acount)
429 char *as1, *as2;
430{
431 register char *s1, *s2;
432 register count;
433
434 s1 = as1;
435 s2 = as2;
436 count = acount;
437 if (count > 0)
438 do
439 if (*s1++ != *s2++)
440 return(0);
441 while (--count);
442 return(1);
443}
444
445/*
446 * Test to see if the passed string is a ctime(3) generated
447 * date string as documented in the manual. The template
448 * below is used as the criterion of correctness.
449 * Also, we check for a possible trailing time zone using
450 * the auxtype template.
451 */
452
453#define L 1 /* A lower case char */
454#define S 2 /* A space */
455#define D 3 /* A digit */
456#define O 4 /* An optional digit or space */
457#define C 5 /* A colon */
458#define N 6 /* A new line */
459#define U 7 /* An upper case char */
460
461char 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};
462char 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};
463
464isdate(date)
465 char date[];
466{
467 register char *cp;
468
469 cp = date;
470 if (cmatch(cp, ctypes))
471 return(1);
472 return(cmatch(cp, tmztypes));
473}
474
475/*
476 * Match the given string against the given template.
477 * Return 1 if they match, 0 if they don't
478 */
479
480cmatch(str, temp)
481 char str[], temp[];
482{
483 register char *cp, *tp;
484 register int c;
485
486 cp = str;
487 tp = temp;
488 while (*cp != '\0' && *tp != 0) {
489 c = *cp++;
490 switch (*tp++) {
491 case L:
492 if (!islower(c))
493 return(0);
494 break;
495
496 case S:
497 if (c != ' ')
498 return(0);
499 break;
500
501 case D:
502 if (!isdigit(c))
503 return(0);
504 break;
505
506 case O:
507 if (c != ' ' && !isdigit(c))
508 return(0);
509 break;
510
511 case C:
512 if (c != ':')
513 return(0);
514 break;
515
516 case N:
517 if (c != '\n')
518 return(0);
519 break;
520
521 case U:
522 if (!isupper(c))
523 return(0);
524 break;
525 }
526 }
527 if (*cp != '\0' || *tp != 0)
528 return(0);
529 return(1);
530}
531
532/*
533 * Collect a liberal (space, tab delimited) word into the word buffer
534 * passed. Also, return a pointer to the next word following that,
535 * or NOSTR if none follow.
536 */
537
538char *
539nextword(wp, wbuf)
540 char wp[], wbuf[];
541{
542 register char *cp, *cp2;
543
544 if ((cp = wp) == NOSTR) {
545 copy("", wbuf);
546 return(NOSTR);
547 }
548 cp2 = wbuf;
549 while (!any(*cp, " \t") && *cp != '\0')
550 if (*cp == '"') {
551 *cp2++ = *cp++;
552 while (*cp != '\0' && *cp != '"')
553 *cp2++ = *cp++;
554 if (*cp == '"')
555 *cp2++ = *cp++;
556 } else
557 *cp2++ = *cp++;
558 *cp2 = '\0';
559 while (any(*cp, " \t"))
560 cp++;
561 if (*cp == '\0')
562 return(NOSTR);
563 return(cp);
564}
565
566/*
567 * Copy str1 to str2, return pointer to null in str2.
568 */
569
570char *
571copy(str1, str2)
572 char *str1, *str2;
573{
574 register char *s1, *s2;
575
576 s1 = str1;
577 s2 = str2;
578 while (*s1)
579 *s2++ = *s1++;
580 *s2 = 0;
581 return(s2);
582}
583
584/*
585 * Is ch any of the characters in str?
586 */
587
588any(ch, str)
589 char *str;
590{
591 register char *f;
592 register c;
593
594 f = str;
595 c = ch;
596 while (*f)
597 if (c == *f++)
598 return(1);
599 return(0);
600}
601
602/*
603 * Convert lower case letters to upper case.
604 */
605
606raise(c)
607 register int c;
608{
609 if (c >= 'a' && c <= 'z')
610 c += 'A' - 'a';
611 return(c);
612}