111894Speter/* RCS utility functions */ 29Sjkh 311894Speter/* Copyright 1982, 1988, 1989 Walter Tichy 411894Speter Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert 59Sjkh Distributed under license by the Free Software Foundation, Inc. 69Sjkh 79SjkhThis file is part of RCS. 89Sjkh 99SjkhRCS is free software; you can redistribute it and/or modify 109Sjkhit under the terms of the GNU General Public License as published by 119Sjkhthe Free Software Foundation; either version 2, or (at your option) 129Sjkhany later version. 139Sjkh 149SjkhRCS is distributed in the hope that it will be useful, 159Sjkhbut WITHOUT ANY WARRANTY; without even the implied warranty of 169SjkhMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 179SjkhGNU General Public License for more details. 189Sjkh 199SjkhYou should have received a copy of the GNU General Public License 2011894Speteralong with RCS; see the file COPYING. 2111894SpeterIf not, write to the Free Software Foundation, 2211894Speter59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 239Sjkh 249SjkhReport problems and direct all questions to: 259Sjkh 269Sjkh rcs-bugs@cs.purdue.edu 279Sjkh 289Sjkh*/ 299Sjkh 309Sjkh 319Sjkh 329Sjkh 3311894Speter/* 3411894Speter * Revision 5.20 1995/06/16 06:19:24 eggert 3511894Speter * (catchsig): Remove `return'. 3611894Speter * Update FSF address. 378858Srgrimes * 3811894Speter * Revision 5.19 1995/06/02 18:19:00 eggert 3911894Speter * (catchsigaction): New name for `catchsig', for sa_sigaction signature. 4011894Speter * Use nRCS even if !has_psiginfo, to remove unused variable warning. 4111894Speter * (setup_catchsig): Use sa_sigaction only if has_sa_sigaction. 4211894Speter * Use ENOTSUP only if defined. 4311894Speter * 4411894Speter * Revision 5.18 1995/06/01 16:23:43 eggert 4511894Speter * (catchsig, restoreints, setup_catchsig): Use SA_SIGINFO, not has_psiginfo, 4611894Speter * to determine whether to use SA_SIGINFO feature, 4711894Speter * but also check at runtime whether the feature works. 4811894Speter * (catchsig): If an mmap_signal occurs, report the affected file name. 4911894Speter * (unsupported_SA_SIGINFO, accessName): New variables. 5011894Speter * (setup_catchsig): If using SA_SIGINFO, use sa_sigaction, not sa_handler. 5111894Speter * If SA_SIGINFO fails, fall back on sa_handler method. 5211894Speter * 5311894Speter * (readAccessFilenameBuffer, dupSafer, fdSafer, fopenSafer): New functions. 5411894Speter * (concatenate): Remove. 5511894Speter * 5611894Speter * (runv): Work around bad_wait_if_SIGCHLD_ignored bug. 5711894Speter * Remove reference to OPEN_O_WORK. 5811894Speter * 5911894Speter * Revision 5.17 1994/03/20 04:52:58 eggert 6011894Speter * Specify subprocess input via file descriptor, not file name. 6111894Speter * Avoid messing with I/O buffers in the child process. 6211894Speter * Define dup in terms of F_DUPFD if it exists. 6311894Speter * Move setmtime to rcsedit.c. Remove lint. 6411894Speter * 6511894Speter * Revision 5.16 1993/11/09 17:40:15 eggert 6611894Speter * -V now prints version on stdout and exits. 6711894Speter * 6811894Speter * Revision 5.15 1993/11/03 17:42:27 eggert 6911894Speter * Use psiginfo and setreuid if available. Move date2str to maketime.c. 7011894Speter * 7111894Speter * Revision 5.14 1992/07/28 16:12:44 eggert 7211894Speter * Add -V. has_sigaction overrides sig_zaps_handler. Fix -M bug. 7311894Speter * Add mmap_signal, which minimizes signal handling for non-mmap hosts. 7411894Speter * 7511894Speter * Revision 5.13 1992/02/17 23:02:28 eggert 7611894Speter * Work around NFS mmap SIGBUS problem. Add -T support. 7711894Speter * 7811894Speter * Revision 5.12 1992/01/24 18:44:19 eggert 7911894Speter * Work around NFS mmap bug that leads to SIGBUS core dumps. lint -> RCS_lint 8011894Speter * 8111894Speter * Revision 5.11 1992/01/06 02:42:34 eggert 8211894Speter * O_BINARY -> OPEN_O_WORK 8311894Speter * while (E) ; -> while (E) continue; 8411894Speter * 859Sjkh * Revision 5.10 1991/10/07 17:32:46 eggert 869Sjkh * Support piece tables even if !has_mmap. 879Sjkh * 889Sjkh * Revision 5.9 1991/08/19 03:13:55 eggert 899Sjkh * Add spawn() support. Explicate assumptions about getting invoker's name. 909Sjkh * Standardize user-visible dates. Tune. 919Sjkh * 929Sjkh * Revision 5.8 1991/04/21 11:58:30 eggert 939Sjkh * Plug setuid security hole. 949Sjkh * 959Sjkh * Revision 5.6 1991/02/26 17:48:39 eggert 969Sjkh * Fix setuid bug. Use fread, fwrite more portably. 979Sjkh * Support waitpid. Don't assume -1 is acceptable to W* macros. 989Sjkh * strsave -> str_save (DG/UX name clash) 999Sjkh * 1009Sjkh * Revision 5.5 1990/12/04 05:18:49 eggert 1019Sjkh * Don't output a blank line after a signal diagnostic. 1029Sjkh * Use -I for prompts and -q for diagnostics. 1039Sjkh * 1049Sjkh * Revision 5.4 1990/11/01 05:03:53 eggert 1059Sjkh * Remove unneeded setid check. Add awrite(), fremember(). 1069Sjkh * 1079Sjkh * Revision 5.3 1990/10/06 00:16:45 eggert 1089Sjkh * Don't fread F if feof(F). 1099Sjkh * 1109Sjkh * Revision 5.2 1990/09/04 08:02:31 eggert 1119Sjkh * Store fread()'s result in an fread_type object. 1129Sjkh * 1139Sjkh * Revision 5.1 1990/08/29 07:14:07 eggert 1149Sjkh * Declare getpwuid() more carefully. 1159Sjkh * 1169Sjkh * Revision 5.0 1990/08/22 08:13:46 eggert 1179Sjkh * Add setuid support. Permit multiple locks per user. 1189Sjkh * Remove compile-time limits; use malloc instead. 1199Sjkh * Switch to GMT. Permit dates past 1999/12/31. 1209Sjkh * Add -V. Remove snooping. Ansify and Posixate. 1219Sjkh * Tune. Some USG hosts define NSIG but not sys_siglist. 1229Sjkh * Don't run /bin/sh if it's hopeless. 1239Sjkh * Don't leave garbage behind if the output is an empty pipe. 1249Sjkh * Clean up after SIGXCPU or SIGXFSZ. Print name of signal that caused cleanup. 1259Sjkh * 1269Sjkh * Revision 4.6 89/05/01 15:13:40 narten 1279Sjkh * changed copyright header to reflect current distribution rules 1288858Srgrimes * 1299Sjkh * Revision 4.5 88/11/08 16:01:02 narten 1309Sjkh * corrected use of varargs routines 1318858Srgrimes * 1329Sjkh * Revision 4.4 88/08/09 19:13:24 eggert 1339Sjkh * Check for memory exhaustion. 1349Sjkh * Permit signal handlers to yield either 'void' or 'int'; fix oldSIGINT botch. 1359Sjkh * Use execv(), not system(); yield exit status like diff(1)'s. 1368858Srgrimes * 1379Sjkh * Revision 4.3 87/10/18 10:40:22 narten 1389Sjkh * Updating version numbers. Changes relative to 1.1 actually 1399Sjkh * relative to 4.1 1408858Srgrimes * 1419Sjkh * Revision 1.3 87/09/24 14:01:01 narten 1428858Srgrimes * Sources now pass through lint (if you ignore printf/sprintf/fprintf 1439Sjkh * warnings) 1448858Srgrimes * 1459Sjkh * Revision 1.2 87/03/27 14:22:43 jenkins 1469Sjkh * Port to suns 1478858Srgrimes * 1489Sjkh * Revision 4.1 83/05/10 15:53:13 wft 1499Sjkh * Added getcaller() and findlock(). 1509Sjkh * Changed catchints() to check SIGINT for SIG_IGN before setting up the signal 1519Sjkh * (needed for background jobs in older shells). Added restoreints(). 1529Sjkh * Removed printing of full RCS path from logcommand(). 1538858Srgrimes * 1549Sjkh * Revision 3.8 83/02/15 15:41:49 wft 1559Sjkh * Added routine fastcopy() to copy remainder of a file in blocks. 1569Sjkh * 1579Sjkh * Revision 3.7 82/12/24 15:25:19 wft 1589Sjkh * added catchints(), ignoreints() for catching and ingnoring interrupts; 1599Sjkh * fixed catchsig(). 1609Sjkh * 1619Sjkh * Revision 3.6 82/12/08 21:52:05 wft 1629Sjkh * Using DATEFORM to format dates. 1639Sjkh * 1649Sjkh * Revision 3.5 82/12/04 18:20:49 wft 1659Sjkh * Replaced SNOOPDIR with SNOOPFILE; changed addlock() to update 1669Sjkh * lockedby-field. 1679Sjkh * 1689Sjkh * Revision 3.4 82/12/03 17:17:43 wft 1699Sjkh * Added check to addlock() ensuring only one lock per person. 1709Sjkh * Addlock also returns a pointer to the lock created. Deleted fancydate(). 1719Sjkh * 1729Sjkh * Revision 3.3 82/11/27 12:24:37 wft 1739Sjkh * moved rmsema(), trysema(), trydiraccess(), getfullRCSname() to rcsfnms.c. 1749Sjkh * Introduced macro SNOOP so that snoop can be placed in directory other than 1759Sjkh * TARGETDIR. Changed %02d to %.2d for compatibility reasons. 1769Sjkh * 1779Sjkh * Revision 3.2 82/10/18 21:15:11 wft 1789Sjkh * added function getfullRCSname(). 1799Sjkh * 1809Sjkh * Revision 3.1 82/10/13 16:17:37 wft 1819Sjkh * Cleanup message is now suppressed in quiet mode. 1829Sjkh */ 1839Sjkh 1849Sjkh 1859Sjkh 1869Sjkh 1879Sjkh#include "rcsbase.h" 1889Sjkh 18950472SpeterlibId(utilId, "$FreeBSD: releng/10.3/gnu/usr.bin/rcs/lib/rcsutil.c 50472 1999-08-27 23:37:10Z peter $") 1909Sjkh 1919Sjkh#if !has_memcmp 1929Sjkh int 1939Sjkhmemcmp(s1, s2, n) 1949Sjkh void const *s1, *s2; 1959Sjkh size_t n; 1969Sjkh{ 1979Sjkh register unsigned char const 1989Sjkh *p1 = (unsigned char const*)s1, 1999Sjkh *p2 = (unsigned char const*)s2; 2009Sjkh register size_t i = n; 2019Sjkh register int r = 0; 2029Sjkh while (i-- && !(r = (*p1++ - *p2++))) 2039Sjkh ; 2049Sjkh return r; 2059Sjkh} 2069Sjkh#endif 2079Sjkh 2089Sjkh#if !has_memcpy 2099Sjkh void * 2109Sjkhmemcpy(s1, s2, n) 2119Sjkh void *s1; 2129Sjkh void const *s2; 2139Sjkh size_t n; 2149Sjkh{ 2159Sjkh register char *p1 = (char*)s1; 2169Sjkh register char const *p2 = (char const*)s2; 2179Sjkh while (n--) 2189Sjkh *p1++ = *p2++; 2199Sjkh return s1; 2209Sjkh} 2219Sjkh#endif 2229Sjkh 22311894Speter#if RCS_lint 2249Sjkh malloc_type lintalloc; 2259Sjkh#endif 2269Sjkh 2279Sjkh/* 2289Sjkh * list of blocks allocated with ftestalloc() 2299Sjkh * These blocks can be freed by ffree when we're done with the current file. 2309Sjkh * We could put the free block inside struct alloclist, rather than a pointer 2319Sjkh * to the free block, but that would be less portable. 2329Sjkh */ 2339Sjkhstruct alloclist { 2349Sjkh malloc_type alloc; 2359Sjkh struct alloclist *nextalloc; 2369Sjkh}; 2379Sjkhstatic struct alloclist *alloced; 2389Sjkh 2399Sjkh 24011894Speter static malloc_type okalloc P((malloc_type)); 2419Sjkh static malloc_type 2429Sjkhokalloc(p) 2439Sjkh malloc_type p; 2449Sjkh{ 2459Sjkh if (!p) 2469Sjkh faterror("out of memory"); 2479Sjkh return p; 2489Sjkh} 2499Sjkh 2509Sjkh malloc_type 2519Sjkhtestalloc(size) 2529Sjkh size_t size; 2539Sjkh/* Allocate a block, testing that the allocation succeeded. */ 2549Sjkh{ 2559Sjkh return okalloc(malloc(size)); 2569Sjkh} 2579Sjkh 2589Sjkh malloc_type 2599Sjkhtestrealloc(ptr, size) 2609Sjkh malloc_type ptr; 2619Sjkh size_t size; 2629Sjkh/* Reallocate a block, testing that the allocation succeeded. */ 2639Sjkh{ 2649Sjkh return okalloc(realloc(ptr, size)); 2659Sjkh} 2669Sjkh 2679Sjkh malloc_type 2689Sjkhfremember(ptr) 2699Sjkh malloc_type ptr; 2709Sjkh/* Remember PTR in 'alloced' so that it can be freed later. Yield PTR. */ 2719Sjkh{ 2729Sjkh register struct alloclist *q = talloc(struct alloclist); 2739Sjkh q->nextalloc = alloced; 2749Sjkh alloced = q; 2759Sjkh return q->alloc = ptr; 2769Sjkh} 2779Sjkh 2789Sjkh malloc_type 2799Sjkhftestalloc(size) 2809Sjkh size_t size; 2819Sjkh/* Allocate a block, putting it in 'alloced' so it can be freed later. */ 2829Sjkh{ 2839Sjkh return fremember(testalloc(size)); 2849Sjkh} 2859Sjkh 2869Sjkh void 2879Sjkhffree() 2889Sjkh/* Free all blocks allocated with ftestalloc(). */ 2899Sjkh{ 2909Sjkh register struct alloclist *p, *q; 2919Sjkh for (p = alloced; p; p = q) { 2929Sjkh q = p->nextalloc; 2939Sjkh tfree(p->alloc); 2949Sjkh tfree(p); 2959Sjkh } 29611894Speter alloced = 0; 2979Sjkh} 2989Sjkh 2999Sjkh void 3009Sjkhffree1(f) 3019Sjkh register char const *f; 3029Sjkh/* Free the block f, which was allocated by ftestalloc. */ 3039Sjkh{ 3049Sjkh register struct alloclist *p, **a = &alloced; 3059Sjkh 3069Sjkh while ((p = *a)->alloc != f) 3079Sjkh a = &p->nextalloc; 3089Sjkh *a = p->nextalloc; 3099Sjkh tfree(p->alloc); 3109Sjkh tfree(p); 3119Sjkh} 3129Sjkh 3139Sjkh char * 3149Sjkhstr_save(s) 3159Sjkh char const *s; 3169Sjkh/* Save s in permanently allocated storage. */ 3179Sjkh{ 3189Sjkh return strcpy(tnalloc(char, strlen(s)+1), s); 3199Sjkh} 3209Sjkh 3219Sjkh char * 3229Sjkhfstr_save(s) 3239Sjkh char const *s; 3249Sjkh/* Save s in storage that will be deallocated when we're done with this file. */ 3259Sjkh{ 3269Sjkh return strcpy(ftnalloc(char, strlen(s)+1), s); 3279Sjkh} 3289Sjkh 3299Sjkh char * 3309Sjkhcgetenv(name) 3319Sjkh char const *name; 3329Sjkh/* Like getenv(), but yield a copy; getenv() can overwrite old results. */ 3339Sjkh{ 3349Sjkh register char *p; 3359Sjkh 3369Sjkh return (p=getenv(name)) ? str_save(p) : p; 3379Sjkh} 3389Sjkh 3399Sjkh char const * 3409Sjkhgetusername(suspicious) 3419Sjkh int suspicious; 3429Sjkh/* Get the caller's login name. Trust only getwpuid if SUSPICIOUS. */ 3439Sjkh{ 3449Sjkh static char *name; 3459Sjkh 3469Sjkh if (!name) { 3479Sjkh if ( 3489Sjkh /* Prefer getenv() unless suspicious; it's much faster. */ 3499Sjkh# if getlogin_is_secure 3509Sjkh (suspicious 35111894Speter || ( 35211894Speter !(name = cgetenv("LOGNAME")) 35311894Speter && !(name = cgetenv("USER")) 35411894Speter )) 3559Sjkh && !(name = getlogin()) 3569Sjkh# else 3579Sjkh suspicious 35811894Speter || ( 3599Sjkh !(name = cgetenv("LOGNAME")) 3609Sjkh && !(name = cgetenv("USER")) 3619Sjkh && !(name = getlogin()) 36211894Speter ) 3639Sjkh# endif 3649Sjkh ) { 3659Sjkh#if has_getuid && has_getpwuid 3669Sjkh struct passwd const *pw = getpwuid(ruid()); 3679Sjkh if (!pw) 3689Sjkh faterror("no password entry for userid %lu", 3699Sjkh (unsigned long)ruid() 3709Sjkh ); 3719Sjkh name = pw->pw_name; 3729Sjkh#else 3739Sjkh#if has_setuid 3749Sjkh faterror("setuid not supported"); 3759Sjkh#else 37611894Speter faterror("Who are you? Please setenv LOGNAME."); 3779Sjkh#endif 3789Sjkh#endif 3799Sjkh } 3809Sjkh checksid(name); 3819Sjkh } 3829Sjkh return name; 3839Sjkh} 3849Sjkh 3859Sjkh 3869Sjkh 3879Sjkh 3889Sjkh#if has_signal 3899Sjkh 3909Sjkh/* 3919Sjkh * Signal handling 3929Sjkh * 3939Sjkh * Standard C places too many restrictions on signal handlers. 3949Sjkh * We obey as many of them as we can. 3959Sjkh * Posix places fewer restrictions, and we are Posix-compatible here. 3969Sjkh */ 3979Sjkh 3989Sjkhstatic sig_atomic_t volatile heldsignal, holdlevel; 39911894Speter#ifdef SA_SIGINFO 40011894Speter static int unsupported_SA_SIGINFO; 40111894Speter static siginfo_t bufsiginfo; 40211894Speter static siginfo_t *volatile heldsiginfo; 40311894Speter#endif 4049Sjkh 4059Sjkh 40611894Speter#if has_NFS && has_mmap && large_memory && mmap_signal 40711894Speter static char const *accessName; 40811894Speter 40911894Speter void 41011894Speter readAccessFilenameBuffer(filename, p) 41111894Speter char const *filename; 41211894Speter unsigned char const *p; 41311894Speter { 41411894Speter unsigned char volatile t; 41511894Speter accessName = filename; 41611894Speter t = *p; 41711894Speter accessName = 0; 41811894Speter } 41911894Speter#else 42011894Speter# define accessName ((char const *) 0) 4219Sjkh#endif 42211894Speter 42311894Speter 42411894Speter#if !has_psignal 42511894Speter 42611894Speter# define psignal my_psignal 42711894Speter static void my_psignal P((int,char const*)); 42811894Speter static void 42911894Spetermy_psignal(sig, s) 43011894Speter int sig; 43111894Speter char const *s; 43211894Speter{ 43311894Speter char const *sname = "Unknown signal"; 43411894Speter# if has_sys_siglist && defined(NSIG) 43511894Speter if ((unsigned)sig < NSIG) 43611894Speter sname = sys_siglist[sig]; 43711894Speter# else 43811894Speter switch (sig) { 43911894Speter# ifdef SIGHUP 4409Sjkh case SIGHUP: sname = "Hangup"; break; 44111894Speter# endif 44211894Speter# ifdef SIGINT 4439Sjkh case SIGINT: sname = "Interrupt"; break; 44411894Speter# endif 44511894Speter# ifdef SIGPIPE 4469Sjkh case SIGPIPE: sname = "Broken pipe"; break; 44711894Speter# endif 44811894Speter# ifdef SIGQUIT 4499Sjkh case SIGQUIT: sname = "Quit"; break; 45011894Speter# endif 45111894Speter# ifdef SIGTERM 4529Sjkh case SIGTERM: sname = "Terminated"; break; 45311894Speter# endif 45411894Speter# ifdef SIGXCPU 4559Sjkh case SIGXCPU: sname = "Cputime limit exceeded"; break; 45611894Speter# endif 45711894Speter# ifdef SIGXFSZ 4589Sjkh case SIGXFSZ: sname = "Filesize limit exceeded"; break; 45911894Speter# endif 46011894Speter# if has_mmap && large_memory 46111894Speter# if defined(SIGBUS) && mmap_signal==SIGBUS 46211894Speter case SIGBUS: sname = "Bus error"; break; 46311894Speter# endif 46411894Speter# if defined(SIGSEGV) && mmap_signal==SIGSEGV 46511894Speter case SIGSEGV: sname = "Segmentation fault"; break; 46611894Speter# endif 46711894Speter# endif 46811894Speter } 46911894Speter# endif 47011894Speter 47111894Speter /* Avoid calling sprintf etc., in case they're not reentrant. */ 47211894Speter { 47311894Speter char const *p; 47411894Speter char buf[BUFSIZ], *b = buf; 47511894Speter for (p = s; *p; *b++ = *p++) 47611894Speter continue; 47711894Speter *b++ = ':'; 47811894Speter *b++ = ' '; 47911894Speter for (p = sname; *p; *b++ = *p++) 48011894Speter continue; 48111894Speter *b++ = '\n'; 48211894Speter VOID write(STDERR_FILENO, buf, b - buf); 48311894Speter } 48411894Speter} 4859Sjkh#endif 48611894Speter 48711894Speterstatic signal_type catchsig P((int)); 48811894Speter#ifdef SA_SIGINFO 48911894Speter static signal_type catchsigaction P((int,siginfo_t*,void*)); 49011894Speter#endif 49111894Speter 49211894Speter static signal_type 49311894Spetercatchsig(s) 49411894Speter int s; 49511894Speter#ifdef SA_SIGINFO 49611894Speter{ 49711894Speter catchsigaction(s, (siginfo_t *)0, (void *)0); 49811894Speter} 49911894Speter static signal_type 50011894Spetercatchsigaction(s, i, c) 50111894Speter int s; 50211894Speter siginfo_t *i; 50311894Speter void *c; 50411894Speter#endif 50511894Speter{ 50611894Speter# if sig_zaps_handler 50711894Speter /* If a signal arrives before we reset the handler, we lose. */ 50811894Speter VOID signal(s, SIG_IGN); 50911894Speter# endif 51011894Speter 51111894Speter# ifdef SA_SIGINFO 51211894Speter if (!unsupported_SA_SIGINFO) 51311894Speter i = 0; 51411894Speter# endif 51511894Speter 51611894Speter if (holdlevel) { 51711894Speter heldsignal = s; 51811894Speter# ifdef SA_SIGINFO 51911894Speter if (i) { 52011894Speter bufsiginfo = *i; 52111894Speter heldsiginfo = &bufsiginfo; 5229Sjkh } 52311894Speter# endif 52411894Speter return; 52511894Speter } 52611894Speter 52711894Speter ignoreints(); 52811894Speter setrid(); 52911894Speter if (!quietflag) { 53011894Speter /* Avoid calling sprintf etc., in case they're not reentrant. */ 53111894Speter char const *p; 53211894Speter char buf[BUFSIZ], *b = buf; 53311894Speter 53411894Speter if ( ! ( 53511894Speter# if has_mmap && large_memory && mmap_signal 53611894Speter /* Check whether this signal was planned. */ 53711894Speter s == mmap_signal && accessName 53811894Speter# else 53911894Speter 0 54011894Speter# endif 54111894Speter )) { 54211894Speter char const *nRCS = "\nRCS"; 54311894Speter# if defined(SA_SIGINFO) && has_si_errno && has_mmap && large_memory && mmap_signal 54411894Speter if (s == mmap_signal && i && i->si_errno) { 54511894Speter errno = i->si_errno; 54611894Speter perror(nRCS++); 54711894Speter } 54811894Speter# endif 54911894Speter# if defined(SA_SIGINFO) && has_psiginfo 55011894Speter if (i) 55111894Speter psiginfo(i, nRCS); 55211894Speter else 55311894Speter psignal(s, nRCS); 55411894Speter# else 55511894Speter psignal(s, nRCS); 55611894Speter# endif 5579Sjkh } 55811894Speter 55911894Speter for (p = "RCS: "; *p; *b++ = *p++) 56011894Speter continue; 56111894Speter# if has_mmap && large_memory && mmap_signal 56211894Speter if (s == mmap_signal) { 56311894Speter p = accessName; 56411894Speter if (!p) 56511894Speter p = "Was a file changed by some other process? "; 56611894Speter else { 56711894Speter char const *p1; 56811894Speter for (p1 = p; *p1; p1++) 56911894Speter continue; 57011894Speter VOID write(STDERR_FILENO, buf, b - buf); 57111894Speter VOID write(STDERR_FILENO, p, p1 - p); 57211894Speter b = buf; 57311894Speter p = ": Permission denied. "; 57411894Speter } 57511894Speter while (*p) 57611894Speter *b++ = *p++; 57711894Speter } 57811894Speter# endif 57911894Speter for (p = "Cleaning up.\n"; *p; *b++ = *p++) 58011894Speter continue; 58111894Speter VOID write(STDERR_FILENO, buf, b - buf); 58211894Speter } 58311894Speter exiterr(); 5849Sjkh} 5859Sjkh 5869Sjkh void 5879Sjkhignoreints() 5889Sjkh{ 5899Sjkh ++holdlevel; 5909Sjkh} 5919Sjkh 5929Sjkh void 5939Sjkhrestoreints() 5949Sjkh{ 5959Sjkh if (!--holdlevel && heldsignal) 59611894Speter# ifdef SA_SIGINFO 59711894Speter VOID catchsigaction(heldsignal, heldsiginfo, (void *)0); 59811894Speter# else 5999Sjkh VOID catchsig(heldsignal); 60011894Speter# endif 6019Sjkh} 6029Sjkh 6039Sjkh 60411894Speterstatic void setup_catchsig P((int const*,int)); 6059Sjkh 6069Sjkh#if has_sigaction 6079Sjkh 60811894Speter static void check_sig P((int)); 6099Sjkh static void 6109Sjkh check_sig(r) 6119Sjkh int r; 6129Sjkh { 6139Sjkh if (r != 0) 61411894Speter efaterror("signal handling"); 6159Sjkh } 6169Sjkh 6179Sjkh static void 61811894Speter setup_catchsig(sig, sigs) 61911894Speter int const *sig; 62011894Speter int sigs; 6219Sjkh { 62211894Speter register int i, j; 6239Sjkh struct sigaction act; 6249Sjkh 62511894Speter for (i=sigs; 0<=--i; ) { 62611894Speter check_sig(sigaction(sig[i], (struct sigaction*)0, &act)); 6279Sjkh if (act.sa_handler != SIG_IGN) { 62811894Speter act.sa_handler = catchsig; 62911894Speter# ifdef SA_SIGINFO 63011894Speter if (!unsupported_SA_SIGINFO) { 63111894Speter# if has_sa_sigaction 63211894Speter act.sa_sigaction = catchsigaction; 63311894Speter# else 63411894Speter act.sa_handler = catchsigaction; 63511894Speter# endif 63611894Speter act.sa_flags |= SA_SIGINFO; 63711894Speter } 63811894Speter# endif 63911894Speter for (j=sigs; 0<=--j; ) 64011894Speter check_sig(sigaddset(&act.sa_mask, sig[j])); 64111894Speter if (sigaction(sig[i], &act, (struct sigaction*)0) != 0) { 64211894Speter# if defined(SA_SIGINFO) && defined(ENOTSUP) 64311894Speter if (errno == ENOTSUP && !unsupported_SA_SIGINFO) { 64411894Speter /* Turn off use of SA_SIGINFO and try again. */ 64511894Speter unsupported_SA_SIGINFO = 1; 64611894Speter i++; 64711894Speter continue; 64811894Speter } 64911894Speter# endif 65011894Speter check_sig(-1); 65111894Speter } 6529Sjkh } 6539Sjkh } 6549Sjkh } 6559Sjkh 6569Sjkh#else 6579Sjkh#if has_sigblock 6589Sjkh 6599Sjkh static void 66011894Speter setup_catchsig(sig, sigs) 66111894Speter int const *sig; 66211894Speter int sigs; 6639Sjkh { 6649Sjkh register int i; 6659Sjkh int mask; 6669Sjkh 6679Sjkh mask = 0; 66811894Speter for (i=sigs; 0<=--i; ) 6699Sjkh mask |= sigmask(sig[i]); 6709Sjkh mask = sigblock(mask); 67111894Speter for (i=sigs; 0<=--i; ) 6729Sjkh if ( 6739Sjkh signal(sig[i], catchsig) == SIG_IGN && 6749Sjkh signal(sig[i], SIG_IGN) != catchsig 6759Sjkh ) 6769Sjkh faterror("signal catcher failure"); 6779Sjkh VOID sigsetmask(mask); 6789Sjkh } 6799Sjkh 6809Sjkh#else 6819Sjkh 6829Sjkh static void 68311894Speter setup_catchsig(sig, sigs) 68411894Speter int const *sig; 68511894Speter int sigs; 6869Sjkh { 6879Sjkh register i; 6889Sjkh 68911894Speter for (i=sigs; 0<=--i; ) 6909Sjkh if ( 6919Sjkh signal(sig[i], SIG_IGN) != SIG_IGN && 6929Sjkh signal(sig[i], catchsig) != SIG_IGN 6939Sjkh ) 6949Sjkh faterror("signal catcher failure"); 6959Sjkh } 6969Sjkh 6979Sjkh#endif 6989Sjkh#endif 6999Sjkh 70011894Speter 70111894Speterstatic int const regsigs[] = { 70211894Speter# ifdef SIGHUP 70311894Speter SIGHUP, 70411894Speter# endif 70511894Speter# ifdef SIGINT 70611894Speter SIGINT, 70711894Speter# endif 70811894Speter# ifdef SIGPIPE 70911894Speter SIGPIPE, 71011894Speter# endif 71111894Speter# ifdef SIGQUIT 71211894Speter SIGQUIT, 71311894Speter# endif 71411894Speter# ifdef SIGTERM 71511894Speter SIGTERM, 71611894Speter# endif 71711894Speter# ifdef SIGXCPU 71811894Speter SIGXCPU, 71911894Speter# endif 72011894Speter# ifdef SIGXFSZ 72111894Speter SIGXFSZ, 72211894Speter# endif 72311894Speter}; 72411894Speter 7259Sjkh void 7269Sjkhcatchints() 7279Sjkh{ 7289Sjkh static int catching_ints; 7299Sjkh if (!catching_ints) { 73011894Speter catching_ints = true; 73111894Speter setup_catchsig(regsigs, (int) (sizeof(regsigs)/sizeof(*regsigs))); 7329Sjkh } 7339Sjkh} 7349Sjkh 73511894Speter#if has_mmap && large_memory && mmap_signal 73611894Speter 73711894Speter /* 73811894Speter * If you mmap an NFS file, and someone on another client removes the last 73911894Speter * link to that file, and you later reference an uncached part of that file, 74011894Speter * you'll get a SIGBUS or SIGSEGV (depending on the operating system). 74111894Speter * Catch the signal and report the problem to the user. 74211894Speter * Unfortunately, there's no portable way to differentiate between this 74311894Speter * problem and actual bugs in the program. 74411894Speter * This NFS problem is rare, thank goodness. 74511894Speter * 74611894Speter * This can also occur if someone truncates the file, even without NFS. 74711894Speter */ 74811894Speter 74911894Speter static int const mmapsigs[] = { mmap_signal }; 75011894Speter 75111894Speter void 75211894Speter catchmmapints() 75311894Speter { 75411894Speter static int catching_mmap_ints; 75511894Speter if (!catching_mmap_ints) { 75611894Speter catching_mmap_ints = true; 75711894Speter setup_catchsig(mmapsigs, (int)(sizeof(mmapsigs)/sizeof(*mmapsigs))); 75811894Speter } 75911894Speter } 76011894Speter#endif 76111894Speter 7629Sjkh#endif /* has_signal */ 7639Sjkh 7649Sjkh 7659Sjkh void 7669Sjkhfastcopy(inf,outf) 7679Sjkh register RILE *inf; 7689Sjkh FILE *outf; 7699Sjkh/* Function: copies the remainder of file inf to outf. 7709Sjkh */ 7719Sjkh{ 7729Sjkh#if large_memory 77311894Speter# if maps_memory 7749Sjkh awrite((char const*)inf->ptr, (size_t)(inf->lim - inf->ptr), outf); 7759Sjkh inf->ptr = inf->lim; 7769Sjkh# else 7779Sjkh for (;;) { 7789Sjkh awrite((char const*)inf->ptr, (size_t)(inf->readlim - inf->ptr), outf); 7799Sjkh inf->ptr = inf->readlim; 7809Sjkh if (inf->ptr == inf->lim) 7819Sjkh break; 7829Sjkh VOID Igetmore(inf); 7839Sjkh } 7849Sjkh# endif 7859Sjkh#else 7869Sjkh char buf[BUFSIZ*8]; 7879Sjkh register fread_type rcount; 7889Sjkh 7899Sjkh /*now read the rest of the file in blocks*/ 7909Sjkh while (!feof(inf)) { 7919Sjkh if (!(rcount = Fread(buf,sizeof(*buf),sizeof(buf),inf))) { 7929Sjkh testIerror(inf); 7939Sjkh return; 7949Sjkh } 7959Sjkh awrite(buf, (size_t)rcount, outf); 7969Sjkh } 7979Sjkh#endif 7989Sjkh} 7999Sjkh 8009Sjkh#ifndef SSIZE_MAX 8019Sjkh /* This does not work in #ifs, but it's good enough for us. */ 8029Sjkh /* Underestimating SSIZE_MAX may slow us down, but it won't break us. */ 8039Sjkh# define SSIZE_MAX ((unsigned)-1 >> 1) 8049Sjkh#endif 8059Sjkh 8069Sjkh void 8079Sjkhawrite(buf, chars, f) 8089Sjkh char const *buf; 8099Sjkh size_t chars; 8109Sjkh FILE *f; 8119Sjkh{ 8129Sjkh /* Posix 1003.1-1990 ssize_t hack */ 8139Sjkh while (SSIZE_MAX < chars) { 8149Sjkh if (Fwrite(buf, sizeof(*buf), SSIZE_MAX, f) != SSIZE_MAX) 8159Sjkh Oerror(); 8169Sjkh buf += SSIZE_MAX; 8179Sjkh chars -= SSIZE_MAX; 8189Sjkh } 8199Sjkh 8209Sjkh if (Fwrite(buf, sizeof(*buf), chars, f) != chars) 8219Sjkh Oerror(); 8229Sjkh} 8239Sjkh 82411894Speter/* dup a file descriptor; the result must not be stdin, stdout, or stderr. */ 82511894Speter static int dupSafer P((int)); 82611894Speter static int 82711894SpeterdupSafer(fd) 82811894Speter int fd; 82911894Speter{ 83011894Speter# ifdef F_DUPFD 83111894Speter return fcntl(fd, F_DUPFD, STDERR_FILENO + 1); 83211894Speter# else 83311894Speter int e, f, i, used = 0; 83411894Speter while (STDIN_FILENO <= (f = dup(fd)) && f <= STDERR_FILENO) 83511894Speter used |= 1<<f; 83611894Speter e = errno; 83711894Speter for (i = STDIN_FILENO; i <= STDERR_FILENO; i++) 83811894Speter if (used & (1<<i)) 83911894Speter VOID close(i); 84011894Speter errno = e; 84111894Speter return f; 84211894Speter# endif 84311894Speter} 8449Sjkh 84511894Speter/* Renumber a file descriptor so that it's not stdin, stdout, or stderr. */ 84611894Speter int 84711894SpeterfdSafer(fd) 84811894Speter int fd; 84911894Speter{ 85011894Speter if (STDIN_FILENO <= fd && fd <= STDERR_FILENO) { 85111894Speter int f = dupSafer(fd); 85211894Speter int e = errno; 85311894Speter VOID close(fd); 85411894Speter errno = e; 85511894Speter fd = f; 85611894Speter } 85711894Speter return fd; 85811894Speter} 8599Sjkh 86011894Speter/* Like fopen, except the result is never stdin, stdout, or stderr. */ 86111894Speter FILE * 86211894SpeterfopenSafer(filename, type) 86311894Speter char const *filename; 86411894Speter char const *type; 86511894Speter{ 86611894Speter FILE *stream = fopen(filename, type); 86711894Speter if (stream) { 86811894Speter int fd = fileno(stream); 86911894Speter if (STDIN_FILENO <= fd && fd <= STDERR_FILENO) { 87011894Speter int f = dupSafer(fd); 87111894Speter if (f < 0) { 87211894Speter int e = errno; 87311894Speter VOID fclose(stream); 87411894Speter errno = e; 87511894Speter return 0; 87611894Speter } 87711894Speter if (fclose(stream) != 0) { 87811894Speter int e = errno; 87911894Speter VOID close(f); 88011894Speter errno = e; 88111894Speter return 0; 88211894Speter } 88311894Speter stream = fdopen(f, type); 88411894Speter } 88511894Speter } 88611894Speter return stream; 88711894Speter} 8889Sjkh 8899Sjkh 89011894Speter#ifdef F_DUPFD 89111894Speter# undef dup 89211894Speter# define dup(fd) fcntl(fd, F_DUPFD, 0) 89311894Speter#endif 89411894Speter 89511894Speter 89611894Speter#if has_fork || has_spawn 89711894Speter 89811894Speter static int movefd P((int,int)); 8999Sjkh static int 9009Sjkhmovefd(old, new) 9019Sjkh int old, new; 9029Sjkh{ 9039Sjkh if (old < 0 || old == new) 9049Sjkh return old; 9059Sjkh# ifdef F_DUPFD 9069Sjkh new = fcntl(old, F_DUPFD, new); 9079Sjkh# else 9089Sjkh new = dup2(old, new); 9099Sjkh# endif 9109Sjkh return close(old)==0 ? new : -1; 9119Sjkh} 9129Sjkh 91311894Speter static int fdreopen P((int,char const*,int)); 9149Sjkh static int 9159Sjkhfdreopen(fd, file, flags) 9169Sjkh int fd; 9179Sjkh char const *file; 9189Sjkh int flags; 9199Sjkh{ 9209Sjkh int newfd; 9219Sjkh VOID close(fd); 9229Sjkh newfd = 9239Sjkh#if !open_can_creat 9249Sjkh flags&O_CREAT ? creat(file, S_IRUSR|S_IWUSR) : 9259Sjkh#endif 9269Sjkh open(file, flags, S_IRUSR|S_IWUSR); 9279Sjkh return movefd(newfd, fd); 9289Sjkh} 9299Sjkh 93011894Speter#if has_spawn 93111894Speter static void redirect P((int,int)); 9329Sjkh static void 9339Sjkhredirect(old, new) 9349Sjkh int old, new; 93511894Speter/* 93611894Speter* Move file descriptor OLD to NEW. 93711894Speter* If OLD is -1, do nothing. 93811894Speter* If OLD is -2, just close NEW. 93911894Speter*/ 9409Sjkh{ 94111894Speter if ((old != -1 && close(new) != 0) || (0 <= old && movefd(old,new) < 0)) 9429Sjkh efaterror("spawn I/O redirection"); 9439Sjkh} 9449Sjkh#endif 9459Sjkh 9469Sjkh 94711894Speter#else /* !has_fork && !has_spawn */ 9489Sjkh 94911894Speter static void bufargcat P((struct buf*,int,char const*)); 9509Sjkh static void 9519Sjkhbufargcat(b, c, s) 9529Sjkh register struct buf *b; 9539Sjkh int c; 9549Sjkh register char const *s; 9559Sjkh/* Append to B a copy of C, plus a quoted copy of S. */ 9569Sjkh{ 9579Sjkh register char *p; 9589Sjkh register char const *t; 9599Sjkh size_t bl, sl; 9609Sjkh 9619Sjkh for (t=s, sl=0; *t; ) 9629Sjkh sl += 3*(*t++=='\'') + 1; 9639Sjkh bl = strlen(b->string); 9649Sjkh bufrealloc(b, bl + sl + 4); 9659Sjkh p = b->string + bl; 9669Sjkh *p++ = c; 9679Sjkh *p++ = '\''; 9689Sjkh while (*s) { 9699Sjkh if (*s == '\'') { 9709Sjkh *p++ = '\''; 9719Sjkh *p++ = '\\'; 9729Sjkh *p++ = '\''; 9739Sjkh } 9749Sjkh *p++ = *s++; 9759Sjkh } 9769Sjkh *p++ = '\''; 9779Sjkh *p = 0; 9789Sjkh} 97911894Speter 9809Sjkh#endif 9819Sjkh 98211894Speter#if !has_spawn && has_fork 9839Sjkh/* 98411894Speter* Output the string S to stderr, without touching any I/O buffers. 98511894Speter* This is useful if you are a child process, whose buffers are usually wrong. 98611894Speter* Exit immediately if the write does not completely succeed. 9879Sjkh*/ 98811894Speterstatic void write_stderr P((char const *)); 98911894Speter static void 99011894Speterwrite_stderr(s) 99111894Speter char const *s; 99211894Speter{ 99311894Speter size_t slen = strlen(s); 99411894Speter if (write(STDERR_FILENO, s, slen) != slen) 99511894Speter _exit(EXIT_TROUBLE); 99611894Speter} 99711894Speter#endif 99811894Speter 99911894Speter/* 100011894Speter* Run a command. 100111894Speter* infd, if not -1, is the input file descriptor. 100211894Speter* outname, if nonzero, is the name of the output file. 100311894Speter* args[1..] form the command to be run; args[0] might be modified. 100411894Speter*/ 10059Sjkh int 100611894Speterrunv(infd, outname, args) 100711894Speter int infd; 100811894Speter char const *outname, **args; 10099Sjkh{ 10109Sjkh int wstatus; 10119Sjkh 101211894Speter#if bad_wait_if_SIGCHLD_ignored 101311894Speter static int fixed_SIGCHLD; 101411894Speter if (!fixed_SIGCHLD) { 101511894Speter fixed_SIGCHLD = true; 101611894Speter# ifndef SIGCHLD 101711894Speter# define SIGCHLD SIGCLD 101811894Speter# endif 101911894Speter VOID signal(SIGCHLD, SIG_DFL); 102011894Speter } 102111894Speter#endif 102211894Speter 10239Sjkh oflush(); 10249Sjkh eflush(); 10259Sjkh { 10269Sjkh#if has_spawn 10279Sjkh int in, out; 102811894Speter char const *file; 102911894Speter 103011894Speter in = -1; 103111894Speter if (infd != -1 && infd != STDIN_FILENO) { 103211894Speter if ((in = dup(STDIN_FILENO)) < 0) { 103311894Speter if (errno != EBADF) 103411894Speter efaterror("spawn input setup"); 103511894Speter in = -2; 103611894Speter } else { 103711894Speter# ifdef F_DUPFD 103811894Speter if (close(STDIN_FILENO) != 0) 103911894Speter efaterror("spawn input close"); 104011894Speter# endif 104111894Speter } 104211894Speter if ( 104311894Speter# ifdef F_DUPFD 104411894Speter fcntl(infd, F_DUPFD, STDIN_FILENO) != STDIN_FILENO 104511894Speter# else 104611894Speter dup2(infd, STDIN_FILENO) != STDIN_FILENO 104711894Speter# endif 104811894Speter ) 104911894Speter efaterror("spawn input redirection"); 10509Sjkh } 105111894Speter 105211894Speter out = -1; 105311894Speter if (outname) { 105411894Speter if ((out = dup(STDOUT_FILENO)) < 0) { 105511894Speter if (errno != EBADF) 105611894Speter efaterror("spawn output setup"); 105711894Speter out = -2; 105811894Speter } 105911894Speter if (fdreopen( 106011894Speter STDOUT_FILENO, outname, 106111894Speter O_CREAT | O_TRUNC | O_WRONLY 106211894Speter ) < 0) 106311894Speter efaterror(outname); 106411894Speter } 106511894Speter 106611894Speter wstatus = spawn_RCS(0, args[1], (char**)(args + 1)); 106711894Speter# ifdef RCS_SHELL 106811894Speter if (wstatus == -1 && errno == ENOEXEC) { 106911894Speter args[0] = RCS_SHELL; 107011894Speter wstatus = spawnv(0, args[0], (char**)args); 107111894Speter } 107211894Speter# endif 10739Sjkh redirect(in, STDIN_FILENO); 10749Sjkh redirect(out, STDOUT_FILENO); 10759Sjkh#else 10769Sjkh#if has_fork 10779Sjkh pid_t pid; 10789Sjkh if (!(pid = vfork())) { 107911894Speter char const *notfound; 108011894Speter if (infd != -1 && infd != STDIN_FILENO && ( 108111894Speter# ifdef F_DUPFD 108211894Speter (VOID close(STDIN_FILENO), 108311894Speter fcntl(infd, F_DUPFD, STDIN_FILENO) != STDIN_FILENO) 108411894Speter# else 108511894Speter dup2(infd, STDIN_FILENO) != STDIN_FILENO 108611894Speter# endif 108711894Speter )) { 108811894Speter /* Avoid perror since it may misuse buffers. */ 108911894Speter write_stderr(args[1]); 109011894Speter write_stderr(": I/O redirection failed\n"); 109111894Speter _exit(EXIT_TROUBLE); 10929Sjkh } 109311894Speter 109411894Speter if (outname) 109511894Speter if (fdreopen( 109611894Speter STDOUT_FILENO, outname, 109711894Speter O_CREAT | O_TRUNC | O_WRONLY 109811894Speter ) < 0) { 109911894Speter /* Avoid perror since it may misuse buffers. */ 110011894Speter write_stderr(args[1]); 110111894Speter write_stderr(": "); 110211894Speter write_stderr(outname); 110311894Speter write_stderr(": cannot create\n"); 110411894Speter _exit(EXIT_TROUBLE); 110511894Speter } 110611894Speter VOID exec_RCS(args[1], (char**)(args + 1)); 110711894Speter notfound = args[1]; 110811894Speter# ifdef RCS_SHELL 110911894Speter if (errno == ENOEXEC) { 111011894Speter args[0] = notfound = RCS_SHELL; 111111894Speter VOID execv(args[0], (char**)args); 111211894Speter } 111311894Speter# endif 111411894Speter 111511894Speter /* Avoid perror since it may misuse buffers. */ 111611894Speter write_stderr(notfound); 111711894Speter write_stderr(": not found\n"); 11189Sjkh _exit(EXIT_TROUBLE); 11199Sjkh } 11209Sjkh if (pid < 0) 11219Sjkh efaterror("fork"); 11229Sjkh# if has_waitpid 11239Sjkh if (waitpid(pid, &wstatus, 0) < 0) 11249Sjkh efaterror("waitpid"); 11259Sjkh# else 112611894Speter { 112711894Speter pid_t w; 112811894Speter do { 112911894Speter if ((w = wait(&wstatus)) < 0) 113011894Speter efaterror("wait"); 113111894Speter } while (w != pid); 113211894Speter } 11339Sjkh# endif 11349Sjkh#else 11359Sjkh static struct buf b; 113611894Speter char const *p; 11379Sjkh 11389Sjkh /* Use system(). On many hosts system() discards signals. Yuck! */ 113911894Speter p = args + 1; 11409Sjkh bufscpy(&b, *p); 11419Sjkh while (*++p) 11429Sjkh bufargcat(&b, ' ', *p); 114311894Speter if (infd != -1 && infd != STDIN_FILENO) { 114411894Speter char redirection[32]; 114511894Speter VOID sprintf(redirection, "<&%d", infd); 114611894Speter bufscat(&b, redirection); 114711894Speter } 114811894Speter if (outname) 114911894Speter bufargcat(&b, '>', outname); 11509Sjkh wstatus = system(b.string); 11519Sjkh#endif 11529Sjkh#endif 11539Sjkh } 115411894Speter if (!WIFEXITED(wstatus)) { 115511894Speter if (WIFSIGNALED(wstatus)) { 115611894Speter psignal(WTERMSIG(wstatus), args[1]); 115711894Speter fatcleanup(1); 115811894Speter } 115911894Speter faterror("%s failed for unknown reason", args[1]); 116011894Speter } 11619Sjkh return WEXITSTATUS(wstatus); 11629Sjkh} 11639Sjkh 11649Sjkh#define CARGSMAX 20 11659Sjkh/* 11669Sjkh* Run a command. 116711894Speter* infd, if not -1, is the input file descriptor. 116811894Speter* outname, if nonzero, is the name of the output file. 116911894Speter* The remaining arguments specify the command and its arguments. 11709Sjkh*/ 11719Sjkh int 11729Sjkh#if has_prototypes 117311894Speterrun(int infd, char const *outname, ...) 11749Sjkh#else 11759Sjkh /*VARARGS2*/ 117611894Speterrun(infd, outname, va_alist) 117711894Speter int infd; 117811894Speter char const *outname; 11799Sjkh va_dcl 11809Sjkh#endif 11819Sjkh{ 11829Sjkh va_list ap; 11839Sjkh char const *rgargs[CARGSMAX]; 118411894Speter register int i; 118511894Speter vararg_start(ap, outname); 118611894Speter for (i = 1; (rgargs[i++] = va_arg(ap, char const*)); ) 11879Sjkh if (CARGSMAX <= i) 11889Sjkh faterror("too many command arguments"); 11899Sjkh va_end(ap); 119011894Speter return runv(infd, outname, rgargs); 11919Sjkh} 11929Sjkh 11939Sjkh 11949Sjkhint RCSversion; 11959Sjkh 11969Sjkh void 11979SjkhsetRCSversion(str) 11989Sjkh char const *str; 11999Sjkh{ 12009Sjkh static int oldversion; 12019Sjkh 12029Sjkh register char const *s = str + 2; 12039Sjkh 120411894Speter if (*s) { 120511894Speter int v = VERSION_DEFAULT; 12069Sjkh 120711894Speter if (oldversion) 120811894Speter redefined('V'); 120911894Speter oldversion = true; 12109Sjkh v = 0; 12119Sjkh while (isdigit(*s)) 12129Sjkh v = 10*v + *s++ - '0'; 12139Sjkh if (*s) 121411894Speter error("%s isn't a number", str); 121511894Speter else if (v < VERSION_min || VERSION_max < v) 121611894Speter error("%s out of range %d..%d", 121711894Speter str, VERSION_min, VERSION_max 121811894Speter ); 121911894Speter 122011894Speter RCSversion = VERSION(v); 122111894Speter } else { 122211894Speter printf("RCS version %s\n", RCS_version_string); 122311894Speter exit(0); 12249Sjkh } 12259Sjkh} 12269Sjkh 12279Sjkh int 12289SjkhgetRCSINIT(argc, argv, newargv) 12299Sjkh int argc; 12309Sjkh char **argv, ***newargv; 12319Sjkh{ 12329Sjkh register char *p, *q, **pp; 123325699Speter char const *ev; 123411894Speter size_t n; 12359Sjkh 123625699Speter if ((ev = cgetenv("RCSLOCALID"))) 123725699Speter setRCSLocalId(ev); 123825699Speter 123925699Speter if ((ev = cgetenv("RCSINCEXC"))) 124025699Speter setIncExc(ev); 124125699Speter 12429Sjkh if (!(q = cgetenv("RCSINIT"))) 12439Sjkh *newargv = argv; 12449Sjkh else { 12459Sjkh n = argc + 2; 12469Sjkh /* 12479Sjkh * Count spaces in RCSINIT to allocate a new arg vector. 12489Sjkh * This is an upper bound, but it's OK even if too large. 12499Sjkh */ 12509Sjkh for (p = q; ; ) { 12519Sjkh switch (*p++) { 12529Sjkh default: 12539Sjkh continue; 12549Sjkh 12559Sjkh case ' ': 12569Sjkh case '\b': case '\f': case '\n': 12579Sjkh case '\r': case '\t': case '\v': 12589Sjkh n++; 12599Sjkh continue; 12609Sjkh 12619Sjkh case '\0': 12629Sjkh break; 12639Sjkh } 12649Sjkh break; 12659Sjkh } 12669Sjkh *newargv = pp = tnalloc(char*, n); 12679Sjkh *pp++ = *argv++; /* copy program name */ 12689Sjkh for (p = q; ; ) { 12699Sjkh for (;;) { 12709Sjkh switch (*q) { 12719Sjkh case '\0': 12729Sjkh goto copyrest; 12739Sjkh 12749Sjkh case ' ': 12759Sjkh case '\b': case '\f': case '\n': 12769Sjkh case '\r': case '\t': case '\v': 12779Sjkh q++; 12789Sjkh continue; 12799Sjkh } 12809Sjkh break; 12819Sjkh } 12829Sjkh *pp++ = p; 12839Sjkh ++argc; 12849Sjkh for (;;) { 12859Sjkh switch ((*p++ = *q++)) { 12869Sjkh case '\0': 12879Sjkh goto copyrest; 12889Sjkh 12899Sjkh case '\\': 12909Sjkh if (!*q) 12919Sjkh goto copyrest; 12929Sjkh p[-1] = *q++; 12939Sjkh continue; 12949Sjkh 12959Sjkh default: 12969Sjkh continue; 12979Sjkh 12989Sjkh case ' ': 12999Sjkh case '\b': case '\f': case '\n': 13009Sjkh case '\r': case '\t': case '\v': 13019Sjkh break; 13029Sjkh } 13039Sjkh break; 13049Sjkh } 13059Sjkh p[-1] = '\0'; 13069Sjkh } 13079Sjkh copyrest: 13089Sjkh while ((*pp++ = *argv++)) 130911894Speter continue; 13109Sjkh } 13119Sjkh return argc; 13129Sjkh} 13139Sjkh 13149Sjkh 13159Sjkh#define cacheid(E) static uid_t i; static int s; if (!s){ s=1; i=(E); } return i 13169Sjkh 13179Sjkh#if has_getuid 13189Sjkh uid_t ruid() { cacheid(getuid()); } 13199Sjkh#endif 13209Sjkh#if has_setuid 13219Sjkh uid_t euid() { cacheid(geteuid()); } 13229Sjkh#endif 13239Sjkh 13249Sjkh 13259Sjkh#if has_setuid 13269Sjkh 13279Sjkh/* 13289Sjkh * Setuid execution really works only with Posix 1003.1a Draft 5 seteuid(), 13299Sjkh * because it lets us switch back and forth between arbitrary users. 13309Sjkh * If seteuid() doesn't work, we fall back on setuid(), 13319Sjkh * which works if saved setuid is supported, 13329Sjkh * unless the real or effective user is root. 13339Sjkh * This area is such a mess that we always check switches at runtime. 13349Sjkh */ 13359Sjkh 13369Sjkh static void 133711894Speter#if has_prototypes 133811894Speterset_uid_to(uid_t u) 133911894Speter#else 134011894Speter set_uid_to(u) uid_t u; 134111894Speter#endif 13429Sjkh/* Become user u. */ 13439Sjkh{ 13449Sjkh static int looping; 13459Sjkh 13469Sjkh if (euid() == ruid()) 13479Sjkh return; 13489Sjkh#if (has_fork||has_spawn) && DIFF_ABSOLUTE 134911894Speter# if has_setreuid 135011894Speter if (setreuid(u==euid() ? ruid() : euid(), u) != 0) 135111894Speter efaterror("setuid"); 135211894Speter# else 135311894Speter if (seteuid(u) != 0) 135411894Speter efaterror("setuid"); 135511894Speter# endif 13569Sjkh#endif 13579Sjkh if (geteuid() != u) { 13589Sjkh if (looping) 13599Sjkh return; 13609Sjkh looping = true; 13619Sjkh faterror("root setuid not supported" + (u?5:0)); 13629Sjkh } 13639Sjkh} 13649Sjkh 13659Sjkhstatic int stick_with_euid; 13669Sjkh 13679Sjkh void 13689Sjkh/* Ignore all calls to seteid() and setrid(). */ 13699Sjkhnosetid() 13709Sjkh{ 13719Sjkh stick_with_euid = true; 13729Sjkh} 13739Sjkh 13749Sjkh void 13759Sjkhseteid() 13769Sjkh/* Become effective user. */ 13779Sjkh{ 13789Sjkh if (!stick_with_euid) 13799Sjkh set_uid_to(euid()); 13809Sjkh} 13819Sjkh 13829Sjkh void 13839Sjkhsetrid() 13849Sjkh/* Become real user. */ 13859Sjkh{ 13869Sjkh if (!stick_with_euid) 13879Sjkh set_uid_to(ruid()); 13889Sjkh} 13899Sjkh#endif 139011894Speter 139111894Speter time_t 139211894Speternow() 139311894Speter{ 139411894Speter static time_t t; 139511894Speter if (!t && time(&t) == -1) 139611894Speter efaterror("time"); 139711894Speter return t; 139811894Speter} 1399