reply to RFC822 instead of RFC733 headers (from sun!gorodish!guy)
[unix-history] / usr / src / usr.bin / mail / aux.c
CommitLineData
d0aeaf5a
DF
1/*
2 * Copyright (c) 1980 Regents of the University of California.
3 * All rights reserved. The Berkeley software License Agreement
4 * specifies the terms and conditions for redistribution.
5 */
6
2ae9f53f 7#ifndef lint
b4817aab 8static char *sccsid = "@(#)aux.c 5.4 (Berkeley) %G%";
d0aeaf5a 9#endif not lint
6447a23f
KS
10
11#include "rcv.h"
12#include <sys/stat.h>
6447a23f
KS
13#include <ctype.h>
14
15/*
16 * Mail -- a mail program
17 *
18 * Auxiliary functions.
19 */
20
6447a23f
KS
21/*
22 * Return a pointer to a dynamic copy of the argument.
23 */
24
25char *
26savestr(str)
27 char *str;
28{
29 register char *cp, *cp2, *top;
30
31 for (cp = str; *cp; cp++)
32 ;
33 top = salloc(cp-str + 1);
34 if (top == NOSTR)
35 return(NOSTR);
36 for (cp = str, cp2 = top; *cp; cp++)
37 *cp2++ = *cp;
38 *cp2 = 0;
39 return(top);
40}
41
42/*
43 * Copy the name from the passed header line into the passed
44 * name buffer. Null pad the name buffer.
45 */
46
47copyname(linebuf, nbuf)
48 char *linebuf, *nbuf;
49{
50 register char *cp, *cp2;
51
52 for (cp = linebuf + 5, cp2 = nbuf; *cp != ' ' && cp2-nbuf < 8; cp++)
53 *cp2++ = *cp;
54 while (cp2-nbuf < 8)
55 *cp2++ = 0;
56}
57
58/*
59 * Announce a fatal error and die.
60 */
61
62panic(str)
63 char *str;
64{
65 prs("panic: ");
66 prs(str);
67 prs("\n");
68 exit(1);
69}
70
71/*
72 * Catch stdio errors and report them more nicely.
73 */
74
75_error(str)
76 char *str;
77{
78 prs("Stdio Error: ");
79 prs(str);
80 prs("\n");
81 abort();
82}
83
84/*
85 * Print a string on diagnostic output.
86 */
87
88prs(str)
89 char *str;
90{
91 register char *s;
92
93 for (s = str; *s; s++)
94 ;
95 write(2, str, s-str);
96}
97
98/*
99 * Touch the named message by setting its MTOUCH flag.
100 * Touched messages have the effect of not being sent
101 * back to the system mailbox on exit.
102 */
103
104touch(mesg)
105{
dffe7e4f
KS
106 register struct message *mp;
107
108 if (mesg < 1 || mesg > msgCount)
109 return;
110 mp = &message[mesg-1];
111 mp->m_flag |= MTOUCH;
112 if ((mp->m_flag & MREAD) == 0)
113 mp->m_flag |= MREAD|MSTATUS;
6447a23f
KS
114}
115
116/*
117 * Test to see if the passed file name is a directory.
118 * Return true if it is.
119 */
120
121isdir(name)
122 char name[];
123{
124 struct stat sbuf;
125
126 if (stat(name, &sbuf) < 0)
127 return(0);
128 return((sbuf.st_mode & S_IFMT) == S_IFDIR);
129}
130
6447a23f
KS
131/*
132 * Count the number of arguments in the given string raw list.
133 */
134
135argcount(argv)
136 char **argv;
137{
138 register char **ap;
139
140 for (ap = argv; *ap != NOSTR; ap++)
141 ;
142 return(ap-argv);
143}
144
145/*
146 * Given a file address, determine the
147 * block number it represents.
148 */
149
150blockof(off)
151 off_t off;
152{
153 off_t a;
154
155 a = off >> 9;
156 a &= 077777;
157 return((int) a);
158}
159
160/*
161 * Take a file address, and determine
162 * its offset in the current block.
163 */
164
165offsetof(off)
166 off_t off;
167{
168 off_t a;
169
170 a = off & 0777;
171 return((int) a);
172}
173
6447a23f
KS
174/*
175 * Return the desired header line from the passed message
176 * pointer (or NOSTR if the desired header field is not available).
177 */
178
179char *
180hfield(field, mp)
181 char field[];
182 struct message *mp;
183{
184 register FILE *ibuf;
185 char linebuf[LINESIZE];
186 register int lc;
187
188 ibuf = setinput(mp);
189 if ((lc = mp->m_lines) <= 0)
190 return(NOSTR);
191 if (readline(ibuf, linebuf) < 0)
192 return(NOSTR);
193 lc--;
194 do {
195 lc = gethfield(ibuf, linebuf, lc);
196 if (lc == -1)
197 return(NOSTR);
198 if (ishfield(linebuf, field))
199 return(savestr(hcontents(linebuf)));
200 } while (lc > 0);
201 return(NOSTR);
202}
203
204/*
205 * Return the next header field found in the given message.
206 * Return > 0 if something found, <= 0 elsewise.
207 * Must deal with \ continuations & other such fraud.
208 */
209
210gethfield(f, linebuf, rem)
211 register FILE *f;
212 char linebuf[];
213 register int rem;
214{
215 char line2[LINESIZE];
216 long loc;
217 register char *cp, *cp2;
218 register int c;
219
220
221 for (;;) {
222 if (rem <= 0)
223 return(-1);
224 if (readline(f, linebuf) < 0)
225 return(-1);
226 rem--;
227 if (strlen(linebuf) == 0)
228 return(-1);
229 if (isspace(linebuf[0]))
230 continue;
231 if (linebuf[0] == '>')
232 continue;
233 cp = index(linebuf, ':');
234 if (cp == NOSTR)
235 continue;
236 for (cp2 = linebuf; cp2 < cp; cp2++)
237 if (isdigit(*cp2))
238 continue;
239
240 /*
241 * I guess we got a headline.
242 * Handle wraparounding
243 */
244
245 for (;;) {
246 if (rem <= 0)
247 break;
248#ifdef CANTELL
249 loc = ftell(f);
250 if (readline(f, line2) < 0)
251 break;
252 rem--;
253 if (!isspace(line2[0])) {
254 fseek(f, loc, 0);
255 rem++;
256 break;
257 }
258#else
259 c = getc(f);
260 ungetc(c, f);
261 if (!isspace(c) || c == '\n')
262 break;
263 if (readline(f, line2) < 0)
264 break;
265 rem--;
266#endif
267 cp2 = line2;
268 for (cp2 = line2; *cp2 != 0 && isspace(*cp2); cp2++)
269 ;
270 if (strlen(linebuf) + strlen(cp2) >= LINESIZE-2)
271 break;
272 cp = &linebuf[strlen(linebuf)];
273 while (cp > linebuf &&
274 (isspace(cp[-1]) || cp[-1] == '\\'))
275 cp--;
276 *cp++ = ' ';
277 for (cp2 = line2; *cp2 != 0 && isspace(*cp2); cp2++)
278 ;
279 strcpy(cp, cp2);
280 }
281 if ((c = strlen(linebuf)) > 0) {
282 cp = &linebuf[c-1];
283 while (cp > linebuf && isspace(*cp))
284 cp--;
285 *++cp = 0;
286 }
287 return(rem);
288 }
289 /* NOTREACHED */
290}
291
292/*
293 * Check whether the passed line is a header line of
294 * the desired breed.
295 */
296
297ishfield(linebuf, field)
298 char linebuf[], field[];
299{
300 register char *cp;
301 register int c;
302
303 if ((cp = index(linebuf, ':')) == NOSTR)
304 return(0);
305 if (cp == linebuf)
306 return(0);
307 cp--;
308 while (cp > linebuf && isspace(*cp))
309 cp--;
310 c = *++cp;
311 *cp = 0;
312 if (icequal(linebuf ,field)) {
313 *cp = c;
314 return(1);
315 }
316 *cp = c;
317 return(0);
318}
319
320/*
321 * Extract the non label information from the given header field
322 * and return it.
323 */
324
325char *
326hcontents(hfield)
327 char hfield[];
328{
329 register char *cp;
330
331 if ((cp = index(hfield, ':')) == NOSTR)
332 return(NOSTR);
333 cp++;
334 while (*cp && isspace(*cp))
335 cp++;
336 return(cp);
337}
338
339/*
340 * Compare two strings, ignoring case.
341 */
342
343icequal(s1, s2)
344 register char *s1, *s2;
345{
346
347 while (raise(*s1++) == raise(*s2))
348 if (*s2++ == 0)
349 return(1);
350 return(0);
351}
352
da0d3a6d
KS
353/*
354 * Copy a string, lowercasing it as we go.
355 */
356istrcpy(dest, src)
357 char *dest, *src;
358{
359 register char *cp, *cp2;
360
361 cp2 = dest;
362 cp = src;
363 do {
364 *cp2++ = little(*cp);
365 } while (*cp++ != 0);
366}
367
6447a23f
KS
368/*
369 * The following code deals with input stacking to do source
370 * commands. All but the current file pointer are saved on
371 * the stack.
372 */
373
374static int ssp = -1; /* Top of file stack */
e48b42f1
KS
375struct sstack {
376 FILE *s_file; /* File we were in. */
377 int s_cond; /* Saved state of conditionals */
f4aa38b5 378 int s_loading; /* Loading .mailrc, etc. */
46053c99 379} sstack[NOFILE];
6447a23f
KS
380
381/*
382 * Pushdown current input file and switch to a new one.
383 * Set the global flag "sourcing" so that others will realize
384 * that they are no longer reading from a tty (in all probability).
385 */
386
387source(name)
388 char name[];
389{
390 register FILE *fi;
5c7ba847 391 register char *cp;
6447a23f 392
5c7ba847
KS
393 if ((cp = expand(name)) == NOSTR)
394 return(1);
395 if ((fi = fopen(cp, "r")) == NULL) {
396 perror(cp);
6447a23f
KS
397 return(1);
398 }
46053c99 399 if (ssp >= NOFILE - 2) {
6447a23f
KS
400 printf("Too much \"sourcing\" going on.\n");
401 fclose(fi);
402 return(1);
403 }
e48b42f1
KS
404 sstack[++ssp].s_file = input;
405 sstack[ssp].s_cond = cond;
f4aa38b5
KS
406 sstack[ssp].s_loading = loading;
407 loading = 0;
e48b42f1 408 cond = CANY;
6447a23f
KS
409 input = fi;
410 sourcing++;
411 return(0);
412}
413
414/*
415 * Source a file, but do nothing if the file cannot be opened.
416 */
417
418source1(name)
419 char name[];
420{
421 register int f;
422
423 if ((f = open(name, 0)) < 0)
424 return(0);
425 close(f);
426 source(name);
427}
428
429/*
430 * Pop the current input back to the previous level.
431 * Update the "sourcing" flag as appropriate.
432 */
433
434unstack()
435{
436 if (ssp < 0) {
437 printf("\"Source\" stack over-pop.\n");
438 sourcing = 0;
439 return(1);
440 }
441 fclose(input);
e48b42f1
KS
442 if (cond != CANY)
443 printf("Unmatched \"if\"\n");
444 cond = sstack[ssp].s_cond;
f4aa38b5 445 loading = sstack[ssp].s_loading;
e48b42f1 446 input = sstack[ssp--].s_file;
6447a23f 447 if (ssp < 0)
f4aa38b5 448 sourcing = loading;
6447a23f
KS
449 return(0);
450}
451
452/*
453 * Touch the indicated file.
454 * This is nifty for the shell.
455 * If we have the utime() system call, this is better served
456 * by using that, since it will work for empty files.
457 * On non-utime systems, we must sleep a second, then read.
458 */
459
460alter(name)
461 char name[];
462{
463#ifdef UTIME
464 struct stat statb;
465 long time();
466 time_t time_p[2];
467#else
468 register int pid, f;
469 char w;
470#endif UTIME
471
472#ifdef UTIME
473 if (stat(name, &statb) < 0)
474 return;
475 time_p[0] = time((long *) 0) + 1;
476 time_p[1] = statb.st_mtime;
477 utime(name, time_p);
478#else
6447a23f
KS
479 sleep(1);
480 if ((f = open(name, 0)) < 0)
a0852086 481 return;
6447a23f
KS
482 read(f, &w, 1);
483 exit(0);
484#endif
485}
486
487/*
488 * Examine the passed line buffer and
489 * return true if it is all blanks and tabs.
490 */
491
492blankline(linebuf)
493 char linebuf[];
494{
495 register char *cp;
496
497 for (cp = linebuf; *cp; cp++)
46053c99 498 if (*cp != ' ' && *cp != '\t')
6447a23f
KS
499 return(0);
500 return(1);
501}
502
12388009
KS
503/*
504 * Get sender's name from this message. If the message has
505 * a bunch of arpanet stuff in it, we may have to skin the name
506 * before returning it.
507 */
508char *
509nameof(mp, reptype)
510 register struct message *mp;
511{
8bcfa450 512 register char *cp, *cp2;
12388009 513
8bcfa450
KS
514 cp = skin(name1(mp, reptype));
515 if (reptype != 0 || charcount(cp, '!') < 2)
516 return(cp);
517 cp2 = rindex(cp, '!');
518 cp2--;
519 while (cp2 > cp && *cp2 != '!')
520 cp2--;
521 if (*cp2 == '!')
522 return(cp2 + 1);
523 return(cp);
12388009
KS
524}
525
526/*
b4817aab 527 * Skin an arpa net address according to the RFC 822 interpretation
12388009
KS
528 * of "host-phrase."
529 */
530char *
531skin(name)
532 char *name;
533{
534 register int c;
535 register char *cp, *cp2;
b4817aab 536 char *bufend;
12388009
KS
537 int gotlt, lastsp;
538 char nbuf[BUFSIZ];
46053c99 539 int nesting;
12388009
KS
540
541 if (name == NOSTR)
542 return(NOSTR);
e557a09a
CL
543 if (index(name, '(') == NOSTR && index(name, '<') == NOSTR
544 && index(name, ' ') == NOSTR)
12388009
KS
545 return(name);
546 gotlt = 0;
547 lastsp = 0;
b4817aab
KM
548 bufend = nbuf;
549 for (cp = name, cp2 = bufend; c = *cp++; ) {
12388009
KS
550 switch (c) {
551 case '(':
b4817aab
KM
552 /*
553 * Start of a "comment".
554 * Ignore it.
555 */
46053c99 556 nesting = 1;
b4817aab
KM
557 while ((c = *cp) != 0) {
558 cp++;
559 switch (c) {
560 case '\\':
561 if (*cp == 0)
562 goto outcm;
563 cp++;
564 break;
46053c99
S
565 case '(':
566 nesting++;
567 break;
568
569 case ')':
570 --nesting;
571 break;
572 }
573
574 if (nesting <= 0)
575 break;
576 }
b4817aab
KM
577 outcm:
578 lastsp = 0;
579 break;
580
581 case '"':
582 /*
583 * Start of a "quoted-string".
584 * Copy it in its entirety.
585 */
586 while ((c = *cp) != 0) {
587 cp++;
588 switch (c) {
589 case '\\':
590 if ((c = *cp) == 0)
591 goto outqs;
592 cp++;
593 break;
594 case '"':
595 goto outqs;
596 }
597 *cp2++ = c;
598 }
599 outqs:
e557a09a 600 lastsp = 0;
12388009
KS
601 break;
602
603 case ' ':
e557a09a
CL
604 if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ')
605 cp += 3, *cp2++ = '@';
606 else
607 if (cp[0] == '@' && cp[1] == ' ')
608 cp += 2, *cp2++ = '@';
609 else
610 lastsp = 1;
12388009
KS
611 break;
612
613 case '<':
b4817aab 614 cp2 = bufend;
12388009
KS
615 gotlt++;
616 lastsp = 0;
617 break;
618
619 case '>':
b4817aab
KM
620 if (gotlt) {
621 gotlt = 0;
622 while (*cp != ',' && *cp != 0)
623 cp++;
624 if (*cp == 0 )
625 goto done;
626 *cp2++ = ',';
627 *cp2++ = ' ';
628 bufend = cp2;
629 break;
630 }
12388009
KS
631
632 /* Fall into . . . */
633
634 default:
635 if (lastsp) {
636 lastsp = 0;
637 *cp2++ = ' ';
638 }
639 *cp2++ = c;
640 break;
641 }
642 }
643done:
644 *cp2 = 0;
645
646 return(savestr(nbuf));
647}
648
6447a23f
KS
649/*
650 * Fetch the sender's name from the passed message.
12388009
KS
651 * Reptype can be
652 * 0 -- get sender's name for display purposes
653 * 1 -- get sender's name for reply
654 * 2 -- get sender's name for Reply
6447a23f
KS
655 */
656
657char *
12388009 658name1(mp, reptype)
6447a23f
KS
659 register struct message *mp;
660{
661 char namebuf[LINESIZE];
662 char linebuf[LINESIZE];
663 register char *cp, *cp2;
664 register FILE *ibuf;
665 int first = 1;
666
12388009
KS
667 if ((cp = hfield("from", mp)) != NOSTR)
668 return(cp);
669 if (reptype == 0 && (cp = hfield("sender", mp)) != NOSTR)
670 return(cp);
6447a23f
KS
671 ibuf = setinput(mp);
672 copy("", namebuf);
673 if (readline(ibuf, linebuf) <= 0)
674 return(savestr(namebuf));
675newname:
676 for (cp = linebuf; *cp != ' '; cp++)
677 ;
678 while (any(*cp, " \t"))
679 cp++;
680 for (cp2 = &namebuf[strlen(namebuf)]; *cp && !any(*cp, " \t") &&
681 cp2-namebuf < LINESIZE-1; *cp2++ = *cp++)
682 ;
683 *cp2 = '\0';
684 if (readline(ibuf, linebuf) <= 0)
685 return(savestr(namebuf));
686 if ((cp = index(linebuf, 'F')) == NULL)
687 return(savestr(namebuf));
688 if (strncmp(cp, "From", 4) != 0)
689 return(savestr(namebuf));
690 while ((cp = index(cp, 'r')) != NULL) {
691 if (strncmp(cp, "remote", 6) == 0) {
692 if ((cp = index(cp, 'f')) == NULL)
693 break;
694 if (strncmp(cp, "from", 4) != 0)
695 break;
696 if ((cp = index(cp, ' ')) == NULL)
697 break;
698 cp++;
699 if (first) {
700 copy(cp, namebuf);
701 first = 0;
702 } else
703 strcpy(rindex(namebuf, '!')+1, cp);
704 strcat(namebuf, "!");
705 goto newname;
706 }
707 cp++;
708 }
709 return(savestr(namebuf));
710}
711
8bcfa450
KS
712/*
713 * Count the occurances of c in str
714 */
715charcount(str, c)
716 char *str;
717{
718 register char *cp;
719 register int i;
720
721 for (i = 0, cp = str; *cp; cp++)
722 if (*cp == c)
723 i++;
724 return(i);
725}
726
6447a23f
KS
727/*
728 * Find the rightmost pointer to an instance of the
729 * character in the string and return it.
730 */
6447a23f
KS
731char *
732rindex(str, c)
733 char str[];
734 register int c;
735{
736 register char *cp, *cp2;
737
738 for (cp = str, cp2 = NOSTR; *cp; cp++)
739 if (c == *cp)
740 cp2 = cp;
741 return(cp2);
742}
743
744/*
745 * See if the string is a number.
746 */
747
748numeric(str)
749 char str[];
750{
751 register char *cp = str;
752
753 while (*cp)
754 if (!isdigit(*cp++))
755 return(0);
756 return(1);
757}
758
759/*
760 * Are any of the characters in the two strings the same?
761 */
762
763anyof(s1, s2)
764 register char *s1, *s2;
765{
766 register int c;
767
768 while (c = *s1++)
769 if (any(c, s2))
770 return(1);
771 return(0);
772}
773
774/*
775 * Determine the leftmost index of the character
776 * in the string.
777 */
778
779char *
780index(str, ch)
781 char *str;
782{
783 register char *cp;
784 register int c;
785
786 for (c = ch, cp = str; *cp; cp++)
787 if (*cp == c)
788 return(cp);
789 return(NOSTR);
790}
791
da0d3a6d
KS
792/*
793 * See if the given header field is supposed to be ignored.
794 */
795isign(field)
796 char *field;
797{
798 char realfld[BUFSIZ];
da0d3a6d 799
46053c99
S
800 /*
801 * Lower-case the string, so that "Status" and "status"
802 * will hash to the same place.
803 */
da0d3a6d 804 istrcpy(realfld, field);
46053c99
S
805
806 if (nretained > 0)
807 return (!member(realfld, retain));
808 else
809 return (member(realfld, ignore));
810}
811
812member(realfield, table)
813 register char *realfield;
814 register struct ignore **table;
815{
816 register struct ignore *igp;
817
818 for (igp = table[hash(realfield)]; igp != 0; igp = igp->i_link)
819 if (equal(igp->i_field, realfield))
820 return (1);
821
822 return (0);
da0d3a6d 823}