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