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