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