rcsutil.c revision 10
1/*
2 *                     RCS utilities
3 */
4
5/* Copyright (C) 1982, 1988, 1989 Walter Tichy
6   Copyright 1990, 1991 by Paul Eggert
7   Distributed under license by the Free Software Foundation, Inc.
8
9This file is part of RCS.
10
11RCS is free software; you can redistribute it and/or modify
12it under the terms of the GNU General Public License as published by
13the Free Software Foundation; either version 2, or (at your option)
14any later version.
15
16RCS is distributed in the hope that it will be useful,
17but WITHOUT ANY WARRANTY; without even the implied warranty of
18MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19GNU General Public License for more details.
20
21You should have received a copy of the GNU General Public License
22along with RCS; see the file COPYING.  If not, write to
23the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
24
25Report problems and direct all questions to:
26
27    rcs-bugs@cs.purdue.edu
28
29*/
30
31
32
33
34/* $Log: rcsutil.c,v $
35 * Revision 5.10  1991/10/07  17:32:46  eggert
36 * Support piece tables even if !has_mmap.
37 *
38 * Revision 5.9  1991/08/19  03:13:55  eggert
39 * Add spawn() support.  Explicate assumptions about getting invoker's name.
40 * Standardize user-visible dates.  Tune.
41 *
42 * Revision 5.8  1991/04/21  11:58:30  eggert
43 * Plug setuid security hole.
44 *
45 * Revision 5.6  1991/02/26  17:48:39  eggert
46 * Fix setuid bug.  Use fread, fwrite more portably.
47 * Support waitpid.  Don't assume -1 is acceptable to W* macros.
48 * strsave -> str_save (DG/UX name clash)
49 *
50 * Revision 5.5  1990/12/04  05:18:49  eggert
51 * Don't output a blank line after a signal diagnostic.
52 * Use -I for prompts and -q for diagnostics.
53 *
54 * Revision 5.4  1990/11/01  05:03:53  eggert
55 * Remove unneeded setid check.  Add awrite(), fremember().
56 *
57 * Revision 5.3  1990/10/06  00:16:45  eggert
58 * Don't fread F if feof(F).
59 *
60 * Revision 5.2  1990/09/04  08:02:31  eggert
61 * Store fread()'s result in an fread_type object.
62 *
63 * Revision 5.1  1990/08/29  07:14:07  eggert
64 * Declare getpwuid() more carefully.
65 *
66 * Revision 5.0  1990/08/22  08:13:46  eggert
67 * Add setuid support.  Permit multiple locks per user.
68 * Remove compile-time limits; use malloc instead.
69 * Switch to GMT.  Permit dates past 1999/12/31.
70 * Add -V.  Remove snooping.  Ansify and Posixate.
71 * Tune.  Some USG hosts define NSIG but not sys_siglist.
72 * Don't run /bin/sh if it's hopeless.
73 * Don't leave garbage behind if the output is an empty pipe.
74 * Clean up after SIGXCPU or SIGXFSZ.  Print name of signal that caused cleanup.
75 *
76 * Revision 4.6  89/05/01  15:13:40  narten
77 * changed copyright header to reflect current distribution rules
78 *
79 * Revision 4.5  88/11/08  16:01:02  narten
80 * corrected use of varargs routines
81 *
82 * Revision 4.4  88/08/09  19:13:24  eggert
83 * Check for memory exhaustion.
84 * Permit signal handlers to yield either 'void' or 'int'; fix oldSIGINT botch.
85 * Use execv(), not system(); yield exit status like diff(1)'s.
86 *
87 * Revision 4.3  87/10/18  10:40:22  narten
88 * Updating version numbers. Changes relative to 1.1 actually
89 * relative to 4.1
90 *
91 * Revision 1.3  87/09/24  14:01:01  narten
92 * Sources now pass through lint (if you ignore printf/sprintf/fprintf
93 * warnings)
94 *
95 * Revision 1.2  87/03/27  14:22:43  jenkins
96 * Port to suns
97 *
98 * Revision 4.1  83/05/10  15:53:13  wft
99 * Added getcaller() and findlock().
100 * Changed catchints() to check SIGINT for SIG_IGN before setting up the signal
101 * (needed for background jobs in older shells). Added restoreints().
102 * Removed printing of full RCS path from logcommand().
103 *
104 * Revision 3.8  83/02/15  15:41:49  wft
105 * Added routine fastcopy() to copy remainder of a file in blocks.
106 *
107 * Revision 3.7  82/12/24  15:25:19  wft
108 * added catchints(), ignoreints() for catching and ingnoring interrupts;
109 * fixed catchsig().
110 *
111 * Revision 3.6  82/12/08  21:52:05  wft
112 * Using DATEFORM to format dates.
113 *
114 * Revision 3.5  82/12/04  18:20:49  wft
115 * Replaced SNOOPDIR with SNOOPFILE; changed addlock() to update
116 * lockedby-field.
117 *
118 * Revision 3.4  82/12/03  17:17:43  wft
119 * Added check to addlock() ensuring only one lock per person.
120 * Addlock also returns a pointer to the lock created. Deleted fancydate().
121 *
122 * Revision 3.3  82/11/27  12:24:37  wft
123 * moved rmsema(), trysema(), trydiraccess(), getfullRCSname() to rcsfnms.c.
124 * Introduced macro SNOOP so that snoop can be placed in directory other than
125 * TARGETDIR. Changed %02d to %.2d for compatibility reasons.
126 *
127 * Revision 3.2  82/10/18  21:15:11  wft
128 * added function getfullRCSname().
129 *
130 * Revision 3.1  82/10/13  16:17:37  wft
131 * Cleanup message is now suppressed in quiet mode.
132 */
133
134
135
136
137#include "rcsbase.h"
138
139libId(utilId, "$Id: rcsutil.c,v 5.10 1991/10/07 17:32:46 eggert Exp $")
140
141#if !has_memcmp
142	int
143memcmp(s1, s2, n)
144	void const *s1, *s2;
145	size_t n;
146{
147	register unsigned char const
148		*p1 = (unsigned char const*)s1,
149		*p2 = (unsigned char const*)s2;
150	register size_t i = n;
151	register int r = 0;
152	while (i--  &&  !(r = (*p1++ - *p2++)))
153		;
154	return r;
155}
156#endif
157
158#if !has_memcpy
159	void *
160memcpy(s1, s2, n)
161	void *s1;
162	void const *s2;
163	size_t n;
164{
165	register char *p1 = (char*)s1;
166	register char const *p2 = (char const*)s2;
167	while (n--)
168		*p1++ = *p2++;
169	return s1;
170}
171#endif
172
173#if lint
174	malloc_type lintalloc;
175#endif
176
177/*
178 * list of blocks allocated with ftestalloc()
179 * These blocks can be freed by ffree when we're done with the current file.
180 * We could put the free block inside struct alloclist, rather than a pointer
181 * to the free block, but that would be less portable.
182 */
183struct alloclist {
184	malloc_type alloc;
185	struct alloclist *nextalloc;
186};
187static struct alloclist *alloced;
188
189
190	static malloc_type
191okalloc(p)
192	malloc_type p;
193{
194	if (!p)
195		faterror("out of memory");
196	return p;
197}
198
199	malloc_type
200testalloc(size)
201	size_t size;
202/* Allocate a block, testing that the allocation succeeded.  */
203{
204	return okalloc(malloc(size));
205}
206
207	malloc_type
208testrealloc(ptr, size)
209	malloc_type ptr;
210	size_t size;
211/* Reallocate a block, testing that the allocation succeeded.  */
212{
213	return okalloc(realloc(ptr, size));
214}
215
216	malloc_type
217fremember(ptr)
218	malloc_type ptr;
219/* Remember PTR in 'alloced' so that it can be freed later.  Yield PTR.  */
220{
221	register struct alloclist *q = talloc(struct alloclist);
222	q->nextalloc = alloced;
223	alloced = q;
224	return q->alloc = ptr;
225}
226
227	malloc_type
228ftestalloc(size)
229	size_t size;
230/* Allocate a block, putting it in 'alloced' so it can be freed later. */
231{
232	return fremember(testalloc(size));
233}
234
235	void
236ffree()
237/* Free all blocks allocated with ftestalloc().  */
238{
239	register struct alloclist *p, *q;
240	for (p = alloced;  p;  p = q) {
241		q = p->nextalloc;
242		tfree(p->alloc);
243		tfree(p);
244	}
245	alloced = nil;
246}
247
248	void
249ffree1(f)
250	register char const *f;
251/* Free the block f, which was allocated by ftestalloc.  */
252{
253	register struct alloclist *p, **a = &alloced;
254
255	while ((p = *a)->alloc  !=  f)
256		a = &p->nextalloc;
257	*a = p->nextalloc;
258	tfree(p->alloc);
259	tfree(p);
260}
261
262	char *
263str_save(s)
264	char const *s;
265/* Save s in permanently allocated storage. */
266{
267	return strcpy(tnalloc(char, strlen(s)+1), s);
268}
269
270	char *
271fstr_save(s)
272	char const *s;
273/* Save s in storage that will be deallocated when we're done with this file. */
274{
275	return strcpy(ftnalloc(char, strlen(s)+1), s);
276}
277
278	char *
279cgetenv(name)
280	char const *name;
281/* Like getenv(), but yield a copy; getenv() can overwrite old results. */
282{
283	register char *p;
284
285	return (p=getenv(name)) ? str_save(p) : p;
286}
287
288	char const *
289getusername(suspicious)
290	int suspicious;
291/* Get the caller's login name.  Trust only getwpuid if SUSPICIOUS.  */
292{
293	static char *name;
294
295	if (!name) {
296		if (
297		    /* Prefer getenv() unless suspicious; it's much faster.  */
298#		    if getlogin_is_secure
299			    (suspicious
300			    ||
301				!(name = cgetenv("LOGNAME"))
302			    &&  !(name = cgetenv("USER")))
303			&&  !(name = getlogin())
304#		    else
305			suspicious
306			||
307				!(name = cgetenv("LOGNAME"))
308			    &&  !(name = cgetenv("USER"))
309			    &&  !(name = getlogin())
310#		    endif
311		) {
312#if has_getuid && has_getpwuid
313			struct passwd const *pw = getpwuid(ruid());
314			if (!pw)
315			    faterror("no password entry for userid %lu",
316				     (unsigned long)ruid()
317			    );
318			name = pw->pw_name;
319#else
320#if has_setuid
321			faterror("setuid not supported");
322#else
323			faterror("Who are you?  Please set LOGNAME.");
324#endif
325#endif
326		}
327		checksid(name);
328	}
329	return name;
330}
331
332
333
334
335#if has_signal
336
337/*
338 *	 Signal handling
339 *
340 * Standard C places too many restrictions on signal handlers.
341 * We obey as many of them as we can.
342 * Posix places fewer restrictions, and we are Posix-compatible here.
343 */
344
345static sig_atomic_t volatile heldsignal, holdlevel;
346
347	static signal_type
348catchsig(s)
349	int s;
350{
351	char const *sname;
352	char buf[BUFSIZ];
353
354#if sig_zaps_handler
355	/* If a signal arrives before we reset the signal handler, we lose. */
356	VOID signal(s, SIG_IGN);
357#endif
358	if (holdlevel) {
359		heldsignal = s;
360		return;
361	}
362	ignoreints();
363	setrid();
364	if (!quietflag) {
365	    sname = nil;
366#if has_sys_siglist && defined(NSIG)
367	    if ((unsigned)s < NSIG) {
368#		ifndef sys_siglist
369		    extern char const *sys_siglist[];
370#		endif
371		sname = sys_siglist[s];
372	    }
373#else
374	    switch (s) {
375#ifdef SIGHUP
376		case SIGHUP:	sname = "Hangup";  break;
377#endif
378#ifdef SIGINT
379		case SIGINT:	sname = "Interrupt";  break;
380#endif
381#ifdef SIGPIPE
382		case SIGPIPE:	sname = "Broken pipe";  break;
383#endif
384#ifdef SIGQUIT
385		case SIGQUIT:	sname = "Quit";  break;
386#endif
387#ifdef SIGTERM
388		case SIGTERM:	sname = "Terminated";  break;
389#endif
390#ifdef SIGXCPU
391		case SIGXCPU:	sname = "Cputime limit exceeded";  break;
392#endif
393#ifdef SIGXFSZ
394		case SIGXFSZ:	sname = "Filesize limit exceeded";  break;
395#endif
396	    }
397#endif
398	    if (sname)
399		VOID sprintf(buf, "\nRCS: %s.  Cleaning up.\n", sname);
400	    else
401		VOID sprintf(buf, "\nRCS: Signal %d.  Cleaning up.\n", s);
402	    VOID write(STDERR_FILENO, buf, strlen(buf));
403	}
404	exiterr();
405}
406
407	void
408ignoreints()
409{
410	++holdlevel;
411}
412
413	void
414restoreints()
415{
416	if (!--holdlevel && heldsignal)
417		VOID catchsig(heldsignal);
418}
419
420
421static int const sig[] = {
422#ifdef SIGHUP
423	SIGHUP,
424#endif
425#ifdef SIGINT
426	SIGINT,
427#endif
428#ifdef SIGPIPE
429	SIGPIPE,
430#endif
431#ifdef SIGQUIT
432	SIGQUIT,
433#endif
434#ifdef SIGTERM
435	SIGTERM,
436#endif
437#ifdef SIGXCPU
438	SIGXCPU,
439#endif
440#ifdef SIGXFSZ
441	SIGXFSZ,
442#endif
443};
444#define SIGS (sizeof(sig)/sizeof(*sig))
445
446
447#if has_sigaction
448
449	static void
450  check_sig(r)
451	int r;
452  {
453	if (r != 0)
454		efaterror("signal");
455  }
456
457	static void
458  setup_catchsig()
459  {
460	register int i;
461	sigset_t blocked;
462	struct sigaction act;
463
464	check_sig(sigemptyset(&blocked));
465	for (i=SIGS; 0<=--i; )
466	    check_sig(sigaddset(&blocked, sig[i]));
467	for (i=SIGS; 0<=--i; ) {
468	    check_sig(sigaction(sig[i], (struct sigaction*)nil, &act));
469	    if (act.sa_handler != SIG_IGN) {
470		    act.sa_handler = catchsig;
471		    act.sa_mask = blocked;
472		    check_sig(sigaction(sig[i], &act, (struct sigaction*)nil));
473	    }
474	}
475  }
476
477#else
478#if has_sigblock
479
480	static void
481  setup_catchsig()
482  {
483	register int i;
484	int mask;
485
486	mask = 0;
487	for (i=SIGS; 0<=--i; )
488		mask |= sigmask(sig[i]);
489	mask = sigblock(mask);
490	for (i=SIGS; 0<=--i; )
491		if (
492		    signal(sig[i], catchsig) == SIG_IGN  &&
493		    signal(sig[i], SIG_IGN) != catchsig
494		)
495			faterror("signal catcher failure");
496	VOID sigsetmask(mask);
497  }
498
499#else
500
501	static void
502  setup_catchsig()
503  {
504	register i;
505
506	for (i=SIGS; 0<=--i; )
507		if (
508		    signal(sig[i], SIG_IGN) != SIG_IGN  &&
509		    signal(sig[i], catchsig) != SIG_IGN
510		)
511			faterror("signal catcher failure");
512  }
513
514#endif
515#endif
516
517	void
518catchints()
519{
520	static int catching_ints;
521	if (!catching_ints) {
522		catching_ints = true;
523		setup_catchsig();
524	}
525}
526
527#endif /* has_signal */
528
529
530	void
531fastcopy(inf,outf)
532	register RILE *inf;
533	FILE *outf;
534/* Function: copies the remainder of file inf to outf.
535 */
536{
537#if large_memory
538#	if has_mmap
539	    awrite((char const*)inf->ptr, (size_t)(inf->lim - inf->ptr), outf);
540	    inf->ptr = inf->lim;
541#	else
542	    for (;;) {
543		awrite((char const*)inf->ptr, (size_t)(inf->readlim - inf->ptr), outf);
544		inf->ptr = inf->readlim;
545		if (inf->ptr == inf->lim)
546		    break;
547		VOID Igetmore(inf);
548	    }
549#	endif
550#else
551	char buf[BUFSIZ*8];
552	register fread_type rcount;
553
554        /*now read the rest of the file in blocks*/
555	while (!feof(inf)) {
556		if (!(rcount = Fread(buf,sizeof(*buf),sizeof(buf),inf))) {
557			testIerror(inf);
558			return;
559		}
560		awrite(buf, (size_t)rcount, outf);
561        }
562#endif
563}
564
565#ifndef SSIZE_MAX
566 /* This does not work in #ifs, but it's good enough for us.  */
567 /* Underestimating SSIZE_MAX may slow us down, but it won't break us.  */
568#	define SSIZE_MAX ((unsigned)-1 >> 1)
569#endif
570
571	void
572awrite(buf, chars, f)
573	char const *buf;
574	size_t chars;
575	FILE *f;
576{
577	/* Posix 1003.1-1990 ssize_t hack */
578	while (SSIZE_MAX < chars) {
579		if (Fwrite(buf, sizeof(*buf), SSIZE_MAX, f)  !=  SSIZE_MAX)
580			Oerror();
581		buf += SSIZE_MAX;
582		chars -= SSIZE_MAX;
583	}
584
585	if (Fwrite(buf, sizeof(*buf), chars, f)  !=  chars)
586		Oerror();
587}
588
589
590
591
592
593	static int
594movefd(old, new)
595	int old, new;
596{
597	if (old < 0  ||  old == new)
598		return old;
599#	ifdef F_DUPFD
600		new = fcntl(old, F_DUPFD, new);
601#	else
602		new = dup2(old, new);
603#	endif
604	return close(old)==0 ? new : -1;
605}
606
607	static int
608fdreopen(fd, file, flags)
609	int fd;
610	char const *file;
611	int flags;
612{
613	int newfd;
614	VOID close(fd);
615	newfd =
616#if !open_can_creat
617		flags&O_CREAT ? creat(file, S_IRUSR|S_IWUSR) :
618#endif
619		open(file, flags, S_IRUSR|S_IWUSR);
620	return movefd(newfd, fd);
621}
622
623#if !has_spawn
624	static void
625tryopen(fd,file,flags)
626	int fd, flags;
627	char const *file;
628{
629	if (file  &&  fdreopen(fd,file,flags) != fd)
630		efaterror(file);
631}
632#else
633	static int
634tryopen(fd,file,flags)
635	int fd, flags;
636	char const *file;
637{
638	int newfd = -1;
639	if (file  &&  ((newfd=dup(fd)) < 0  ||  fdreopen(fd,file,flags) != fd))
640		efaterror(file);
641	return newfd;
642}
643	static void
644redirect(old, new)
645	int old, new;
646{
647	if (0 <= old   &&   (close(new) != 0  ||  movefd(old,new) < 0))
648		efaterror("spawn I/O redirection");
649}
650#endif
651
652
653
654#if !has_fork && !has_spawn
655	static void
656bufargcat(b, c, s)
657	register struct buf *b;
658	int c;
659	register char const *s;
660/* Append to B a copy of C, plus a quoted copy of S.  */
661{
662	register char *p;
663	register char const *t;
664	size_t bl, sl;
665
666	for (t=s, sl=0;  *t;  )
667		sl  +=  3*(*t++=='\'') + 1;
668	bl = strlen(b->string);
669	bufrealloc(b, bl + sl + 4);
670	p = b->string + bl;
671	*p++ = c;
672	*p++ = '\'';
673	while (*s) {
674		if (*s == '\'') {
675			*p++ = '\'';
676			*p++ = '\\';
677			*p++ = '\'';
678		}
679		*p++ = *s++;
680	}
681	*p++ = '\'';
682	*p = 0;
683}
684#endif
685
686/*
687* Run a command specified by the strings in 'inoutargs'.
688* inoutargs[0], if nonnil, is the name of the input file.
689* inoutargs[1], if nonnil, is the name of the output file.
690* inoutargs[2..] form the command to be run.
691*/
692	int
693runv(inoutargs)
694	char const **inoutargs;
695{
696	register char const **p;
697	int wstatus;
698
699	oflush();
700	eflush();
701    {
702#if has_spawn
703	int in, out;
704	p = inoutargs;
705	in = tryopen(STDIN_FILENO, *p++, O_BINARY|O_RDONLY);
706	out = tryopen(STDOUT_FILENO, *p++, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY);
707	wstatus = spawn_RCS(0, *p, (char*const*)p);
708	if (wstatus == -1  &&  errno == ENOEXEC) {
709		*--p = RCS_SHELL;
710		wstatus = spawnv(0, *p, (char*const*)p);
711	}
712	redirect(in, STDIN_FILENO);
713	redirect(out, STDOUT_FILENO);
714#else
715#if has_fork
716	pid_t pid;
717#	if !has_waitpid
718		pid_t w;
719#	endif
720	if (!(pid = vfork())) {
721		p = inoutargs;
722		tryopen(STDIN_FILENO, *p++, O_BINARY|O_RDONLY);
723		tryopen(STDOUT_FILENO, *p++, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY);
724		VOID exec_RCS(*p, (char*const*)p);
725		if (errno == ENOEXEC) {
726			*--p = RCS_SHELL;
727			VOID execv(*p, (char*const*)p);
728		}
729		VOID write(STDERR_FILENO, *p, strlen(*p));
730		VOID write(STDERR_FILENO, ": not found\n", 12);
731		_exit(EXIT_TROUBLE);
732	}
733	if (pid < 0)
734		efaterror("fork");
735#	if has_waitpid
736		if (waitpid(pid, &wstatus, 0) < 0)
737			efaterror("waitpid");
738#	else
739		do {
740			if ((w = wait(&wstatus)) < 0)
741				efaterror("wait");
742		} while (w != pid);
743#	endif
744#else
745	static struct buf b;
746
747	/* Use system().  On many hosts system() discards signals.  Yuck!  */
748	p = inoutargs+2;
749	bufscpy(&b, *p);
750	while (*++p)
751		bufargcat(&b, ' ', *p);
752	if (inoutargs[0])
753		bufargcat(&b, '<', inoutargs[0]);
754	if (inoutargs[1])
755		bufargcat(&b, '>', inoutargs[1]);
756	wstatus = system(b.string);
757#endif
758#endif
759    }
760	if (!WIFEXITED(wstatus))
761		faterror("%s failed", inoutargs[2]);
762	return WEXITSTATUS(wstatus);
763}
764
765#define CARGSMAX 20
766/*
767* Run a command.
768* The first two arguments are the input and output files (if nonnil);
769* the rest specify the command and its arguments.
770*/
771	int
772#if has_prototypes
773run(char const *infile, char const *outfile, ...)
774#else
775	/*VARARGS2*/
776run(infile, outfile, va_alist)
777	char const *infile;
778	char const *outfile;
779	va_dcl
780#endif
781{
782	va_list ap;
783	char const *rgargs[CARGSMAX];
784	register i = 0;
785	rgargs[0] = infile;
786	rgargs[1] = outfile;
787	vararg_start(ap, outfile);
788	for (i = 2;  (rgargs[i++] = va_arg(ap, char const*));  )
789		if (CARGSMAX <= i)
790			faterror("too many command arguments");
791	va_end(ap);
792	return runv(rgargs);
793}
794
795
796	char const *
797date2str(date, datebuf)
798	char const date[datesize];
799	char datebuf[datesize];
800/*
801* Format a user-readable form of the RCS format DATE into the buffer DATEBUF.
802* Yield DATEBUF.
803*/
804{
805	register char const *p = date;
806
807	while (*p++ != '.')
808		;
809	VOID sprintf(datebuf,
810		"19%.*s/%.2s/%.2s %.2s:%.2s:%s" +
811			(date[2]=='.' && VERSION(5)<=RCSversion  ?  0  :  2),
812		(int)(p-date-1), date,
813		p, p+3, p+6, p+9, p+12
814	);
815	return datebuf;
816}
817
818
819int RCSversion;
820
821	void
822setRCSversion(str)
823	char const *str;
824{
825	static int oldversion;
826
827	register char const *s = str + 2;
828	int v = VERSION_DEFAULT;
829
830	if (oldversion)
831		redefined('V');
832	oldversion = true;
833
834	if (*s) {
835		v = 0;
836		while (isdigit(*s))
837			v  =  10*v + *s++ - '0';
838		if (*s)
839			faterror("%s isn't a number", str);
840		if (v < VERSION_min  ||  VERSION_max < v)
841			faterror("%s out of range %d..%d", str, VERSION_min, VERSION_max);
842	}
843
844	RCSversion = VERSION(v);
845}
846
847	int
848getRCSINIT(argc, argv, newargv)
849	int argc;
850	char **argv, ***newargv;
851{
852	register char *p, *q, **pp;
853	unsigned n;
854
855	if (!(q = cgetenv("RCSINIT")))
856		*newargv = argv;
857	else {
858		n = argc + 2;
859		/*
860		 * Count spaces in RCSINIT to allocate a new arg vector.
861		 * This is an upper bound, but it's OK even if too large.
862		 */
863		for (p = q;  ;  ) {
864			switch (*p++) {
865			    default:
866				continue;
867
868			    case ' ':
869			    case '\b': case '\f': case '\n':
870			    case '\r': case '\t': case '\v':
871				n++;
872				continue;
873
874			    case '\0':
875				break;
876			}
877			break;
878		}
879		*newargv = pp = tnalloc(char*, n);
880		*pp++ = *argv++; /* copy program name */
881		for (p = q;  ;  ) {
882			for (;;) {
883				switch (*q) {
884				    case '\0':
885					goto copyrest;
886
887				    case ' ':
888				    case '\b': case '\f': case '\n':
889				    case '\r': case '\t': case '\v':
890					q++;
891					continue;
892				}
893				break;
894			}
895			*pp++ = p;
896			++argc;
897			for (;;) {
898				switch ((*p++ = *q++)) {
899				    case '\0':
900					goto copyrest;
901
902				    case '\\':
903					if (!*q)
904						goto copyrest;
905					p[-1] = *q++;
906					continue;
907
908				    default:
909					continue;
910
911				    case ' ':
912				    case '\b': case '\f': case '\n':
913				    case '\r': case '\t': case '\v':
914					break;
915				}
916				break;
917			}
918			p[-1] = '\0';
919		}
920	    copyrest:
921		while ((*pp++ = *argv++))
922			;
923	}
924	return argc;
925}
926
927
928#define cacheid(E) static uid_t i; static int s; if (!s){ s=1; i=(E); } return i
929
930#if has_getuid
931	uid_t ruid() { cacheid(getuid()); }
932#endif
933#if has_setuid
934	uid_t euid() { cacheid(geteuid()); }
935#endif
936
937
938#if has_setuid
939
940/*
941 * Setuid execution really works only with Posix 1003.1a Draft 5 seteuid(),
942 * because it lets us switch back and forth between arbitrary users.
943 * If seteuid() doesn't work, we fall back on setuid(),
944 * which works if saved setuid is supported,
945 * unless the real or effective user is root.
946 * This area is such a mess that we always check switches at runtime.
947 */
948
949	static void
950set_uid_to(u)
951	uid_t u;
952/* Become user u.  */
953{
954	static int looping;
955
956	if (euid() == ruid())
957		return;
958#if (has_fork||has_spawn) && DIFF_ABSOLUTE
959	if (seteuid(u) != 0)
960		efaterror("setuid");
961#endif
962	if (geteuid() != u) {
963		if (looping)
964			return;
965		looping = true;
966		faterror("root setuid not supported" + (u?5:0));
967	}
968}
969
970static int stick_with_euid;
971
972	void
973/* Ignore all calls to seteid() and setrid().  */
974nosetid()
975{
976	stick_with_euid = true;
977}
978
979	void
980seteid()
981/* Become effective user.  */
982{
983	if (!stick_with_euid)
984		set_uid_to(euid());
985}
986
987	void
988setrid()
989/* Become real user.  */
990{
991	if (!stick_with_euid)
992		set_uid_to(ruid());
993}
994#endif
995