xref: /original-bsd/usr.bin/mail/aux.c (revision 6c57d260)
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	1.6 03/11/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 
399 	if ((fi = fopen(name, "r")) == NULL) {
400 		perror(name);
401 		return(1);
402 	}
403 	if (ssp >= _NFILE-2) {
404 		printf("Too much \"sourcing\" going on.\n");
405 		fclose(fi);
406 		return(1);
407 	}
408 	sstack[++ssp].s_file = input;
409 	sstack[ssp].s_cond = cond;
410 	cond = CANY;
411 	input = fi;
412 	sourcing++;
413 	return(0);
414 }
415 
416 /*
417  * Source a file, but do nothing if the file cannot be opened.
418  */
419 
420 source1(name)
421 	char name[];
422 {
423 	register int f;
424 
425 	if ((f = open(name, 0)) < 0)
426 		return(0);
427 	close(f);
428 	source(name);
429 }
430 
431 /*
432  * Pop the current input back to the previous level.
433  * Update the "sourcing" flag as appropriate.
434  */
435 
436 unstack()
437 {
438 	if (ssp < 0) {
439 		printf("\"Source\" stack over-pop.\n");
440 		sourcing = 0;
441 		return(1);
442 	}
443 	fclose(input);
444 	if (cond != CANY)
445 		printf("Unmatched \"if\"\n");
446 	cond = sstack[ssp].s_cond;
447 	input = sstack[ssp--].s_file;
448 	if (ssp < 0)
449 		sourcing = 0;
450 	return(0);
451 }
452 
453 /*
454  * Touch the indicated file.
455  * This is nifty for the shell.
456  * If we have the utime() system call, this is better served
457  * by using that, since it will work for empty files.
458  * On non-utime systems, we must sleep a second, then read.
459  */
460 
461 alter(name)
462 	char name[];
463 {
464 #ifdef UTIME
465 	struct stat statb;
466 	long time();
467 	time_t time_p[2];
468 #else
469 	register int pid, f;
470 	char w;
471 #endif UTIME
472 
473 #ifdef UTIME
474 	if (stat(name, &statb) < 0)
475 		return;
476 	time_p[0] = time((long *) 0) + 1;
477 	time_p[1] = statb.st_mtime;
478 	utime(name, time_p);
479 #else
480 	if ((pid = fork()) != 0)
481 		return;
482 	clrbuf(stdout);
483 	clrbuf(stderr);
484 	clrbuf(stdin);
485 	sleep(1);
486 	if ((f = open(name, 0)) < 0)
487 		exit(1);
488 	read(f, &w, 1);
489 	exit(0);
490 #endif
491 }
492 
493 /*
494  * Examine the passed line buffer and
495  * return true if it is all blanks and tabs.
496  */
497 
498 blankline(linebuf)
499 	char linebuf[];
500 {
501 	register char *cp;
502 
503 	for (cp = linebuf; *cp; cp++)
504 		if (!any(*cp, " \t"))
505 			return(0);
506 	return(1);
507 }
508 
509 /*
510  * Get sender's name from this message.  If the message has
511  * a bunch of arpanet stuff in it, we may have to skin the name
512  * before returning it.
513  */
514 char *
515 nameof(mp, reptype)
516 	register struct message *mp;
517 {
518 
519 	return(skin(name1(mp, reptype)));
520 }
521 
522 /*
523  * Skin an arpa net address according to the RFC 733 interpretation
524  * of "host-phrase."
525  */
526 char *
527 skin(name)
528 	char *name;
529 {
530 	register int c;
531 	register char *cp, *cp2;
532 	int gotlt, lastsp;
533 	char nbuf[BUFSIZ];
534 
535 	if (name == NOSTR)
536 		return(NOSTR);
537 	if (index(name, '(') == NOSTR && index(name, '<') == NOSTR)
538 		return(name);
539 	gotlt = 0;
540 	lastsp = 0;
541 	for (cp = name, cp2 = nbuf, c = *cp++; *cp; c = *cp++) {
542 		switch (c) {
543 		case '(':
544 			while (*cp != ')' && *cp != 0)
545 				cp++;
546 			if (*cp)
547 				cp++;
548 			break;
549 
550 		case ' ':
551 			lastsp = 1;
552 			break;
553 
554 		case '<':
555 			cp2 = nbuf;
556 			gotlt++;
557 			lastsp = 0;
558 			break;
559 
560 		case '>':
561 			if (gotlt)
562 				goto done;
563 
564 			/* Fall into . . . */
565 
566 		default:
567 			if (lastsp) {
568 				lastsp = 0;
569 				*cp2++ = ' ';
570 			}
571 			*cp2++ = c;
572 			break;
573 		}
574 	}
575 done:
576 	*cp2 = 0;
577 
578 	return(savestr(nbuf));
579 }
580 
581 /*
582  * Fetch the sender's name from the passed message.
583  * Reptype can be
584  *	0 -- get sender's name for display purposes
585  *	1 -- get sender's name for reply
586  *	2 -- get sender's name for Reply
587  */
588 
589 char *
590 name1(mp, reptype)
591 	register struct message *mp;
592 {
593 	char namebuf[LINESIZE];
594 	char linebuf[LINESIZE];
595 	register char *cp, *cp2;
596 	register FILE *ibuf;
597 	int first = 1;
598 
599 #ifndef DELIVERMAIL
600 	if ((cp = hfield("from", mp)) != NOSTR)
601 		return(cp);
602 	if (reptype == 0 && (cp = hfield("sender", mp)) != NOSTR)
603 		return(cp);
604 #endif
605 	ibuf = setinput(mp);
606 	copy("", namebuf);
607 	if (readline(ibuf, linebuf) <= 0)
608 		return(savestr(namebuf));
609 newname:
610 	for (cp = linebuf; *cp != ' '; cp++)
611 		;
612 	while (any(*cp, " \t"))
613 		cp++;
614 	for (cp2 = &namebuf[strlen(namebuf)]; *cp && !any(*cp, " \t") &&
615 	    cp2-namebuf < LINESIZE-1; *cp2++ = *cp++)
616 		;
617 	*cp2 = '\0';
618 	if (readline(ibuf, linebuf) <= 0)
619 		return(savestr(namebuf));
620 	if ((cp = index(linebuf, 'F')) == NULL)
621 		return(savestr(namebuf));
622 	if (strncmp(cp, "From", 4) != 0)
623 		return(savestr(namebuf));
624 	while ((cp = index(cp, 'r')) != NULL) {
625 		if (strncmp(cp, "remote", 6) == 0) {
626 			if ((cp = index(cp, 'f')) == NULL)
627 				break;
628 			if (strncmp(cp, "from", 4) != 0)
629 				break;
630 			if ((cp = index(cp, ' ')) == NULL)
631 				break;
632 			cp++;
633 			if (first) {
634 				copy(cp, namebuf);
635 				first = 0;
636 			} else
637 				strcpy(rindex(namebuf, '!')+1, cp);
638 			strcat(namebuf, "!");
639 			goto newname;
640 		}
641 		cp++;
642 	}
643 	return(savestr(namebuf));
644 }
645 
646 /*
647  * Find the rightmost pointer to an instance of the
648  * character in the string and return it.
649  */
650 
651 char *
652 rindex(str, c)
653 	char str[];
654 	register int c;
655 {
656 	register char *cp, *cp2;
657 
658 	for (cp = str, cp2 = NOSTR; *cp; cp++)
659 		if (c == *cp)
660 			cp2 = cp;
661 	return(cp2);
662 }
663 
664 /*
665  * See if the string is a number.
666  */
667 
668 numeric(str)
669 	char str[];
670 {
671 	register char *cp = str;
672 
673 	while (*cp)
674 		if (!isdigit(*cp++))
675 			return(0);
676 	return(1);
677 }
678 
679 /*
680  * Are any of the characters in the two strings the same?
681  */
682 
683 anyof(s1, s2)
684 	register char *s1, *s2;
685 {
686 	register int c;
687 
688 	while (c = *s1++)
689 		if (any(c, s2))
690 			return(1);
691 	return(0);
692 }
693 
694 /*
695  * Determine the leftmost index of the character
696  * in the string.
697  */
698 
699 char *
700 index(str, ch)
701 	char *str;
702 {
703 	register char *cp;
704 	register int c;
705 
706 	for (c = ch, cp = str; *cp; cp++)
707 		if (*cp == c)
708 			return(cp);
709 	return(NOSTR);
710 }
711 
712 /*
713  * String compare two strings of bounded length.
714  */
715 
716 strncmp(as1, as2, an)
717 	char *as1, *as2;
718 {
719 	register char *s1, *s2;
720 	register int n;
721 
722 	s1 = as1;
723 	s2 = as2;
724 	n = an;
725 	while (--n >= 0 && *s1 == *s2++)
726 		if (*s1++ == '\0')
727 			return(0);
728 	return(n<0 ? 0 : *s1 - *--s2);
729 }
730 
731