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