misc.c revision 2311
12311Sjkh/* Copyright 1988,1990,1993,1994 by Paul Vixie
22311Sjkh * All rights reserved
32311Sjkh *
42311Sjkh * Distribute freely, except: don't remove my name from the source or
52311Sjkh * documentation (don't take credit for my work), mark your changes (don't
62311Sjkh * get me blamed for your possible bugs), don't alter or remove this
72311Sjkh * notice.  May be sold if buildable source is provided to buyer.  No
82311Sjkh * warrantee of any kind, express or implied, is included with this
92311Sjkh * software; use at your own risk, responsibility for damages (if any) to
102311Sjkh * anyone resulting from the use of this software rests entirely with the
112311Sjkh * user.
122311Sjkh *
132311Sjkh * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
142311Sjkh * I'll try to keep a version up to date.  I can be reached as follows:
152311Sjkh * Paul Vixie          <paul@vix.com>          uunet!decwrl!vixie!paul
162311Sjkh */
172311Sjkh
182311Sjkh#if !defined(lint) && !defined(LINT)
192311Sjkhstatic char rcsid[] = "$Id: misc.c,v 2.9 1994/01/15 20:43:43 vixie Exp $";
202311Sjkh#endif
212311Sjkh
222311Sjkh/* vix 26jan87 [RCS has the rest of the log]
232311Sjkh * vix 30dec86 [written]
242311Sjkh */
252311Sjkh
262311Sjkh
272311Sjkh#include "cron.h"
282311Sjkh#if SYS_TIME_H
292311Sjkh# include <sys/time.h>
302311Sjkh#else
312311Sjkh# include <time.h>
322311Sjkh#endif
332311Sjkh#include <sys/file.h>
342311Sjkh#include <sys/stat.h>
352311Sjkh#include <errno.h>
362311Sjkh#include <string.h>
372311Sjkh#include <fcntl.h>
382311Sjkh#if defined(SYSLOG)
392311Sjkh# include <syslog.h>
402311Sjkh#endif
412311Sjkh
422311Sjkh
432311Sjkh#if defined(LOG_DAEMON) && !defined(LOG_CRON)
442311Sjkh#define LOG_CRON LOG_DAEMON
452311Sjkh#endif
462311Sjkh
472311Sjkh
482311Sjkhstatic int		LogFD = ERR;
492311Sjkh
502311Sjkh
512311Sjkhint
522311Sjkhstrcmp_until(left, right, until)
532311Sjkh	char	*left;
542311Sjkh	char	*right;
552311Sjkh	int	until;
562311Sjkh{
572311Sjkh	register int	diff;
582311Sjkh
592311Sjkh	while (*left && *left != until && *left == *right) {
602311Sjkh		left++;
612311Sjkh		right++;
622311Sjkh	}
632311Sjkh
642311Sjkh	if ((*left=='\0' || *left == until) &&
652311Sjkh	    (*right=='\0' || *right == until)) {
662311Sjkh		diff = 0;
672311Sjkh	} else {
682311Sjkh		diff = *left - *right;
692311Sjkh	}
702311Sjkh
712311Sjkh	return diff;
722311Sjkh}
732311Sjkh
742311Sjkh
752311Sjkh/* strdtb(s) - delete trailing blanks in string 's' and return new length
762311Sjkh */
772311Sjkhint
782311Sjkhstrdtb(s)
792311Sjkh	char	*s;
802311Sjkh{
812311Sjkh	char	*x = s;
822311Sjkh
832311Sjkh	/* scan forward to the null
842311Sjkh	 */
852311Sjkh	while (*x)
862311Sjkh		x++;
872311Sjkh
882311Sjkh	/* scan backward to either the first character before the string,
892311Sjkh	 * or the last non-blank in the string, whichever comes first.
902311Sjkh	 */
912311Sjkh	do	{x--;}
922311Sjkh	while (x >= s && isspace(*x));
932311Sjkh
942311Sjkh	/* one character beyond where we stopped above is where the null
952311Sjkh	 * goes.
962311Sjkh	 */
972311Sjkh	*++x = '\0';
982311Sjkh
992311Sjkh	/* the difference between the position of the null character and
1002311Sjkh	 * the position of the first character of the string is the length.
1012311Sjkh	 */
1022311Sjkh	return x - s;
1032311Sjkh}
1042311Sjkh
1052311Sjkh
1062311Sjkhint
1072311Sjkhset_debug_flags(flags)
1082311Sjkh	char	*flags;
1092311Sjkh{
1102311Sjkh	/* debug flags are of the form    flag[,flag ...]
1112311Sjkh	 *
1122311Sjkh	 * if an error occurs, print a message to stdout and return FALSE.
1132311Sjkh	 * otherwise return TRUE after setting ERROR_FLAGS.
1142311Sjkh	 */
1152311Sjkh
1162311Sjkh#if !DEBUGGING
1172311Sjkh
1182311Sjkh	printf("this program was compiled without debugging enabled\n");
1192311Sjkh	return FALSE;
1202311Sjkh
1212311Sjkh#else /* DEBUGGING */
1222311Sjkh
1232311Sjkh	char	*pc = flags;
1242311Sjkh
1252311Sjkh	DebugFlags = 0;
1262311Sjkh
1272311Sjkh	while (*pc) {
1282311Sjkh		char	**test;
1292311Sjkh		int	mask;
1302311Sjkh
1312311Sjkh		/* try to find debug flag name in our list.
1322311Sjkh		 */
1332311Sjkh		for (	test = DebugFlagNames, mask = 1;
1342311Sjkh			*test && strcmp_until(*test, pc, ',');
1352311Sjkh			test++, mask <<= 1
1362311Sjkh		    )
1372311Sjkh			;
1382311Sjkh
1392311Sjkh		if (!*test) {
1402311Sjkh			fprintf(stderr,
1412311Sjkh				"unrecognized debug flag <%s> <%s>\n",
1422311Sjkh				flags, pc);
1432311Sjkh			return FALSE;
1442311Sjkh		}
1452311Sjkh
1462311Sjkh		DebugFlags |= mask;
1472311Sjkh
1482311Sjkh		/* skip to the next flag
1492311Sjkh		 */
1502311Sjkh		while (*pc && *pc != ',')
1512311Sjkh			pc++;
1522311Sjkh		if (*pc == ',')
1532311Sjkh			pc++;
1542311Sjkh	}
1552311Sjkh
1562311Sjkh	if (DebugFlags) {
1572311Sjkh		int	flag;
1582311Sjkh
1592311Sjkh		fprintf(stderr, "debug flags enabled:");
1602311Sjkh
1612311Sjkh		for (flag = 0;  DebugFlagNames[flag];  flag++)
1622311Sjkh			if (DebugFlags & (1 << flag))
1632311Sjkh				fprintf(stderr, " %s", DebugFlagNames[flag]);
1642311Sjkh		fprintf(stderr, "\n");
1652311Sjkh	}
1662311Sjkh
1672311Sjkh	return TRUE;
1682311Sjkh
1692311Sjkh#endif /* DEBUGGING */
1702311Sjkh}
1712311Sjkh
1722311Sjkh
1732311Sjkhvoid
1742311Sjkhset_cron_uid()
1752311Sjkh{
1762311Sjkh#if defined(BSD) || defined(POSIX)
1772311Sjkh	if (seteuid(ROOT_UID) < OK) {
1782311Sjkh		perror("seteuid");
1792311Sjkh		exit(ERROR_EXIT);
1802311Sjkh	}
1812311Sjkh#else
1822311Sjkh	if (setuid(ROOT_UID) < OK) {
1832311Sjkh		perror("setuid");
1842311Sjkh		exit(ERROR_EXIT);
1852311Sjkh	}
1862311Sjkh#endif
1872311Sjkh}
1882311Sjkh
1892311Sjkh
1902311Sjkhvoid
1912311Sjkhset_cron_cwd()
1922311Sjkh{
1932311Sjkh	struct stat	sb;
1942311Sjkh
1952311Sjkh	/* first check for CRONDIR ("/var/cron" or some such)
1962311Sjkh	 */
1972311Sjkh	if (stat(CRONDIR, &sb) < OK && errno == ENOENT) {
1982311Sjkh		perror(CRONDIR);
1992311Sjkh		if (OK == mkdir(CRONDIR, 0700)) {
2002311Sjkh			fprintf(stderr, "%s: created\n", CRONDIR);
2012311Sjkh			stat(CRONDIR, &sb);
2022311Sjkh		} else {
2032311Sjkh			fprintf(stderr, "%s: ", CRONDIR);
2042311Sjkh			perror("mkdir");
2052311Sjkh			exit(ERROR_EXIT);
2062311Sjkh		}
2072311Sjkh	}
2082311Sjkh	if (!(sb.st_mode & S_IFDIR)) {
2092311Sjkh		fprintf(stderr, "'%s' is not a directory, bailing out.\n",
2102311Sjkh			CRONDIR);
2112311Sjkh		exit(ERROR_EXIT);
2122311Sjkh	}
2132311Sjkh	if (chdir(CRONDIR) < OK) {
2142311Sjkh		fprintf(stderr, "cannot chdir(%s), bailing out.\n", CRONDIR);
2152311Sjkh		perror(CRONDIR);
2162311Sjkh		exit(ERROR_EXIT);
2172311Sjkh	}
2182311Sjkh
2192311Sjkh	/* CRONDIR okay (now==CWD), now look at SPOOL_DIR ("tabs" or some such)
2202311Sjkh	 */
2212311Sjkh	if (stat(SPOOL_DIR, &sb) < OK && errno == ENOENT) {
2222311Sjkh		perror(SPOOL_DIR);
2232311Sjkh		if (OK == mkdir(SPOOL_DIR, 0700)) {
2242311Sjkh			fprintf(stderr, "%s: created\n", SPOOL_DIR);
2252311Sjkh			stat(SPOOL_DIR, &sb);
2262311Sjkh		} else {
2272311Sjkh			fprintf(stderr, "%s: ", SPOOL_DIR);
2282311Sjkh			perror("mkdir");
2292311Sjkh			exit(ERROR_EXIT);
2302311Sjkh		}
2312311Sjkh	}
2322311Sjkh	if (!(sb.st_mode & S_IFDIR)) {
2332311Sjkh		fprintf(stderr, "'%s' is not a directory, bailing out.\n",
2342311Sjkh			SPOOL_DIR);
2352311Sjkh		exit(ERROR_EXIT);
2362311Sjkh	}
2372311Sjkh}
2382311Sjkh
2392311Sjkh
2402311Sjkh/* acquire_daemonlock() - write our PID into /etc/cron.pid, unless
2412311Sjkh *	another daemon is already running, which we detect here.
2422311Sjkh *
2432311Sjkh * note: main() calls us twice; once before forking, once after.
2442311Sjkh *	we maintain static storage of the file pointer so that we
2452311Sjkh *	can rewrite our PID into the PIDFILE after the fork.
2462311Sjkh *
2472311Sjkh * it would be great if fflush() disassociated the file buffer.
2482311Sjkh */
2492311Sjkhvoid
2502311Sjkhacquire_daemonlock(closeflag)
2512311Sjkh	int closeflag;
2522311Sjkh{
2532311Sjkh	static	FILE	*fp = NULL;
2542311Sjkh
2552311Sjkh	if (closeflag && fp) {
2562311Sjkh		fclose(fp);
2572311Sjkh		fp = NULL;
2582311Sjkh		return;
2592311Sjkh	}
2602311Sjkh
2612311Sjkh	if (!fp) {
2622311Sjkh		char	pidfile[MAX_FNAME];
2632311Sjkh		char	buf[MAX_TEMPSTR];
2642311Sjkh		int	fd, otherpid;
2652311Sjkh
2662311Sjkh		(void) sprintf(pidfile, PIDFILE, PIDDIR);
2672311Sjkh		if ((-1 == (fd = open(pidfile, O_RDWR|O_CREAT, 0644)))
2682311Sjkh		    || (NULL == (fp = fdopen(fd, "r+")))
2692311Sjkh		    ) {
2702311Sjkh			sprintf(buf, "can't open or create %s: %s",
2712311Sjkh				pidfile, strerror(errno));
2722311Sjkh			fprintf(stderr, "%s: %s\n", ProgramName, buf);
2732311Sjkh			log_it("CRON", getpid(), "DEATH", buf);
2742311Sjkh			exit(ERROR_EXIT);
2752311Sjkh		}
2762311Sjkh
2772311Sjkh		if (flock(fd, LOCK_EX|LOCK_NB) < OK) {
2782311Sjkh			int save_errno = errno;
2792311Sjkh
2802311Sjkh			fscanf(fp, "%d", &otherpid);
2812311Sjkh			sprintf(buf, "can't lock %s, otherpid may be %d: %s",
2822311Sjkh				pidfile, otherpid, strerror(save_errno));
2832311Sjkh			fprintf(stderr, "%s: %s\n", ProgramName, buf);
2842311Sjkh			log_it("CRON", getpid(), "DEATH", buf);
2852311Sjkh			exit(ERROR_EXIT);
2862311Sjkh		}
2872311Sjkh
2882311Sjkh		(void) fcntl(fd, F_SETFD, 1);
2892311Sjkh	}
2902311Sjkh
2912311Sjkh	rewind(fp);
2922311Sjkh	fprintf(fp, "%d\n", getpid());
2932311Sjkh	fflush(fp);
2942311Sjkh	(void) ftruncate(fileno(fp), ftell(fp));
2952311Sjkh
2962311Sjkh	/* abandon fd and fp even though the file is open. we need to
2972311Sjkh	 * keep it open and locked, but we don't need the handles elsewhere.
2982311Sjkh	 */
2992311Sjkh}
3002311Sjkh
3012311Sjkh/* get_char(file) : like getc() but increment LineNumber on newlines
3022311Sjkh */
3032311Sjkhint
3042311Sjkhget_char(file)
3052311Sjkh	FILE	*file;
3062311Sjkh{
3072311Sjkh	int	ch;
3082311Sjkh
3092311Sjkh	ch = getc(file);
3102311Sjkh	if (ch == '\n')
3112311Sjkh		Set_LineNum(LineNumber + 1)
3122311Sjkh	return ch;
3132311Sjkh}
3142311Sjkh
3152311Sjkh
3162311Sjkh/* unget_char(ch, file) : like ungetc but do LineNumber processing
3172311Sjkh */
3182311Sjkhvoid
3192311Sjkhunget_char(ch, file)
3202311Sjkh	int	ch;
3212311Sjkh	FILE	*file;
3222311Sjkh{
3232311Sjkh	ungetc(ch, file);
3242311Sjkh	if (ch == '\n')
3252311Sjkh		Set_LineNum(LineNumber - 1)
3262311Sjkh}
3272311Sjkh
3282311Sjkh
3292311Sjkh/* get_string(str, max, file, termstr) : like fgets() but
3302311Sjkh *		(1) has terminator string which should include \n
3312311Sjkh *		(2) will always leave room for the null
3322311Sjkh *		(3) uses get_char() so LineNumber will be accurate
3332311Sjkh *		(4) returns EOF or terminating character, whichever
3342311Sjkh */
3352311Sjkhint
3362311Sjkhget_string(string, size, file, terms)
3372311Sjkh	char	*string;
3382311Sjkh	int	size;
3392311Sjkh	FILE	*file;
3402311Sjkh	char	*terms;
3412311Sjkh{
3422311Sjkh	int	ch;
3432311Sjkh
3442311Sjkh	while (EOF != (ch = get_char(file)) && !strchr(terms, ch)) {
3452311Sjkh		if (size > 1) {
3462311Sjkh			*string++ = (char) ch;
3472311Sjkh			size--;
3482311Sjkh		}
3492311Sjkh	}
3502311Sjkh
3512311Sjkh	if (size > 0)
3522311Sjkh		*string = '\0';
3532311Sjkh
3542311Sjkh	return ch;
3552311Sjkh}
3562311Sjkh
3572311Sjkh
3582311Sjkh/* skip_comments(file) : read past comment (if any)
3592311Sjkh */
3602311Sjkhvoid
3612311Sjkhskip_comments(file)
3622311Sjkh	FILE	*file;
3632311Sjkh{
3642311Sjkh	int	ch;
3652311Sjkh
3662311Sjkh	while (EOF != (ch = get_char(file))) {
3672311Sjkh		/* ch is now the first character of a line.
3682311Sjkh		 */
3692311Sjkh
3702311Sjkh		while (ch == ' ' || ch == '\t')
3712311Sjkh			ch = get_char(file);
3722311Sjkh
3732311Sjkh		if (ch == EOF)
3742311Sjkh			break;
3752311Sjkh
3762311Sjkh		/* ch is now the first non-blank character of a line.
3772311Sjkh		 */
3782311Sjkh
3792311Sjkh		if (ch != '\n' && ch != '#')
3802311Sjkh			break;
3812311Sjkh
3822311Sjkh		/* ch must be a newline or comment as first non-blank
3832311Sjkh		 * character on a line.
3842311Sjkh		 */
3852311Sjkh
3862311Sjkh		while (ch != '\n' && ch != EOF)
3872311Sjkh			ch = get_char(file);
3882311Sjkh
3892311Sjkh		/* ch is now the newline of a line which we're going to
3902311Sjkh		 * ignore.
3912311Sjkh		 */
3922311Sjkh	}
3932311Sjkh	if (ch != EOF)
3942311Sjkh		unget_char(ch, file);
3952311Sjkh}
3962311Sjkh
3972311Sjkh
3982311Sjkh/* int in_file(char *string, FILE *file)
3992311Sjkh *	return TRUE if one of the lines in file matches string exactly,
4002311Sjkh *	FALSE otherwise.
4012311Sjkh */
4022311Sjkhstatic int
4032311Sjkhin_file(string, file)
4042311Sjkh	char *string;
4052311Sjkh	FILE *file;
4062311Sjkh{
4072311Sjkh	char line[MAX_TEMPSTR];
4082311Sjkh
4092311Sjkh	rewind(file);
4102311Sjkh	while (fgets(line, MAX_TEMPSTR, file)) {
4112311Sjkh		if (line[0] != '\0')
4122311Sjkh			line[strlen(line)-1] = '\0';
4132311Sjkh		if (0 == strcmp(line, string))
4142311Sjkh			return TRUE;
4152311Sjkh	}
4162311Sjkh	return FALSE;
4172311Sjkh}
4182311Sjkh
4192311Sjkh
4202311Sjkh/* int allowed(char *username)
4212311Sjkh *	returns TRUE if (ALLOW_FILE exists and user is listed)
4222311Sjkh *	or (DENY_FILE exists and user is NOT listed)
4232311Sjkh *	or (neither file exists but user=="root" so it's okay)
4242311Sjkh */
4252311Sjkhint
4262311Sjkhallowed(username)
4272311Sjkh	char *username;
4282311Sjkh{
4292311Sjkh	static int	init = FALSE;
4302311Sjkh	static FILE	*allow, *deny;
4312311Sjkh
4322311Sjkh	if (!init) {
4332311Sjkh		init = TRUE;
4342311Sjkh#if defined(ALLOW_FILE) && defined(DENY_FILE)
4352311Sjkh		allow = fopen(ALLOW_FILE, "r");
4362311Sjkh		deny = fopen(DENY_FILE, "r");
4372311Sjkh		Debug(DMISC, ("allow/deny enabled, %d/%d\n", !!allow, !!deny))
4382311Sjkh#else
4392311Sjkh		allow = NULL;
4402311Sjkh		deny = NULL;
4412311Sjkh#endif
4422311Sjkh	}
4432311Sjkh
4442311Sjkh	if (allow)
4452311Sjkh		return (in_file(username, allow));
4462311Sjkh	if (deny)
4472311Sjkh		return (!in_file(username, deny));
4482311Sjkh
4492311Sjkh#if defined(ALLOW_ONLY_ROOT)
4502311Sjkh	return (strcmp(username, ROOT_USER) == 0);
4512311Sjkh#else
4522311Sjkh	return TRUE;
4532311Sjkh#endif
4542311Sjkh}
4552311Sjkh
4562311Sjkh
4572311Sjkhvoid
4582311Sjkhlog_it(username, xpid, event, detail)
4592311Sjkh	char	*username;
4602311Sjkh	int	xpid;
4612311Sjkh	char	*event;
4622311Sjkh	char	*detail;
4632311Sjkh{
4642311Sjkh	PID_T			pid = xpid;
4652311Sjkh#if defined(LOG_FILE)
4662311Sjkh	char			*msg;
4672311Sjkh	TIME_T			now = time((TIME_T) 0);
4682311Sjkh	register struct tm	*t = localtime(&now);
4692311Sjkh#endif /*LOG_FILE*/
4702311Sjkh
4712311Sjkh#if defined(SYSLOG)
4722311Sjkh	static int		syslog_open = 0;
4732311Sjkh#endif
4742311Sjkh
4752311Sjkh#if defined(LOG_FILE)
4762311Sjkh	/* we assume that MAX_TEMPSTR will hold the date, time, &punctuation.
4772311Sjkh	 */
4782311Sjkh	msg = malloc(strlen(username)
4792311Sjkh		     + strlen(event)
4802311Sjkh		     + strlen(detail)
4812311Sjkh		     + MAX_TEMPSTR);
4822311Sjkh
4832311Sjkh	if (LogFD < OK) {
4842311Sjkh		LogFD = open(LOG_FILE, O_WRONLY|O_APPEND|O_CREAT, 0600);
4852311Sjkh		if (LogFD < OK) {
4862311Sjkh			fprintf(stderr, "%s: can't open log file\n",
4872311Sjkh				ProgramName);
4882311Sjkh			perror(LOG_FILE);
4892311Sjkh		} else {
4902311Sjkh			(void) fcntl(LogFD, F_SETFD, 1);
4912311Sjkh		}
4922311Sjkh	}
4932311Sjkh
4942311Sjkh	/* we have to sprintf() it because fprintf() doesn't always write
4952311Sjkh	 * everything out in one chunk and this has to be atomically appended
4962311Sjkh	 * to the log file.
4972311Sjkh	 */
4982311Sjkh	sprintf(msg, "%s (%02d/%02d-%02d:%02d:%02d-%d) %s (%s)\n",
4992311Sjkh		username,
5002311Sjkh		t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, pid,
5012311Sjkh		event, detail);
5022311Sjkh
5032311Sjkh	/* we have to run strlen() because sprintf() returns (char*) on old BSD
5042311Sjkh	 */
5052311Sjkh	if (LogFD < OK || write(LogFD, msg, strlen(msg)) < OK) {
5062311Sjkh		if (LogFD >= OK)
5072311Sjkh			perror(LOG_FILE);
5082311Sjkh		fprintf(stderr, "%s: can't write to log file\n", ProgramName);
5092311Sjkh		write(STDERR, msg, strlen(msg));
5102311Sjkh	}
5112311Sjkh
5122311Sjkh	free(msg);
5132311Sjkh#endif /*LOG_FILE*/
5142311Sjkh
5152311Sjkh#if defined(SYSLOG)
5162311Sjkh	if (!syslog_open) {
5172311Sjkh		/* we don't use LOG_PID since the pid passed to us by
5182311Sjkh		 * our client may not be our own.  therefore we want to
5192311Sjkh		 * print the pid ourselves.
5202311Sjkh		 */
5212311Sjkh# ifdef LOG_DAEMON
5222311Sjkh		openlog(ProgramName, LOG_PID, LOG_CRON);
5232311Sjkh# else
5242311Sjkh		openlog(ProgramName, LOG_PID);
5252311Sjkh# endif
5262311Sjkh		syslog_open = TRUE;		/* assume openlog success */
5272311Sjkh	}
5282311Sjkh
5292311Sjkh	syslog(LOG_INFO, "(%s) %s (%s)\n", username, event, detail);
5302311Sjkh
5312311Sjkh#endif /*SYSLOG*/
5322311Sjkh
5332311Sjkh#if DEBUGGING
5342311Sjkh	if (DebugFlags) {
5352311Sjkh		fprintf(stderr, "log_it: (%s %d) %s (%s)\n",
5362311Sjkh			username, pid, event, detail);
5372311Sjkh	}
5382311Sjkh#endif
5392311Sjkh}
5402311Sjkh
5412311Sjkh
5422311Sjkhvoid
5432311Sjkhlog_close() {
5442311Sjkh	if (LogFD != ERR) {
5452311Sjkh		close(LogFD);
5462311Sjkh		LogFD = ERR;
5472311Sjkh	}
5482311Sjkh}
5492311Sjkh
5502311Sjkh
5512311Sjkh/* two warnings:
5522311Sjkh *	(1) this routine is fairly slow
5532311Sjkh *	(2) it returns a pointer to static storage
5542311Sjkh */
5552311Sjkhchar *
5562311Sjkhfirst_word(s, t)
5572311Sjkh	register char *s;	/* string we want the first word of */
5582311Sjkh	register char *t;	/* terminators, implicitly including \0 */
5592311Sjkh{
5602311Sjkh	static char retbuf[2][MAX_TEMPSTR + 1];	/* sure wish C had GC */
5612311Sjkh	static int retsel = 0;
5622311Sjkh	register char *rb, *rp;
5632311Sjkh
5642311Sjkh	/* select a return buffer */
5652311Sjkh	retsel = 1-retsel;
5662311Sjkh	rb = &retbuf[retsel][0];
5672311Sjkh	rp = rb;
5682311Sjkh
5692311Sjkh	/* skip any leading terminators */
5702311Sjkh	while (*s && (NULL != strchr(t, *s))) {
5712311Sjkh		s++;
5722311Sjkh	}
5732311Sjkh
5742311Sjkh	/* copy until next terminator or full buffer */
5752311Sjkh	while (*s && (NULL == strchr(t, *s)) && (rp < &rb[MAX_TEMPSTR])) {
5762311Sjkh		*rp++ = *s++;
5772311Sjkh	}
5782311Sjkh
5792311Sjkh	/* finish the return-string and return it */
5802311Sjkh	*rp = '\0';
5812311Sjkh	return rb;
5822311Sjkh}
5832311Sjkh
5842311Sjkh
5852311Sjkh/* warning:
5862311Sjkh *	heavily ascii-dependent.
5872311Sjkh */
5882311Sjkhvoid
5892311Sjkhmkprint(dst, src, len)
5902311Sjkh	register char *dst;
5912311Sjkh	register unsigned char *src;
5922311Sjkh	register int len;
5932311Sjkh{
5942311Sjkh	while (len-- > 0)
5952311Sjkh	{
5962311Sjkh		register unsigned char ch = *src++;
5972311Sjkh
5982311Sjkh		if (ch < ' ') {			/* control character */
5992311Sjkh			*dst++ = '^';
6002311Sjkh			*dst++ = ch + '@';
6012311Sjkh		} else if (ch < 0177) {		/* printable */
6022311Sjkh			*dst++ = ch;
6032311Sjkh		} else if (ch == 0177) {	/* delete/rubout */
6042311Sjkh			*dst++ = '^';
6052311Sjkh			*dst++ = '?';
6062311Sjkh		} else {			/* parity character */
6072311Sjkh			sprintf(dst, "\\%03o", ch);
6082311Sjkh			dst += 4;
6092311Sjkh		}
6102311Sjkh	}
6112311Sjkh	*dst = '\0';
6122311Sjkh}
6132311Sjkh
6142311Sjkh
6152311Sjkh/* warning:
6162311Sjkh *	returns a pointer to malloc'd storage, you must call free yourself.
6172311Sjkh */
6182311Sjkhchar *
6192311Sjkhmkprints(src, len)
6202311Sjkh	register unsigned char *src;
6212311Sjkh	register unsigned int len;
6222311Sjkh{
6232311Sjkh	register char *dst = malloc(len*4 + 1);
6242311Sjkh
6252311Sjkh	mkprint(dst, src, len);
6262311Sjkh
6272311Sjkh	return dst;
6282311Sjkh}
6292311Sjkh
6302311Sjkh
6312311Sjkh#ifdef MAIL_DATE
6322311Sjkh/* Sat, 27 Feb 93 11:44:51 CST
6332311Sjkh * 123456789012345678901234567
6342311Sjkh */
6352311Sjkhchar *
6362311Sjkharpadate(clock)
6372311Sjkh	time_t *clock;
6382311Sjkh{
6392311Sjkh	time_t t = clock ?*clock :time(0L);
6402311Sjkh	struct tm *tm = localtime(&t);
6412311Sjkh	static char ret[30];	/* zone name might be >3 chars */
6422311Sjkh
6432311Sjkh	(void) sprintf(ret, "%s, %2d %s %2d %02d:%02d:%02d %s",
6442311Sjkh		       DowNames[tm->tm_wday],
6452311Sjkh		       tm->tm_mday,
6462311Sjkh		       MonthNames[tm->tm_mon],
6472311Sjkh		       tm->tm_year,
6482311Sjkh		       tm->tm_hour,
6492311Sjkh		       tm->tm_min,
6502311Sjkh		       tm->tm_sec,
6512311Sjkh		       TZONE(*tm));
6522311Sjkh	return ret;
6532311Sjkh}
6542311Sjkh#endif /*MAIL_DATE*/
6552311Sjkh
6562311Sjkh
6572311Sjkh#ifdef HAVE_SAVED_SUIDS
6582311Sjkhstatic int save_euid;
6592311Sjkhint swap_uids() { save_euid = geteuid(); return seteuid(getuid()); }
6602311Sjkhint swap_uids_back() { return seteuid(save_euid); }
6612311Sjkh#else /*HAVE_SAVED_UIDS*/
6622311Sjkhint swap_uids() { return setreuid(geteuid(), getuid()); }
6632311Sjkhint swap_uids_back() { return swap_uids(); }
6642311Sjkh#endif /*HAVE_SAVED_UIDS*/
665