xref: /csrg-svn/usr.bin/mail/aux.c (revision 1519)
11219Skas #
21219Skas 
31219Skas #include "rcv.h"
41219Skas #include <sys/stat.h>
51219Skas #include <sgtty.h>
61219Skas #include <ctype.h>
71219Skas 
81219Skas /*
91219Skas  * Mail -- a mail program
101219Skas  *
111219Skas  * Auxiliary functions.
121219Skas  */
131219Skas 
14*1519Skas static char *SccsId = "@(#)aux.c	1.3 10/18/80";
151219Skas 
161219Skas /*
171219Skas  * Return a pointer to a dynamic copy of the argument.
181219Skas  */
191219Skas 
201219Skas char *
211219Skas savestr(str)
221219Skas 	char *str;
231219Skas {
241219Skas 	register char *cp, *cp2, *top;
251219Skas 
261219Skas 	for (cp = str; *cp; cp++)
271219Skas 		;
281219Skas 	top = salloc(cp-str + 1);
291219Skas 	if (top == NOSTR)
301219Skas 		return(NOSTR);
311219Skas 	for (cp = str, cp2 = top; *cp; cp++)
321219Skas 		*cp2++ = *cp;
331219Skas 	*cp2 = 0;
341219Skas 	return(top);
351219Skas }
361219Skas 
371219Skas /*
381219Skas  * Copy the name from the passed header line into the passed
391219Skas  * name buffer.  Null pad the name buffer.
401219Skas  */
411219Skas 
421219Skas copyname(linebuf, nbuf)
431219Skas 	char *linebuf, *nbuf;
441219Skas {
451219Skas 	register char *cp, *cp2;
461219Skas 
471219Skas 	for (cp = linebuf + 5, cp2 = nbuf; *cp != ' ' && cp2-nbuf < 8; cp++)
481219Skas 		*cp2++ = *cp;
491219Skas 	while (cp2-nbuf < 8)
501219Skas 		*cp2++ = 0;
511219Skas }
521219Skas 
531219Skas /*
541219Skas  * Announce a fatal error and die.
551219Skas  */
561219Skas 
571219Skas panic(str)
581219Skas 	char *str;
591219Skas {
601219Skas 	prs("panic: ");
611219Skas 	prs(str);
621219Skas 	prs("\n");
631219Skas 	exit(1);
641219Skas }
651219Skas 
661219Skas /*
671219Skas  * Catch stdio errors and report them more nicely.
681219Skas  */
691219Skas 
701219Skas _error(str)
711219Skas 	char *str;
721219Skas {
731219Skas 	prs("Stdio Error: ");
741219Skas 	prs(str);
751219Skas 	prs("\n");
761219Skas 	abort();
771219Skas }
781219Skas 
791219Skas /*
801219Skas  * Print a string on diagnostic output.
811219Skas  */
821219Skas 
831219Skas prs(str)
841219Skas 	char *str;
851219Skas {
861219Skas 	register char *s;
871219Skas 
881219Skas 	for (s = str; *s; s++)
891219Skas 		;
901219Skas 	write(2, str, s-str);
911219Skas }
921219Skas 
931219Skas /*
941219Skas  * Touch the named message by setting its MTOUCH flag.
951219Skas  * Touched messages have the effect of not being sent
961219Skas  * back to the system mailbox on exit.
971219Skas  */
981219Skas 
991219Skas touch(mesg)
1001219Skas {
1011479Skas 	register struct message *mp;
1021479Skas 
1031479Skas 	if (mesg < 1 || mesg > msgCount)
1041479Skas 		return;
1051479Skas 	mp = &message[mesg-1];
1061479Skas 	mp->m_flag |= MTOUCH;
1071479Skas 	if ((mp->m_flag & MREAD) == 0)
1081479Skas 		mp->m_flag |= MREAD|MSTATUS;
1091219Skas }
1101219Skas 
1111219Skas /*
1121219Skas  * Test to see if the passed file name is a directory.
1131219Skas  * Return true if it is.
1141219Skas  */
1151219Skas 
1161219Skas isdir(name)
1171219Skas 	char name[];
1181219Skas {
1191219Skas 	struct stat sbuf;
1201219Skas 
1211219Skas 	if (stat(name, &sbuf) < 0)
1221219Skas 		return(0);
1231219Skas 	return((sbuf.st_mode & S_IFMT) == S_IFDIR);
1241219Skas }
1251219Skas 
1261219Skas /*
1271219Skas  * Compute the size in characters of the passed message
1281219Skas  */
1291219Skas 
1301219Skas unsigned int
1311219Skas msize(messp)
1321219Skas 	struct message *messp;
1331219Skas {
1341219Skas 	register struct message *mp;
1351219Skas 
1361219Skas 	mp = messp;
1371219Skas 	return(mp->m_size);
1381219Skas }
1391219Skas 
1401219Skas /*
1411219Skas  * Count the number of arguments in the given string raw list.
1421219Skas  */
1431219Skas 
1441219Skas argcount(argv)
1451219Skas 	char **argv;
1461219Skas {
1471219Skas 	register char **ap;
1481219Skas 
1491219Skas 	for (ap = argv; *ap != NOSTR; ap++)
1501219Skas 		;
1511219Skas 	return(ap-argv);
1521219Skas }
1531219Skas 
1541219Skas /*
1551219Skas  * Given a file address, determine the
1561219Skas  * block number it represents.
1571219Skas  */
1581219Skas 
1591219Skas blockof(off)
1601219Skas 	off_t off;
1611219Skas {
1621219Skas 	off_t a;
1631219Skas 
1641219Skas 	a = off >> 9;
1651219Skas 	a &= 077777;
1661219Skas 	return((int) a);
1671219Skas }
1681219Skas 
1691219Skas /*
1701219Skas  * Take a file address, and determine
1711219Skas  * its offset in the current block.
1721219Skas  */
1731219Skas 
1741219Skas offsetof(off)
1751219Skas 	off_t off;
1761219Skas {
1771219Skas 	off_t a;
1781219Skas 
1791219Skas 	a = off & 0777;
1801219Skas 	return((int) a);
1811219Skas }
1821219Skas 
1831219Skas /*
1841219Skas  * Determine if the passed file is actually a tty, via a call to
1851219Skas  * gtty.  This is not totally reliable, but . . .
1861219Skas  */
1871219Skas 
1881219Skas isatty(f)
1891219Skas {
1901219Skas 	struct sgttyb buf;
1911219Skas 
1921219Skas 	if (gtty(f, &buf) < 0)
1931219Skas 		return(0);
1941219Skas 	return(1);
1951219Skas }
1961219Skas 
1971219Skas /*
1981219Skas  * Return the desired header line from the passed message
1991219Skas  * pointer (or NOSTR if the desired header field is not available).
2001219Skas  */
2011219Skas 
2021219Skas char *
2031219Skas hfield(field, mp)
2041219Skas 	char field[];
2051219Skas 	struct message *mp;
2061219Skas {
2071219Skas 	register FILE *ibuf;
2081219Skas 	char linebuf[LINESIZE];
2091219Skas 	register int lc;
2101219Skas 
2111219Skas 	ibuf = setinput(mp);
2121219Skas 	if ((lc = mp->m_lines) <= 0)
2131219Skas 		return(NOSTR);
2141219Skas 	if (readline(ibuf, linebuf) < 0)
2151219Skas 		return(NOSTR);
2161219Skas 	lc--;
2171219Skas 	do {
2181219Skas 		lc = gethfield(ibuf, linebuf, lc);
2191219Skas 		if (lc == -1)
2201219Skas 			return(NOSTR);
2211219Skas 		if (ishfield(linebuf, field))
2221219Skas 			return(savestr(hcontents(linebuf)));
2231219Skas 	} while (lc > 0);
2241219Skas 	return(NOSTR);
2251219Skas }
2261219Skas 
2271219Skas /*
2281219Skas  * Return the next header field found in the given message.
2291219Skas  * Return > 0 if something found, <= 0 elsewise.
2301219Skas  * Must deal with \ continuations & other such fraud.
2311219Skas  */
2321219Skas 
2331219Skas gethfield(f, linebuf, rem)
2341219Skas 	register FILE *f;
2351219Skas 	char linebuf[];
2361219Skas 	register int rem;
2371219Skas {
2381219Skas 	char line2[LINESIZE];
2391219Skas 	long loc;
2401219Skas 	register char *cp, *cp2;
2411219Skas 	register int c;
2421219Skas 
2431219Skas 
2441219Skas 	for (;;) {
2451219Skas 		if (rem <= 0)
2461219Skas 			return(-1);
2471219Skas 		if (readline(f, linebuf) < 0)
2481219Skas 			return(-1);
2491219Skas 		rem--;
2501219Skas 		if (strlen(linebuf) == 0)
2511219Skas 			return(-1);
2521219Skas 		if (isspace(linebuf[0]))
2531219Skas 			continue;
2541219Skas 		if (linebuf[0] == '>')
2551219Skas 			continue;
2561219Skas 		cp = index(linebuf, ':');
2571219Skas 		if (cp == NOSTR)
2581219Skas 			continue;
2591219Skas 		for (cp2 = linebuf; cp2 < cp; cp2++)
2601219Skas 			if (isdigit(*cp2))
2611219Skas 				continue;
2621219Skas 
2631219Skas 		/*
2641219Skas 		 * I guess we got a headline.
2651219Skas 		 * Handle wraparounding
2661219Skas 		 */
2671219Skas 
2681219Skas 		for (;;) {
2691219Skas 			if (rem <= 0)
2701219Skas 				break;
2711219Skas #ifdef CANTELL
2721219Skas 			loc = ftell(f);
2731219Skas 			if (readline(f, line2) < 0)
2741219Skas 				break;
2751219Skas 			rem--;
2761219Skas 			if (!isspace(line2[0])) {
2771219Skas 				fseek(f, loc, 0);
2781219Skas 				rem++;
2791219Skas 				break;
2801219Skas 			}
2811219Skas #else
2821219Skas 			c = getc(f);
2831219Skas 			ungetc(c, f);
2841219Skas 			if (!isspace(c) || c == '\n')
2851219Skas 				break;
2861219Skas 			if (readline(f, line2) < 0)
2871219Skas 				break;
2881219Skas 			rem--;
2891219Skas #endif
2901219Skas 			cp2 = line2;
2911219Skas 			for (cp2 = line2; *cp2 != 0 && isspace(*cp2); cp2++)
2921219Skas 				;
2931219Skas 			if (strlen(linebuf) + strlen(cp2) >= LINESIZE-2)
2941219Skas 				break;
2951219Skas 			cp = &linebuf[strlen(linebuf)];
2961219Skas 			while (cp > linebuf &&
2971219Skas 			    (isspace(cp[-1]) || cp[-1] == '\\'))
2981219Skas 				cp--;
2991219Skas 			*cp++ = ' ';
3001219Skas 			for (cp2 = line2; *cp2 != 0 && isspace(*cp2); cp2++)
3011219Skas 				;
3021219Skas 			strcpy(cp, cp2);
3031219Skas 		}
3041219Skas 		if ((c = strlen(linebuf)) > 0) {
3051219Skas 			cp = &linebuf[c-1];
3061219Skas 			while (cp > linebuf && isspace(*cp))
3071219Skas 				cp--;
3081219Skas 			*++cp = 0;
3091219Skas 		}
3101219Skas 		return(rem);
3111219Skas 	}
3121219Skas 	/* NOTREACHED */
3131219Skas }
3141219Skas 
3151219Skas /*
3161219Skas  * Check whether the passed line is a header line of
3171219Skas  * the desired breed.
3181219Skas  */
3191219Skas 
3201219Skas ishfield(linebuf, field)
3211219Skas 	char linebuf[], field[];
3221219Skas {
3231219Skas 	register char *cp;
3241219Skas 	register int c;
3251219Skas 
3261219Skas 	if ((cp = index(linebuf, ':')) == NOSTR)
3271219Skas 		return(0);
3281219Skas 	if (cp == linebuf)
3291219Skas 		return(0);
3301219Skas 	cp--;
3311219Skas 	while (cp > linebuf && isspace(*cp))
3321219Skas 		cp--;
3331219Skas 	c = *++cp;
3341219Skas 	*cp = 0;
3351219Skas 	if (icequal(linebuf ,field)) {
3361219Skas 		*cp = c;
3371219Skas 		return(1);
3381219Skas 	}
3391219Skas 	*cp = c;
3401219Skas 	return(0);
3411219Skas }
3421219Skas 
3431219Skas /*
3441219Skas  * Extract the non label information from the given header field
3451219Skas  * and return it.
3461219Skas  */
3471219Skas 
3481219Skas char *
3491219Skas hcontents(hfield)
3501219Skas 	char hfield[];
3511219Skas {
3521219Skas 	register char *cp;
3531219Skas 
3541219Skas 	if ((cp = index(hfield, ':')) == NOSTR)
3551219Skas 		return(NOSTR);
3561219Skas 	cp++;
3571219Skas 	while (*cp && isspace(*cp))
3581219Skas 		cp++;
3591219Skas 	return(cp);
3601219Skas }
3611219Skas 
3621219Skas /*
3631219Skas  * Compare two strings, ignoring case.
3641219Skas  */
3651219Skas 
3661219Skas icequal(s1, s2)
3671219Skas 	register char *s1, *s2;
3681219Skas {
3691219Skas 
3701219Skas 	while (raise(*s1++) == raise(*s2))
3711219Skas 		if (*s2++ == 0)
3721219Skas 			return(1);
3731219Skas 	return(0);
3741219Skas }
3751219Skas 
3761219Skas /*
3771219Skas  * The following code deals with input stacking to do source
3781219Skas  * commands.  All but the current file pointer are saved on
3791219Skas  * the stack.
3801219Skas  */
3811219Skas 
3821219Skas static	int	ssp = -1;		/* Top of file stack */
383*1519Skas struct sstack {
384*1519Skas 	FILE	*s_file;		/* File we were in. */
385*1519Skas 	int	s_cond;			/* Saved state of conditionals */
386*1519Skas } sstack[_NFILE];
3871219Skas 
3881219Skas /*
3891219Skas  * Pushdown current input file and switch to a new one.
3901219Skas  * Set the global flag "sourcing" so that others will realize
3911219Skas  * that they are no longer reading from a tty (in all probability).
3921219Skas  */
3931219Skas 
3941219Skas source(name)
3951219Skas 	char name[];
3961219Skas {
3971219Skas 	register FILE *fi;
3981219Skas 
3991219Skas 	if ((fi = fopen(name, "r")) == NULL) {
4001219Skas 		perror(name);
4011219Skas 		return(1);
4021219Skas 	}
4031219Skas 	if (ssp >= _NFILE-2) {
4041219Skas 		printf("Too much \"sourcing\" going on.\n");
4051219Skas 		fclose(fi);
4061219Skas 		return(1);
4071219Skas 	}
408*1519Skas 	sstack[++ssp].s_file = input;
409*1519Skas 	sstack[ssp].s_cond = cond;
410*1519Skas 	cond = CANY;
4111219Skas 	input = fi;
4121219Skas 	sourcing++;
4131219Skas 	return(0);
4141219Skas }
4151219Skas 
4161219Skas /*
4171219Skas  * Source a file, but do nothing if the file cannot be opened.
4181219Skas  */
4191219Skas 
4201219Skas source1(name)
4211219Skas 	char name[];
4221219Skas {
4231219Skas 	register int f;
4241219Skas 
4251219Skas 	if ((f = open(name, 0)) < 0)
4261219Skas 		return(0);
4271219Skas 	close(f);
4281219Skas 	source(name);
4291219Skas }
4301219Skas 
4311219Skas /*
4321219Skas  * Pop the current input back to the previous level.
4331219Skas  * Update the "sourcing" flag as appropriate.
4341219Skas  */
4351219Skas 
4361219Skas unstack()
4371219Skas {
4381219Skas 	if (ssp < 0) {
4391219Skas 		printf("\"Source\" stack over-pop.\n");
4401219Skas 		sourcing = 0;
4411219Skas 		return(1);
4421219Skas 	}
4431219Skas 	fclose(input);
444*1519Skas 	if (cond != CANY)
445*1519Skas 		printf("Unmatched \"if\"\n");
446*1519Skas 	cond = sstack[ssp].s_cond;
447*1519Skas 	input = sstack[ssp--].s_file;
4481219Skas 	if (ssp < 0)
4491219Skas 		sourcing = 0;
4501219Skas 	return(0);
4511219Skas }
4521219Skas 
4531219Skas /*
4541219Skas  * Touch the indicated file.
4551219Skas  * This is nifty for the shell.
4561219Skas  * If we have the utime() system call, this is better served
4571219Skas  * by using that, since it will work for empty files.
4581219Skas  * On non-utime systems, we must sleep a second, then read.
4591219Skas  */
4601219Skas 
4611219Skas alter(name)
4621219Skas 	char name[];
4631219Skas {
4641219Skas #ifdef UTIME
4651219Skas 	struct stat statb;
4661219Skas 	long time();
4671219Skas 	time_t time_p[2];
4681219Skas #else
4691219Skas 	register int pid, f;
4701219Skas 	char w;
4711219Skas #endif UTIME
4721219Skas 
4731219Skas #ifdef UTIME
4741219Skas 	if (stat(name, &statb) < 0)
4751219Skas 		return;
4761219Skas 	time_p[0] = time((long *) 0) + 1;
4771219Skas 	time_p[1] = statb.st_mtime;
4781219Skas 	utime(name, time_p);
4791219Skas #else
4801219Skas 	if ((pid = fork()) != 0)
4811219Skas 		return;
4821219Skas 	clrbuf(stdout);
4831219Skas 	clrbuf(stderr);
4841219Skas 	clrbuf(stdin);
4851219Skas 	sleep(1);
4861219Skas 	if ((f = open(name, 0)) < 0)
4871219Skas 		exit(1);
4881219Skas 	read(f, &w, 1);
4891219Skas 	exit(0);
4901219Skas #endif
4911219Skas }
4921219Skas 
4931219Skas /*
4941219Skas  * Examine the passed line buffer and
4951219Skas  * return true if it is all blanks and tabs.
4961219Skas  */
4971219Skas 
4981219Skas blankline(linebuf)
4991219Skas 	char linebuf[];
5001219Skas {
5011219Skas 	register char *cp;
5021219Skas 
5031219Skas 	for (cp = linebuf; *cp; cp++)
5041219Skas 		if (!any(*cp, " \t"))
5051219Skas 			return(0);
5061219Skas 	return(1);
5071219Skas }
5081219Skas 
5091219Skas /*
5101219Skas  * Fetch the sender's name from the passed message.
5111219Skas  */
5121219Skas 
5131219Skas char *
5141219Skas nameof(mp)
5151219Skas 	register struct message *mp;
5161219Skas {
5171219Skas 	char namebuf[LINESIZE];
5181219Skas 	char linebuf[LINESIZE];
5191219Skas 	register char *cp, *cp2;
5201219Skas 	register FILE *ibuf;
5211219Skas 	int first = 1;
5221219Skas 
5231219Skas 	if ((cp = hfield("reply-to", mp)) != NOSTR) {
5241219Skas 		strcpy(namebuf, cp);
5251219Skas 		return(namebuf);
5261219Skas 	}
5271219Skas 	ibuf = setinput(mp);
5281219Skas 	copy("", namebuf);
5291219Skas 	if (readline(ibuf, linebuf) <= 0)
5301219Skas 		return(savestr(namebuf));
5311219Skas newname:
5321219Skas 	for (cp = linebuf; *cp != ' '; cp++)
5331219Skas 		;
5341219Skas 	while (any(*cp, " \t"))
5351219Skas 		cp++;
5361219Skas 	for (cp2 = &namebuf[strlen(namebuf)]; *cp && !any(*cp, " \t") &&
5371219Skas 	    cp2-namebuf < LINESIZE-1; *cp2++ = *cp++)
5381219Skas 		;
5391219Skas 	*cp2 = '\0';
5401219Skas 	if (readline(ibuf, linebuf) <= 0)
5411219Skas 		return(savestr(namebuf));
5421219Skas 	if ((cp = index(linebuf, 'F')) == NULL)
5431219Skas 		return(savestr(namebuf));
5441219Skas 	if (strncmp(cp, "From", 4) != 0)
5451219Skas 		return(savestr(namebuf));
5461219Skas 	while ((cp = index(cp, 'r')) != NULL) {
5471219Skas 		if (strncmp(cp, "remote", 6) == 0) {
5481219Skas 			if ((cp = index(cp, 'f')) == NULL)
5491219Skas 				break;
5501219Skas 			if (strncmp(cp, "from", 4) != 0)
5511219Skas 				break;
5521219Skas 			if ((cp = index(cp, ' ')) == NULL)
5531219Skas 				break;
5541219Skas 			cp++;
5551219Skas 			if (first) {
5561219Skas 				copy(cp, namebuf);
5571219Skas 				first = 0;
5581219Skas 			} else
5591219Skas 				strcpy(rindex(namebuf, '!')+1, cp);
5601219Skas 			strcat(namebuf, "!");
5611219Skas 			goto newname;
5621219Skas 		}
5631219Skas 		cp++;
5641219Skas 	}
5651219Skas 	return(savestr(namebuf));
5661219Skas }
5671219Skas 
5681219Skas /*
5691219Skas  * Find the rightmost pointer to an instance of the
5701219Skas  * character in the string and return it.
5711219Skas  */
5721219Skas 
5731219Skas char *
5741219Skas rindex(str, c)
5751219Skas 	char str[];
5761219Skas 	register int c;
5771219Skas {
5781219Skas 	register char *cp, *cp2;
5791219Skas 
5801219Skas 	for (cp = str, cp2 = NOSTR; *cp; cp++)
5811219Skas 		if (c == *cp)
5821219Skas 			cp2 = cp;
5831219Skas 	return(cp2);
5841219Skas }
5851219Skas 
5861219Skas /*
5871219Skas  * See if the string is a number.
5881219Skas  */
5891219Skas 
5901219Skas numeric(str)
5911219Skas 	char str[];
5921219Skas {
5931219Skas 	register char *cp = str;
5941219Skas 
5951219Skas 	while (*cp)
5961219Skas 		if (!isdigit(*cp++))
5971219Skas 			return(0);
5981219Skas 	return(1);
5991219Skas }
6001219Skas 
6011219Skas /*
6021219Skas  * Are any of the characters in the two strings the same?
6031219Skas  */
6041219Skas 
6051219Skas anyof(s1, s2)
6061219Skas 	register char *s1, *s2;
6071219Skas {
6081219Skas 	register int c;
6091219Skas 
6101219Skas 	while (c = *s1++)
6111219Skas 		if (any(c, s2))
6121219Skas 			return(1);
6131219Skas 	return(0);
6141219Skas }
6151219Skas 
6161219Skas /*
6171219Skas  * Determine the leftmost index of the character
6181219Skas  * in the string.
6191219Skas  */
6201219Skas 
6211219Skas char *
6221219Skas index(str, ch)
6231219Skas 	char *str;
6241219Skas {
6251219Skas 	register char *cp;
6261219Skas 	register int c;
6271219Skas 
6281219Skas 	for (c = ch, cp = str; *cp; cp++)
6291219Skas 		if (*cp == c)
6301219Skas 			return(cp);
6311219Skas 	return(NOSTR);
6321219Skas }
6331219Skas 
6341219Skas /*
6351219Skas  * String compare two strings of bounded length.
6361219Skas  */
6371219Skas 
6381219Skas strncmp(as1, as2, an)
6391219Skas 	char *as1, *as2;
6401219Skas {
6411219Skas 	register char *s1, *s2;
6421219Skas 	register int n;
6431219Skas 
6441219Skas 	s1 = as1;
6451219Skas 	s2 = as2;
6461219Skas 	n = an;
6471219Skas 	while (--n >= 0 && *s1 == *s2++)
6481219Skas 		if (*s1++ == '\0')
6491219Skas 			return(0);
6501219Skas 	return(n<0 ? 0 : *s1 - *--s2);
6511219Skas }
6521219Skas 
653