xref: /original-bsd/usr.bin/mail/aux.c (revision ba72ef4c)
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.1 10/08/80";
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 	if (mesg >= 1 && mesg <= msgCount)
102 		message[mesg-1].m_flag |= MTOUCH;
103 }
104 
105 /*
106  * Test to see if the passed file name is a directory.
107  * Return true if it is.
108  */
109 
110 isdir(name)
111 	char name[];
112 {
113 	struct stat sbuf;
114 
115 	if (stat(name, &sbuf) < 0)
116 		return(0);
117 	return((sbuf.st_mode & S_IFMT) == S_IFDIR);
118 }
119 
120 /*
121  * Compute the size in characters of the passed message
122  */
123 
124 unsigned int
125 msize(messp)
126 	struct message *messp;
127 {
128 	register struct message *mp;
129 
130 	mp = messp;
131 	return(mp->m_size);
132 }
133 
134 /*
135  * Count the number of arguments in the given string raw list.
136  */
137 
138 argcount(argv)
139 	char **argv;
140 {
141 	register char **ap;
142 
143 	for (ap = argv; *ap != NOSTR; ap++)
144 		;
145 	return(ap-argv);
146 }
147 
148 /*
149  * Given a file address, determine the
150  * block number it represents.
151  */
152 
153 blockof(off)
154 	off_t off;
155 {
156 	off_t a;
157 
158 	a = off >> 9;
159 	a &= 077777;
160 	return((int) a);
161 }
162 
163 /*
164  * Take a file address, and determine
165  * its offset in the current block.
166  */
167 
168 offsetof(off)
169 	off_t off;
170 {
171 	off_t a;
172 
173 	a = off & 0777;
174 	return((int) a);
175 }
176 
177 /*
178  * Determine if the passed file is actually a tty, via a call to
179  * gtty.  This is not totally reliable, but . . .
180  */
181 
182 isatty(f)
183 {
184 	struct sgttyb buf;
185 
186 	if (gtty(f, &buf) < 0)
187 		return(0);
188 	return(1);
189 }
190 
191 /*
192  * Return the desired header line from the passed message
193  * pointer (or NOSTR if the desired header field is not available).
194  */
195 
196 char *
197 hfield(field, mp)
198 	char field[];
199 	struct message *mp;
200 {
201 	register FILE *ibuf;
202 	char linebuf[LINESIZE];
203 	register int lc;
204 
205 	ibuf = setinput(mp);
206 	if ((lc = mp->m_lines) <= 0)
207 		return(NOSTR);
208 	if (readline(ibuf, linebuf) < 0)
209 		return(NOSTR);
210 	lc--;
211 	do {
212 		lc = gethfield(ibuf, linebuf, lc);
213 		if (lc == -1)
214 			return(NOSTR);
215 		if (ishfield(linebuf, field))
216 			return(savestr(hcontents(linebuf)));
217 	} while (lc > 0);
218 	return(NOSTR);
219 }
220 
221 /*
222  * Return the next header field found in the given message.
223  * Return > 0 if something found, <= 0 elsewise.
224  * Must deal with \ continuations & other such fraud.
225  */
226 
227 gethfield(f, linebuf, rem)
228 	register FILE *f;
229 	char linebuf[];
230 	register int rem;
231 {
232 	char line2[LINESIZE];
233 	long loc;
234 	register char *cp, *cp2;
235 	register int c;
236 
237 
238 	for (;;) {
239 		if (rem <= 0)
240 			return(-1);
241 		if (readline(f, linebuf) < 0)
242 			return(-1);
243 		rem--;
244 		if (strlen(linebuf) == 0)
245 			return(-1);
246 		if (isspace(linebuf[0]))
247 			continue;
248 		if (linebuf[0] == '>')
249 			continue;
250 		cp = index(linebuf, ':');
251 		if (cp == NOSTR)
252 			continue;
253 		for (cp2 = linebuf; cp2 < cp; cp2++)
254 			if (isdigit(*cp2))
255 				continue;
256 
257 		/*
258 		 * I guess we got a headline.
259 		 * Handle wraparounding
260 		 */
261 
262 		for (;;) {
263 			if (rem <= 0)
264 				break;
265 #ifdef CANTELL
266 			loc = ftell(f);
267 			if (readline(f, line2) < 0)
268 				break;
269 			rem--;
270 			if (!isspace(line2[0])) {
271 				fseek(f, loc, 0);
272 				rem++;
273 				break;
274 			}
275 #else
276 			c = getc(f);
277 			ungetc(c, f);
278 			if (!isspace(c) || c == '\n')
279 				break;
280 			if (readline(f, line2) < 0)
281 				break;
282 			rem--;
283 #endif
284 			cp2 = line2;
285 			for (cp2 = line2; *cp2 != 0 && isspace(*cp2); cp2++)
286 				;
287 			if (strlen(linebuf) + strlen(cp2) >= LINESIZE-2)
288 				break;
289 			cp = &linebuf[strlen(linebuf)];
290 			while (cp > linebuf &&
291 			    (isspace(cp[-1]) || cp[-1] == '\\'))
292 				cp--;
293 			*cp++ = ' ';
294 			for (cp2 = line2; *cp2 != 0 && isspace(*cp2); cp2++)
295 				;
296 			strcpy(cp, cp2);
297 		}
298 		if ((c = strlen(linebuf)) > 0) {
299 			cp = &linebuf[c-1];
300 			while (cp > linebuf && isspace(*cp))
301 				cp--;
302 			*++cp = 0;
303 		}
304 		return(rem);
305 	}
306 	/* NOTREACHED */
307 }
308 
309 /*
310  * Check whether the passed line is a header line of
311  * the desired breed.
312  */
313 
314 ishfield(linebuf, field)
315 	char linebuf[], field[];
316 {
317 	register char *cp;
318 	register int c;
319 
320 	if ((cp = index(linebuf, ':')) == NOSTR)
321 		return(0);
322 	if (cp == linebuf)
323 		return(0);
324 	cp--;
325 	while (cp > linebuf && isspace(*cp))
326 		cp--;
327 	c = *++cp;
328 	*cp = 0;
329 	if (icequal(linebuf ,field)) {
330 		*cp = c;
331 		return(1);
332 	}
333 	*cp = c;
334 	return(0);
335 }
336 
337 /*
338  * Extract the non label information from the given header field
339  * and return it.
340  */
341 
342 char *
343 hcontents(hfield)
344 	char hfield[];
345 {
346 	register char *cp;
347 
348 	if ((cp = index(hfield, ':')) == NOSTR)
349 		return(NOSTR);
350 	cp++;
351 	while (*cp && isspace(*cp))
352 		cp++;
353 	return(cp);
354 }
355 
356 /*
357  * Compare two strings, ignoring case.
358  */
359 
360 icequal(s1, s2)
361 	register char *s1, *s2;
362 {
363 
364 	while (raise(*s1++) == raise(*s2))
365 		if (*s2++ == 0)
366 			return(1);
367 	return(0);
368 }
369 
370 /*
371  * The following code deals with input stacking to do source
372  * commands.  All but the current file pointer are saved on
373  * the stack.
374  */
375 
376 static	int	ssp = -1;		/* Top of file stack */
377 static	FILE	*sstack[_NFILE];	/* Saved input files */
378 
379 /*
380  * Pushdown current input file and switch to a new one.
381  * Set the global flag "sourcing" so that others will realize
382  * that they are no longer reading from a tty (in all probability).
383  */
384 
385 source(name)
386 	char name[];
387 {
388 	register FILE *fi;
389 
390 	if ((fi = fopen(name, "r")) == NULL) {
391 		perror(name);
392 		return(1);
393 	}
394 	if (ssp >= _NFILE-2) {
395 		printf("Too much \"sourcing\" going on.\n");
396 		fclose(fi);
397 		return(1);
398 	}
399 	sstack[++ssp] = input;
400 	input = fi;
401 	sourcing++;
402 	return(0);
403 }
404 
405 /*
406  * Source a file, but do nothing if the file cannot be opened.
407  */
408 
409 source1(name)
410 	char name[];
411 {
412 	register int f;
413 
414 	if ((f = open(name, 0)) < 0)
415 		return(0);
416 	close(f);
417 	source(name);
418 }
419 
420 /*
421  * Pop the current input back to the previous level.
422  * Update the "sourcing" flag as appropriate.
423  */
424 
425 unstack()
426 {
427 	if (ssp < 0) {
428 		printf("\"Source\" stack over-pop.\n");
429 		sourcing = 0;
430 		return(1);
431 	}
432 	fclose(input);
433 	input = sstack[ssp--];
434 	if (ssp < 0)
435 		sourcing = 0;
436 	return(0);
437 }
438 
439 /*
440  * Touch the indicated file.
441  * This is nifty for the shell.
442  * If we have the utime() system call, this is better served
443  * by using that, since it will work for empty files.
444  * On non-utime systems, we must sleep a second, then read.
445  */
446 
447 alter(name)
448 	char name[];
449 {
450 #ifdef UTIME
451 	struct stat statb;
452 	long time();
453 	time_t time_p[2];
454 #else
455 	register int pid, f;
456 	char w;
457 #endif UTIME
458 
459 #ifdef UTIME
460 	if (stat(name, &statb) < 0)
461 		return;
462 	time_p[0] = time((long *) 0) + 1;
463 	time_p[1] = statb.st_mtime;
464 	utime(name, time_p);
465 #else
466 	if ((pid = fork()) != 0)
467 		return;
468 	clrbuf(stdout);
469 	clrbuf(stderr);
470 	clrbuf(stdin);
471 	sleep(1);
472 	if ((f = open(name, 0)) < 0)
473 		exit(1);
474 	read(f, &w, 1);
475 	exit(0);
476 #endif
477 }
478 
479 /*
480  * Examine the passed line buffer and
481  * return true if it is all blanks and tabs.
482  */
483 
484 blankline(linebuf)
485 	char linebuf[];
486 {
487 	register char *cp;
488 
489 	for (cp = linebuf; *cp; cp++)
490 		if (!any(*cp, " \t"))
491 			return(0);
492 	return(1);
493 }
494 
495 /*
496  * Fetch the sender's name from the passed message.
497  */
498 
499 char *
500 nameof(mp)
501 	register struct message *mp;
502 {
503 	char namebuf[LINESIZE];
504 	char linebuf[LINESIZE];
505 	register char *cp, *cp2;
506 	register FILE *ibuf;
507 	int first = 1;
508 
509 	if ((cp = hfield("reply-to", mp)) != NOSTR) {
510 		strcpy(namebuf, cp);
511 		return(namebuf);
512 	}
513 	ibuf = setinput(mp);
514 	copy("", namebuf);
515 	if (readline(ibuf, linebuf) <= 0)
516 		return(savestr(namebuf));
517 newname:
518 	for (cp = linebuf; *cp != ' '; cp++)
519 		;
520 	while (any(*cp, " \t"))
521 		cp++;
522 	for (cp2 = &namebuf[strlen(namebuf)]; *cp && !any(*cp, " \t") &&
523 	    cp2-namebuf < LINESIZE-1; *cp2++ = *cp++)
524 		;
525 	*cp2 = '\0';
526 	if (readline(ibuf, linebuf) <= 0)
527 		return(savestr(namebuf));
528 	if ((cp = index(linebuf, 'F')) == NULL)
529 		return(savestr(namebuf));
530 	if (strncmp(cp, "From", 4) != 0)
531 		return(savestr(namebuf));
532 	while ((cp = index(cp, 'r')) != NULL) {
533 		if (strncmp(cp, "remote", 6) == 0) {
534 			if ((cp = index(cp, 'f')) == NULL)
535 				break;
536 			if (strncmp(cp, "from", 4) != 0)
537 				break;
538 			if ((cp = index(cp, ' ')) == NULL)
539 				break;
540 			cp++;
541 			if (first) {
542 				copy(cp, namebuf);
543 				first = 0;
544 			} else
545 				strcpy(rindex(namebuf, '!')+1, cp);
546 			strcat(namebuf, "!");
547 			goto newname;
548 		}
549 		cp++;
550 	}
551 	return(savestr(namebuf));
552 }
553 
554 /*
555  * Find the rightmost pointer to an instance of the
556  * character in the string and return it.
557  */
558 
559 char *
560 rindex(str, c)
561 	char str[];
562 	register int c;
563 {
564 	register char *cp, *cp2;
565 
566 	for (cp = str, cp2 = NOSTR; *cp; cp++)
567 		if (c == *cp)
568 			cp2 = cp;
569 	return(cp2);
570 }
571 
572 /*
573  * See if the string is a number.
574  */
575 
576 numeric(str)
577 	char str[];
578 {
579 	register char *cp = str;
580 
581 	while (*cp)
582 		if (!isdigit(*cp++))
583 			return(0);
584 	return(1);
585 }
586 
587 /*
588  * Are any of the characters in the two strings the same?
589  */
590 
591 anyof(s1, s2)
592 	register char *s1, *s2;
593 {
594 	register int c;
595 
596 	while (c = *s1++)
597 		if (any(c, s2))
598 			return(1);
599 	return(0);
600 }
601 
602 /*
603  * Determine the leftmost index of the character
604  * in the string.
605  */
606 
607 char *
608 index(str, ch)
609 	char *str;
610 {
611 	register char *cp;
612 	register int c;
613 
614 	for (c = ch, cp = str; *cp; cp++)
615 		if (*cp == c)
616 			return(cp);
617 	return(NOSTR);
618 }
619 
620 /*
621  * String compare two strings of bounded length.
622  */
623 
624 strncmp(as1, as2, an)
625 	char *as1, *as2;
626 {
627 	register char *s1, *s2;
628 	register int n;
629 
630 	s1 = as1;
631 	s2 = as2;
632 	n = an;
633 	while (--n >= 0 && *s1 == *s2++)
634 		if (*s1++ == '\0')
635 			return(0);
636 	return(n<0 ? 0 : *s1 - *--s2);
637 }
638 
639