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