Deleted Added
full compact
rcsutil.c (22996) rcsutil.c (25699)
1/* RCS utility functions */
2
3/* Copyright 1982, 1988, 1989 Walter Tichy
4 Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
5 Distributed under license by the Free Software Foundation, Inc.
6
7This file is part of RCS.
8
9RCS is free software; you can redistribute it and/or modify
10it under the terms of the GNU General Public License as published by
11the Free Software Foundation; either version 2, or (at your option)
12any later version.
13
14RCS is distributed in the hope that it will be useful,
15but WITHOUT ANY WARRANTY; without even the implied warranty of
16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17GNU General Public License for more details.
18
19You should have received a copy of the GNU General Public License
20along with RCS; see the file COPYING.
21If not, write to the Free Software Foundation,
2259 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23
24Report problems and direct all questions to:
25
26 rcs-bugs@cs.purdue.edu
27
28*/
29
30
31
32
33/*
34 * Revision 5.20 1995/06/16 06:19:24 eggert
35 * (catchsig): Remove `return'.
36 * Update FSF address.
37 *
38 * Revision 5.19 1995/06/02 18:19:00 eggert
39 * (catchsigaction): New name for `catchsig', for sa_sigaction signature.
40 * Use nRCS even if !has_psiginfo, to remove unused variable warning.
41 * (setup_catchsig): Use sa_sigaction only if has_sa_sigaction.
42 * Use ENOTSUP only if defined.
43 *
44 * Revision 5.18 1995/06/01 16:23:43 eggert
45 * (catchsig, restoreints, setup_catchsig): Use SA_SIGINFO, not has_psiginfo,
46 * to determine whether to use SA_SIGINFO feature,
47 * but also check at runtime whether the feature works.
48 * (catchsig): If an mmap_signal occurs, report the affected file name.
49 * (unsupported_SA_SIGINFO, accessName): New variables.
50 * (setup_catchsig): If using SA_SIGINFO, use sa_sigaction, not sa_handler.
51 * If SA_SIGINFO fails, fall back on sa_handler method.
52 *
53 * (readAccessFilenameBuffer, dupSafer, fdSafer, fopenSafer): New functions.
54 * (concatenate): Remove.
55 *
56 * (runv): Work around bad_wait_if_SIGCHLD_ignored bug.
57 * Remove reference to OPEN_O_WORK.
58 *
59 * Revision 5.17 1994/03/20 04:52:58 eggert
60 * Specify subprocess input via file descriptor, not file name.
61 * Avoid messing with I/O buffers in the child process.
62 * Define dup in terms of F_DUPFD if it exists.
63 * Move setmtime to rcsedit.c. Remove lint.
64 *
65 * Revision 5.16 1993/11/09 17:40:15 eggert
66 * -V now prints version on stdout and exits.
67 *
68 * Revision 5.15 1993/11/03 17:42:27 eggert
69 * Use psiginfo and setreuid if available. Move date2str to maketime.c.
70 *
71 * Revision 5.14 1992/07/28 16:12:44 eggert
72 * Add -V. has_sigaction overrides sig_zaps_handler. Fix -M bug.
73 * Add mmap_signal, which minimizes signal handling for non-mmap hosts.
74 *
75 * Revision 5.13 1992/02/17 23:02:28 eggert
76 * Work around NFS mmap SIGBUS problem. Add -T support.
77 *
78 * Revision 5.12 1992/01/24 18:44:19 eggert
79 * Work around NFS mmap bug that leads to SIGBUS core dumps. lint -> RCS_lint
80 *
81 * Revision 5.11 1992/01/06 02:42:34 eggert
82 * O_BINARY -> OPEN_O_WORK
83 * while (E) ; -> while (E) continue;
84 *
85 * Revision 5.10 1991/10/07 17:32:46 eggert
86 * Support piece tables even if !has_mmap.
87 *
88 * Revision 5.9 1991/08/19 03:13:55 eggert
89 * Add spawn() support. Explicate assumptions about getting invoker's name.
90 * Standardize user-visible dates. Tune.
91 *
92 * Revision 5.8 1991/04/21 11:58:30 eggert
93 * Plug setuid security hole.
94 *
95 * Revision 5.6 1991/02/26 17:48:39 eggert
96 * Fix setuid bug. Use fread, fwrite more portably.
97 * Support waitpid. Don't assume -1 is acceptable to W* macros.
98 * strsave -> str_save (DG/UX name clash)
99 *
100 * Revision 5.5 1990/12/04 05:18:49 eggert
101 * Don't output a blank line after a signal diagnostic.
102 * Use -I for prompts and -q for diagnostics.
103 *
104 * Revision 5.4 1990/11/01 05:03:53 eggert
105 * Remove unneeded setid check. Add awrite(), fremember().
106 *
107 * Revision 5.3 1990/10/06 00:16:45 eggert
108 * Don't fread F if feof(F).
109 *
110 * Revision 5.2 1990/09/04 08:02:31 eggert
111 * Store fread()'s result in an fread_type object.
112 *
113 * Revision 5.1 1990/08/29 07:14:07 eggert
114 * Declare getpwuid() more carefully.
115 *
116 * Revision 5.0 1990/08/22 08:13:46 eggert
117 * Add setuid support. Permit multiple locks per user.
118 * Remove compile-time limits; use malloc instead.
119 * Switch to GMT. Permit dates past 1999/12/31.
120 * Add -V. Remove snooping. Ansify and Posixate.
121 * Tune. Some USG hosts define NSIG but not sys_siglist.
122 * Don't run /bin/sh if it's hopeless.
123 * Don't leave garbage behind if the output is an empty pipe.
124 * Clean up after SIGXCPU or SIGXFSZ. Print name of signal that caused cleanup.
125 *
126 * Revision 4.6 89/05/01 15:13:40 narten
127 * changed copyright header to reflect current distribution rules
128 *
129 * Revision 4.5 88/11/08 16:01:02 narten
130 * corrected use of varargs routines
131 *
132 * Revision 4.4 88/08/09 19:13:24 eggert
133 * Check for memory exhaustion.
134 * Permit signal handlers to yield either 'void' or 'int'; fix oldSIGINT botch.
135 * Use execv(), not system(); yield exit status like diff(1)'s.
136 *
137 * Revision 4.3 87/10/18 10:40:22 narten
138 * Updating version numbers. Changes relative to 1.1 actually
139 * relative to 4.1
140 *
141 * Revision 1.3 87/09/24 14:01:01 narten
142 * Sources now pass through lint (if you ignore printf/sprintf/fprintf
143 * warnings)
144 *
145 * Revision 1.2 87/03/27 14:22:43 jenkins
146 * Port to suns
147 *
148 * Revision 4.1 83/05/10 15:53:13 wft
149 * Added getcaller() and findlock().
150 * Changed catchints() to check SIGINT for SIG_IGN before setting up the signal
151 * (needed for background jobs in older shells). Added restoreints().
152 * Removed printing of full RCS path from logcommand().
153 *
154 * Revision 3.8 83/02/15 15:41:49 wft
155 * Added routine fastcopy() to copy remainder of a file in blocks.
156 *
157 * Revision 3.7 82/12/24 15:25:19 wft
158 * added catchints(), ignoreints() for catching and ingnoring interrupts;
159 * fixed catchsig().
160 *
161 * Revision 3.6 82/12/08 21:52:05 wft
162 * Using DATEFORM to format dates.
163 *
164 * Revision 3.5 82/12/04 18:20:49 wft
165 * Replaced SNOOPDIR with SNOOPFILE; changed addlock() to update
166 * lockedby-field.
167 *
168 * Revision 3.4 82/12/03 17:17:43 wft
169 * Added check to addlock() ensuring only one lock per person.
170 * Addlock also returns a pointer to the lock created. Deleted fancydate().
171 *
172 * Revision 3.3 82/11/27 12:24:37 wft
173 * moved rmsema(), trysema(), trydiraccess(), getfullRCSname() to rcsfnms.c.
174 * Introduced macro SNOOP so that snoop can be placed in directory other than
175 * TARGETDIR. Changed %02d to %.2d for compatibility reasons.
176 *
177 * Revision 3.2 82/10/18 21:15:11 wft
178 * added function getfullRCSname().
179 *
180 * Revision 3.1 82/10/13 16:17:37 wft
181 * Cleanup message is now suppressed in quiet mode.
182 */
183
184
185
186
187#include "rcsbase.h"
188
1/* RCS utility functions */
2
3/* Copyright 1982, 1988, 1989 Walter Tichy
4 Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
5 Distributed under license by the Free Software Foundation, Inc.
6
7This file is part of RCS.
8
9RCS is free software; you can redistribute it and/or modify
10it under the terms of the GNU General Public License as published by
11the Free Software Foundation; either version 2, or (at your option)
12any later version.
13
14RCS is distributed in the hope that it will be useful,
15but WITHOUT ANY WARRANTY; without even the implied warranty of
16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17GNU General Public License for more details.
18
19You should have received a copy of the GNU General Public License
20along with RCS; see the file COPYING.
21If not, write to the Free Software Foundation,
2259 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23
24Report problems and direct all questions to:
25
26 rcs-bugs@cs.purdue.edu
27
28*/
29
30
31
32
33/*
34 * Revision 5.20 1995/06/16 06:19:24 eggert
35 * (catchsig): Remove `return'.
36 * Update FSF address.
37 *
38 * Revision 5.19 1995/06/02 18:19:00 eggert
39 * (catchsigaction): New name for `catchsig', for sa_sigaction signature.
40 * Use nRCS even if !has_psiginfo, to remove unused variable warning.
41 * (setup_catchsig): Use sa_sigaction only if has_sa_sigaction.
42 * Use ENOTSUP only if defined.
43 *
44 * Revision 5.18 1995/06/01 16:23:43 eggert
45 * (catchsig, restoreints, setup_catchsig): Use SA_SIGINFO, not has_psiginfo,
46 * to determine whether to use SA_SIGINFO feature,
47 * but also check at runtime whether the feature works.
48 * (catchsig): If an mmap_signal occurs, report the affected file name.
49 * (unsupported_SA_SIGINFO, accessName): New variables.
50 * (setup_catchsig): If using SA_SIGINFO, use sa_sigaction, not sa_handler.
51 * If SA_SIGINFO fails, fall back on sa_handler method.
52 *
53 * (readAccessFilenameBuffer, dupSafer, fdSafer, fopenSafer): New functions.
54 * (concatenate): Remove.
55 *
56 * (runv): Work around bad_wait_if_SIGCHLD_ignored bug.
57 * Remove reference to OPEN_O_WORK.
58 *
59 * Revision 5.17 1994/03/20 04:52:58 eggert
60 * Specify subprocess input via file descriptor, not file name.
61 * Avoid messing with I/O buffers in the child process.
62 * Define dup in terms of F_DUPFD if it exists.
63 * Move setmtime to rcsedit.c. Remove lint.
64 *
65 * Revision 5.16 1993/11/09 17:40:15 eggert
66 * -V now prints version on stdout and exits.
67 *
68 * Revision 5.15 1993/11/03 17:42:27 eggert
69 * Use psiginfo and setreuid if available. Move date2str to maketime.c.
70 *
71 * Revision 5.14 1992/07/28 16:12:44 eggert
72 * Add -V. has_sigaction overrides sig_zaps_handler. Fix -M bug.
73 * Add mmap_signal, which minimizes signal handling for non-mmap hosts.
74 *
75 * Revision 5.13 1992/02/17 23:02:28 eggert
76 * Work around NFS mmap SIGBUS problem. Add -T support.
77 *
78 * Revision 5.12 1992/01/24 18:44:19 eggert
79 * Work around NFS mmap bug that leads to SIGBUS core dumps. lint -> RCS_lint
80 *
81 * Revision 5.11 1992/01/06 02:42:34 eggert
82 * O_BINARY -> OPEN_O_WORK
83 * while (E) ; -> while (E) continue;
84 *
85 * Revision 5.10 1991/10/07 17:32:46 eggert
86 * Support piece tables even if !has_mmap.
87 *
88 * Revision 5.9 1991/08/19 03:13:55 eggert
89 * Add spawn() support. Explicate assumptions about getting invoker's name.
90 * Standardize user-visible dates. Tune.
91 *
92 * Revision 5.8 1991/04/21 11:58:30 eggert
93 * Plug setuid security hole.
94 *
95 * Revision 5.6 1991/02/26 17:48:39 eggert
96 * Fix setuid bug. Use fread, fwrite more portably.
97 * Support waitpid. Don't assume -1 is acceptable to W* macros.
98 * strsave -> str_save (DG/UX name clash)
99 *
100 * Revision 5.5 1990/12/04 05:18:49 eggert
101 * Don't output a blank line after a signal diagnostic.
102 * Use -I for prompts and -q for diagnostics.
103 *
104 * Revision 5.4 1990/11/01 05:03:53 eggert
105 * Remove unneeded setid check. Add awrite(), fremember().
106 *
107 * Revision 5.3 1990/10/06 00:16:45 eggert
108 * Don't fread F if feof(F).
109 *
110 * Revision 5.2 1990/09/04 08:02:31 eggert
111 * Store fread()'s result in an fread_type object.
112 *
113 * Revision 5.1 1990/08/29 07:14:07 eggert
114 * Declare getpwuid() more carefully.
115 *
116 * Revision 5.0 1990/08/22 08:13:46 eggert
117 * Add setuid support. Permit multiple locks per user.
118 * Remove compile-time limits; use malloc instead.
119 * Switch to GMT. Permit dates past 1999/12/31.
120 * Add -V. Remove snooping. Ansify and Posixate.
121 * Tune. Some USG hosts define NSIG but not sys_siglist.
122 * Don't run /bin/sh if it's hopeless.
123 * Don't leave garbage behind if the output is an empty pipe.
124 * Clean up after SIGXCPU or SIGXFSZ. Print name of signal that caused cleanup.
125 *
126 * Revision 4.6 89/05/01 15:13:40 narten
127 * changed copyright header to reflect current distribution rules
128 *
129 * Revision 4.5 88/11/08 16:01:02 narten
130 * corrected use of varargs routines
131 *
132 * Revision 4.4 88/08/09 19:13:24 eggert
133 * Check for memory exhaustion.
134 * Permit signal handlers to yield either 'void' or 'int'; fix oldSIGINT botch.
135 * Use execv(), not system(); yield exit status like diff(1)'s.
136 *
137 * Revision 4.3 87/10/18 10:40:22 narten
138 * Updating version numbers. Changes relative to 1.1 actually
139 * relative to 4.1
140 *
141 * Revision 1.3 87/09/24 14:01:01 narten
142 * Sources now pass through lint (if you ignore printf/sprintf/fprintf
143 * warnings)
144 *
145 * Revision 1.2 87/03/27 14:22:43 jenkins
146 * Port to suns
147 *
148 * Revision 4.1 83/05/10 15:53:13 wft
149 * Added getcaller() and findlock().
150 * Changed catchints() to check SIGINT for SIG_IGN before setting up the signal
151 * (needed for background jobs in older shells). Added restoreints().
152 * Removed printing of full RCS path from logcommand().
153 *
154 * Revision 3.8 83/02/15 15:41:49 wft
155 * Added routine fastcopy() to copy remainder of a file in blocks.
156 *
157 * Revision 3.7 82/12/24 15:25:19 wft
158 * added catchints(), ignoreints() for catching and ingnoring interrupts;
159 * fixed catchsig().
160 *
161 * Revision 3.6 82/12/08 21:52:05 wft
162 * Using DATEFORM to format dates.
163 *
164 * Revision 3.5 82/12/04 18:20:49 wft
165 * Replaced SNOOPDIR with SNOOPFILE; changed addlock() to update
166 * lockedby-field.
167 *
168 * Revision 3.4 82/12/03 17:17:43 wft
169 * Added check to addlock() ensuring only one lock per person.
170 * Addlock also returns a pointer to the lock created. Deleted fancydate().
171 *
172 * Revision 3.3 82/11/27 12:24:37 wft
173 * moved rmsema(), trysema(), trydiraccess(), getfullRCSname() to rcsfnms.c.
174 * Introduced macro SNOOP so that snoop can be placed in directory other than
175 * TARGETDIR. Changed %02d to %.2d for compatibility reasons.
176 *
177 * Revision 3.2 82/10/18 21:15:11 wft
178 * added function getfullRCSname().
179 *
180 * Revision 3.1 82/10/13 16:17:37 wft
181 * Cleanup message is now suppressed in quiet mode.
182 */
183
184
185
186
187#include "rcsbase.h"
188
189libId(utilId, "$Id$")
189libId(utilId, "$Id: rcsutil.c,v 1.6 1997/02/22 15:47:43 peter Exp $")
190
191#if !has_memcmp
192 int
193memcmp(s1, s2, n)
194 void const *s1, *s2;
195 size_t n;
196{
197 register unsigned char const
198 *p1 = (unsigned char const*)s1,
199 *p2 = (unsigned char const*)s2;
200 register size_t i = n;
201 register int r = 0;
202 while (i-- && !(r = (*p1++ - *p2++)))
203 ;
204 return r;
205}
206#endif
207
208#if !has_memcpy
209 void *
210memcpy(s1, s2, n)
211 void *s1;
212 void const *s2;
213 size_t n;
214{
215 register char *p1 = (char*)s1;
216 register char const *p2 = (char const*)s2;
217 while (n--)
218 *p1++ = *p2++;
219 return s1;
220}
221#endif
222
223#if RCS_lint
224 malloc_type lintalloc;
225#endif
226
227/*
228 * list of blocks allocated with ftestalloc()
229 * These blocks can be freed by ffree when we're done with the current file.
230 * We could put the free block inside struct alloclist, rather than a pointer
231 * to the free block, but that would be less portable.
232 */
233struct alloclist {
234 malloc_type alloc;
235 struct alloclist *nextalloc;
236};
237static struct alloclist *alloced;
238
239
240 static malloc_type okalloc P((malloc_type));
241 static malloc_type
242okalloc(p)
243 malloc_type p;
244{
245 if (!p)
246 faterror("out of memory");
247 return p;
248}
249
250 malloc_type
251testalloc(size)
252 size_t size;
253/* Allocate a block, testing that the allocation succeeded. */
254{
255 return okalloc(malloc(size));
256}
257
258 malloc_type
259testrealloc(ptr, size)
260 malloc_type ptr;
261 size_t size;
262/* Reallocate a block, testing that the allocation succeeded. */
263{
264 return okalloc(realloc(ptr, size));
265}
266
267 malloc_type
268fremember(ptr)
269 malloc_type ptr;
270/* Remember PTR in 'alloced' so that it can be freed later. Yield PTR. */
271{
272 register struct alloclist *q = talloc(struct alloclist);
273 q->nextalloc = alloced;
274 alloced = q;
275 return q->alloc = ptr;
276}
277
278 malloc_type
279ftestalloc(size)
280 size_t size;
281/* Allocate a block, putting it in 'alloced' so it can be freed later. */
282{
283 return fremember(testalloc(size));
284}
285
286 void
287ffree()
288/* Free all blocks allocated with ftestalloc(). */
289{
290 register struct alloclist *p, *q;
291 for (p = alloced; p; p = q) {
292 q = p->nextalloc;
293 tfree(p->alloc);
294 tfree(p);
295 }
296 alloced = 0;
297}
298
299 void
300ffree1(f)
301 register char const *f;
302/* Free the block f, which was allocated by ftestalloc. */
303{
304 register struct alloclist *p, **a = &alloced;
305
306 while ((p = *a)->alloc != f)
307 a = &p->nextalloc;
308 *a = p->nextalloc;
309 tfree(p->alloc);
310 tfree(p);
311}
312
313 char *
314str_save(s)
315 char const *s;
316/* Save s in permanently allocated storage. */
317{
318 return strcpy(tnalloc(char, strlen(s)+1), s);
319}
320
321 char *
322fstr_save(s)
323 char const *s;
324/* Save s in storage that will be deallocated when we're done with this file. */
325{
326 return strcpy(ftnalloc(char, strlen(s)+1), s);
327}
328
329 char *
330cgetenv(name)
331 char const *name;
332/* Like getenv(), but yield a copy; getenv() can overwrite old results. */
333{
334 register char *p;
335
336 return (p=getenv(name)) ? str_save(p) : p;
337}
338
339 char const *
340getusername(suspicious)
341 int suspicious;
342/* Get the caller's login name. Trust only getwpuid if SUSPICIOUS. */
343{
344 static char *name;
345
346 if (!name) {
347 if (
348 /* Prefer getenv() unless suspicious; it's much faster. */
349# if getlogin_is_secure
350 (suspicious
351 || (
352 !(name = cgetenv("LOGNAME"))
353 && !(name = cgetenv("USER"))
354 ))
355 && !(name = getlogin())
356# else
357 suspicious
358 || (
359 !(name = cgetenv("LOGNAME"))
360 && !(name = cgetenv("USER"))
361 && !(name = getlogin())
362 )
363# endif
364 ) {
365#if has_getuid && has_getpwuid
366 struct passwd const *pw = getpwuid(ruid());
367 if (!pw)
368 faterror("no password entry for userid %lu",
369 (unsigned long)ruid()
370 );
371 name = pw->pw_name;
372#else
373#if has_setuid
374 faterror("setuid not supported");
375#else
376 faterror("Who are you? Please setenv LOGNAME.");
377#endif
378#endif
379 }
380 checksid(name);
381 }
382 return name;
383}
384
385
386
387
388#if has_signal
389
390/*
391 * Signal handling
392 *
393 * Standard C places too many restrictions on signal handlers.
394 * We obey as many of them as we can.
395 * Posix places fewer restrictions, and we are Posix-compatible here.
396 */
397
398static sig_atomic_t volatile heldsignal, holdlevel;
399#ifdef SA_SIGINFO
400 static int unsupported_SA_SIGINFO;
401 static siginfo_t bufsiginfo;
402 static siginfo_t *volatile heldsiginfo;
403#endif
404
405
406#if has_NFS && has_mmap && large_memory && mmap_signal
407 static char const *accessName;
408
409 void
410 readAccessFilenameBuffer(filename, p)
411 char const *filename;
412 unsigned char const *p;
413 {
414 unsigned char volatile t;
415 accessName = filename;
416 t = *p;
417 accessName = 0;
418 }
419#else
420# define accessName ((char const *) 0)
421#endif
422
423
424#if !has_psignal
425
426# define psignal my_psignal
427 static void my_psignal P((int,char const*));
428 static void
429my_psignal(sig, s)
430 int sig;
431 char const *s;
432{
433 char const *sname = "Unknown signal";
434# if has_sys_siglist && defined(NSIG)
435 if ((unsigned)sig < NSIG)
436 sname = sys_siglist[sig];
437# else
438 switch (sig) {
439# ifdef SIGHUP
440 case SIGHUP: sname = "Hangup"; break;
441# endif
442# ifdef SIGINT
443 case SIGINT: sname = "Interrupt"; break;
444# endif
445# ifdef SIGPIPE
446 case SIGPIPE: sname = "Broken pipe"; break;
447# endif
448# ifdef SIGQUIT
449 case SIGQUIT: sname = "Quit"; break;
450# endif
451# ifdef SIGTERM
452 case SIGTERM: sname = "Terminated"; break;
453# endif
454# ifdef SIGXCPU
455 case SIGXCPU: sname = "Cputime limit exceeded"; break;
456# endif
457# ifdef SIGXFSZ
458 case SIGXFSZ: sname = "Filesize limit exceeded"; break;
459# endif
460# if has_mmap && large_memory
461# if defined(SIGBUS) && mmap_signal==SIGBUS
462 case SIGBUS: sname = "Bus error"; break;
463# endif
464# if defined(SIGSEGV) && mmap_signal==SIGSEGV
465 case SIGSEGV: sname = "Segmentation fault"; break;
466# endif
467# endif
468 }
469# endif
470
471 /* Avoid calling sprintf etc., in case they're not reentrant. */
472 {
473 char const *p;
474 char buf[BUFSIZ], *b = buf;
475 for (p = s; *p; *b++ = *p++)
476 continue;
477 *b++ = ':';
478 *b++ = ' ';
479 for (p = sname; *p; *b++ = *p++)
480 continue;
481 *b++ = '\n';
482 VOID write(STDERR_FILENO, buf, b - buf);
483 }
484}
485#endif
486
487static signal_type catchsig P((int));
488#ifdef SA_SIGINFO
489 static signal_type catchsigaction P((int,siginfo_t*,void*));
490#endif
491
492 static signal_type
493catchsig(s)
494 int s;
495#ifdef SA_SIGINFO
496{
497 catchsigaction(s, (siginfo_t *)0, (void *)0);
498}
499 static signal_type
500catchsigaction(s, i, c)
501 int s;
502 siginfo_t *i;
503 void *c;
504#endif
505{
506# if sig_zaps_handler
507 /* If a signal arrives before we reset the handler, we lose. */
508 VOID signal(s, SIG_IGN);
509# endif
510
511# ifdef SA_SIGINFO
512 if (!unsupported_SA_SIGINFO)
513 i = 0;
514# endif
515
516 if (holdlevel) {
517 heldsignal = s;
518# ifdef SA_SIGINFO
519 if (i) {
520 bufsiginfo = *i;
521 heldsiginfo = &bufsiginfo;
522 }
523# endif
524 return;
525 }
526
527 ignoreints();
528 setrid();
529 if (!quietflag) {
530 /* Avoid calling sprintf etc., in case they're not reentrant. */
531 char const *p;
532 char buf[BUFSIZ], *b = buf;
533
534 if ( ! (
535# if has_mmap && large_memory && mmap_signal
536 /* Check whether this signal was planned. */
537 s == mmap_signal && accessName
538# else
539 0
540# endif
541 )) {
542 char const *nRCS = "\nRCS";
543# if defined(SA_SIGINFO) && has_si_errno && has_mmap && large_memory && mmap_signal
544 if (s == mmap_signal && i && i->si_errno) {
545 errno = i->si_errno;
546 perror(nRCS++);
547 }
548# endif
549# if defined(SA_SIGINFO) && has_psiginfo
550 if (i)
551 psiginfo(i, nRCS);
552 else
553 psignal(s, nRCS);
554# else
555 psignal(s, nRCS);
556# endif
557 }
558
559 for (p = "RCS: "; *p; *b++ = *p++)
560 continue;
561# if has_mmap && large_memory && mmap_signal
562 if (s == mmap_signal) {
563 p = accessName;
564 if (!p)
565 p = "Was a file changed by some other process? ";
566 else {
567 char const *p1;
568 for (p1 = p; *p1; p1++)
569 continue;
570 VOID write(STDERR_FILENO, buf, b - buf);
571 VOID write(STDERR_FILENO, p, p1 - p);
572 b = buf;
573 p = ": Permission denied. ";
574 }
575 while (*p)
576 *b++ = *p++;
577 }
578# endif
579 for (p = "Cleaning up.\n"; *p; *b++ = *p++)
580 continue;
581 VOID write(STDERR_FILENO, buf, b - buf);
582 }
583 exiterr();
584}
585
586 void
587ignoreints()
588{
589 ++holdlevel;
590}
591
592 void
593restoreints()
594{
595 if (!--holdlevel && heldsignal)
596# ifdef SA_SIGINFO
597 VOID catchsigaction(heldsignal, heldsiginfo, (void *)0);
598# else
599 VOID catchsig(heldsignal);
600# endif
601}
602
603
604static void setup_catchsig P((int const*,int));
605
606#if has_sigaction
607
608 static void check_sig P((int));
609 static void
610 check_sig(r)
611 int r;
612 {
613 if (r != 0)
614 efaterror("signal handling");
615 }
616
617 static void
618 setup_catchsig(sig, sigs)
619 int const *sig;
620 int sigs;
621 {
622 register int i, j;
623 struct sigaction act;
624
625 for (i=sigs; 0<=--i; ) {
626 check_sig(sigaction(sig[i], (struct sigaction*)0, &act));
627 if (act.sa_handler != SIG_IGN) {
628 act.sa_handler = catchsig;
629# ifdef SA_SIGINFO
630 if (!unsupported_SA_SIGINFO) {
631# if has_sa_sigaction
632 act.sa_sigaction = catchsigaction;
633# else
634 act.sa_handler = catchsigaction;
635# endif
636 act.sa_flags |= SA_SIGINFO;
637 }
638# endif
639 for (j=sigs; 0<=--j; )
640 check_sig(sigaddset(&act.sa_mask, sig[j]));
641 if (sigaction(sig[i], &act, (struct sigaction*)0) != 0) {
642# if defined(SA_SIGINFO) && defined(ENOTSUP)
643 if (errno == ENOTSUP && !unsupported_SA_SIGINFO) {
644 /* Turn off use of SA_SIGINFO and try again. */
645 unsupported_SA_SIGINFO = 1;
646 i++;
647 continue;
648 }
649# endif
650 check_sig(-1);
651 }
652 }
653 }
654 }
655
656#else
657#if has_sigblock
658
659 static void
660 setup_catchsig(sig, sigs)
661 int const *sig;
662 int sigs;
663 {
664 register int i;
665 int mask;
666
667 mask = 0;
668 for (i=sigs; 0<=--i; )
669 mask |= sigmask(sig[i]);
670 mask = sigblock(mask);
671 for (i=sigs; 0<=--i; )
672 if (
673 signal(sig[i], catchsig) == SIG_IGN &&
674 signal(sig[i], SIG_IGN) != catchsig
675 )
676 faterror("signal catcher failure");
677 VOID sigsetmask(mask);
678 }
679
680#else
681
682 static void
683 setup_catchsig(sig, sigs)
684 int const *sig;
685 int sigs;
686 {
687 register i;
688
689 for (i=sigs; 0<=--i; )
690 if (
691 signal(sig[i], SIG_IGN) != SIG_IGN &&
692 signal(sig[i], catchsig) != SIG_IGN
693 )
694 faterror("signal catcher failure");
695 }
696
697#endif
698#endif
699
700
701static int const regsigs[] = {
702# ifdef SIGHUP
703 SIGHUP,
704# endif
705# ifdef SIGINT
706 SIGINT,
707# endif
708# ifdef SIGPIPE
709 SIGPIPE,
710# endif
711# ifdef SIGQUIT
712 SIGQUIT,
713# endif
714# ifdef SIGTERM
715 SIGTERM,
716# endif
717# ifdef SIGXCPU
718 SIGXCPU,
719# endif
720# ifdef SIGXFSZ
721 SIGXFSZ,
722# endif
723};
724
725 void
726catchints()
727{
728 static int catching_ints;
729 if (!catching_ints) {
730 catching_ints = true;
731 setup_catchsig(regsigs, (int) (sizeof(regsigs)/sizeof(*regsigs)));
732 }
733}
734
735#if has_mmap && large_memory && mmap_signal
736
737 /*
738 * If you mmap an NFS file, and someone on another client removes the last
739 * link to that file, and you later reference an uncached part of that file,
740 * you'll get a SIGBUS or SIGSEGV (depending on the operating system).
741 * Catch the signal and report the problem to the user.
742 * Unfortunately, there's no portable way to differentiate between this
743 * problem and actual bugs in the program.
744 * This NFS problem is rare, thank goodness.
745 *
746 * This can also occur if someone truncates the file, even without NFS.
747 */
748
749 static int const mmapsigs[] = { mmap_signal };
750
751 void
752 catchmmapints()
753 {
754 static int catching_mmap_ints;
755 if (!catching_mmap_ints) {
756 catching_mmap_ints = true;
757 setup_catchsig(mmapsigs, (int)(sizeof(mmapsigs)/sizeof(*mmapsigs)));
758 }
759 }
760#endif
761
762#endif /* has_signal */
763
764
765 void
766fastcopy(inf,outf)
767 register RILE *inf;
768 FILE *outf;
769/* Function: copies the remainder of file inf to outf.
770 */
771{
772#if large_memory
773# if maps_memory
774 awrite((char const*)inf->ptr, (size_t)(inf->lim - inf->ptr), outf);
775 inf->ptr = inf->lim;
776# else
777 for (;;) {
778 awrite((char const*)inf->ptr, (size_t)(inf->readlim - inf->ptr), outf);
779 inf->ptr = inf->readlim;
780 if (inf->ptr == inf->lim)
781 break;
782 VOID Igetmore(inf);
783 }
784# endif
785#else
786 char buf[BUFSIZ*8];
787 register fread_type rcount;
788
789 /*now read the rest of the file in blocks*/
790 while (!feof(inf)) {
791 if (!(rcount = Fread(buf,sizeof(*buf),sizeof(buf),inf))) {
792 testIerror(inf);
793 return;
794 }
795 awrite(buf, (size_t)rcount, outf);
796 }
797#endif
798}
799
800#ifndef SSIZE_MAX
801 /* This does not work in #ifs, but it's good enough for us. */
802 /* Underestimating SSIZE_MAX may slow us down, but it won't break us. */
803# define SSIZE_MAX ((unsigned)-1 >> 1)
804#endif
805
806 void
807awrite(buf, chars, f)
808 char const *buf;
809 size_t chars;
810 FILE *f;
811{
812 /* Posix 1003.1-1990 ssize_t hack */
813 while (SSIZE_MAX < chars) {
814 if (Fwrite(buf, sizeof(*buf), SSIZE_MAX, f) != SSIZE_MAX)
815 Oerror();
816 buf += SSIZE_MAX;
817 chars -= SSIZE_MAX;
818 }
819
820 if (Fwrite(buf, sizeof(*buf), chars, f) != chars)
821 Oerror();
822}
823
824/* dup a file descriptor; the result must not be stdin, stdout, or stderr. */
825 static int dupSafer P((int));
826 static int
827dupSafer(fd)
828 int fd;
829{
830# ifdef F_DUPFD
831 return fcntl(fd, F_DUPFD, STDERR_FILENO + 1);
832# else
833 int e, f, i, used = 0;
834 while (STDIN_FILENO <= (f = dup(fd)) && f <= STDERR_FILENO)
835 used |= 1<<f;
836 e = errno;
837 for (i = STDIN_FILENO; i <= STDERR_FILENO; i++)
838 if (used & (1<<i))
839 VOID close(i);
840 errno = e;
841 return f;
842# endif
843}
844
845/* Renumber a file descriptor so that it's not stdin, stdout, or stderr. */
846 int
847fdSafer(fd)
848 int fd;
849{
850 if (STDIN_FILENO <= fd && fd <= STDERR_FILENO) {
851 int f = dupSafer(fd);
852 int e = errno;
853 VOID close(fd);
854 errno = e;
855 fd = f;
856 }
857 return fd;
858}
859
860/* Like fopen, except the result is never stdin, stdout, or stderr. */
861 FILE *
862fopenSafer(filename, type)
863 char const *filename;
864 char const *type;
865{
866 FILE *stream = fopen(filename, type);
867 if (stream) {
868 int fd = fileno(stream);
869 if (STDIN_FILENO <= fd && fd <= STDERR_FILENO) {
870 int f = dupSafer(fd);
871 if (f < 0) {
872 int e = errno;
873 VOID fclose(stream);
874 errno = e;
875 return 0;
876 }
877 if (fclose(stream) != 0) {
878 int e = errno;
879 VOID close(f);
880 errno = e;
881 return 0;
882 }
883 stream = fdopen(f, type);
884 }
885 }
886 return stream;
887}
888
889
890#ifdef F_DUPFD
891# undef dup
892# define dup(fd) fcntl(fd, F_DUPFD, 0)
893#endif
894
895
896#if has_fork || has_spawn
897
898 static int movefd P((int,int));
899 static int
900movefd(old, new)
901 int old, new;
902{
903 if (old < 0 || old == new)
904 return old;
905# ifdef F_DUPFD
906 new = fcntl(old, F_DUPFD, new);
907# else
908 new = dup2(old, new);
909# endif
910 return close(old)==0 ? new : -1;
911}
912
913 static int fdreopen P((int,char const*,int));
914 static int
915fdreopen(fd, file, flags)
916 int fd;
917 char const *file;
918 int flags;
919{
920 int newfd;
921 VOID close(fd);
922 newfd =
923#if !open_can_creat
924 flags&O_CREAT ? creat(file, S_IRUSR|S_IWUSR) :
925#endif
926 open(file, flags, S_IRUSR|S_IWUSR);
927 return movefd(newfd, fd);
928}
929
930#if has_spawn
931 static void redirect P((int,int));
932 static void
933redirect(old, new)
934 int old, new;
935/*
936* Move file descriptor OLD to NEW.
937* If OLD is -1, do nothing.
938* If OLD is -2, just close NEW.
939*/
940{
941 if ((old != -1 && close(new) != 0) || (0 <= old && movefd(old,new) < 0))
942 efaterror("spawn I/O redirection");
943}
944#endif
945
946
947#else /* !has_fork && !has_spawn */
948
949 static void bufargcat P((struct buf*,int,char const*));
950 static void
951bufargcat(b, c, s)
952 register struct buf *b;
953 int c;
954 register char const *s;
955/* Append to B a copy of C, plus a quoted copy of S. */
956{
957 register char *p;
958 register char const *t;
959 size_t bl, sl;
960
961 for (t=s, sl=0; *t; )
962 sl += 3*(*t++=='\'') + 1;
963 bl = strlen(b->string);
964 bufrealloc(b, bl + sl + 4);
965 p = b->string + bl;
966 *p++ = c;
967 *p++ = '\'';
968 while (*s) {
969 if (*s == '\'') {
970 *p++ = '\'';
971 *p++ = '\\';
972 *p++ = '\'';
973 }
974 *p++ = *s++;
975 }
976 *p++ = '\'';
977 *p = 0;
978}
979
980#endif
981
982#if !has_spawn && has_fork
983/*
984* Output the string S to stderr, without touching any I/O buffers.
985* This is useful if you are a child process, whose buffers are usually wrong.
986* Exit immediately if the write does not completely succeed.
987*/
988static void write_stderr P((char const *));
989 static void
990write_stderr(s)
991 char const *s;
992{
993 size_t slen = strlen(s);
994 if (write(STDERR_FILENO, s, slen) != slen)
995 _exit(EXIT_TROUBLE);
996}
997#endif
998
999/*
1000* Run a command.
1001* infd, if not -1, is the input file descriptor.
1002* outname, if nonzero, is the name of the output file.
1003* args[1..] form the command to be run; args[0] might be modified.
1004*/
1005 int
1006runv(infd, outname, args)
1007 int infd;
1008 char const *outname, **args;
1009{
1010 int wstatus;
1011
1012#if bad_wait_if_SIGCHLD_ignored
1013 static int fixed_SIGCHLD;
1014 if (!fixed_SIGCHLD) {
1015 fixed_SIGCHLD = true;
1016# ifndef SIGCHLD
1017# define SIGCHLD SIGCLD
1018# endif
1019 VOID signal(SIGCHLD, SIG_DFL);
1020 }
1021#endif
1022
1023 oflush();
1024 eflush();
1025 {
1026#if has_spawn
1027 int in, out;
1028 char const *file;
1029
1030 in = -1;
1031 if (infd != -1 && infd != STDIN_FILENO) {
1032 if ((in = dup(STDIN_FILENO)) < 0) {
1033 if (errno != EBADF)
1034 efaterror("spawn input setup");
1035 in = -2;
1036 } else {
1037# ifdef F_DUPFD
1038 if (close(STDIN_FILENO) != 0)
1039 efaterror("spawn input close");
1040# endif
1041 }
1042 if (
1043# ifdef F_DUPFD
1044 fcntl(infd, F_DUPFD, STDIN_FILENO) != STDIN_FILENO
1045# else
1046 dup2(infd, STDIN_FILENO) != STDIN_FILENO
1047# endif
1048 )
1049 efaterror("spawn input redirection");
1050 }
1051
1052 out = -1;
1053 if (outname) {
1054 if ((out = dup(STDOUT_FILENO)) < 0) {
1055 if (errno != EBADF)
1056 efaterror("spawn output setup");
1057 out = -2;
1058 }
1059 if (fdreopen(
1060 STDOUT_FILENO, outname,
1061 O_CREAT | O_TRUNC | O_WRONLY
1062 ) < 0)
1063 efaterror(outname);
1064 }
1065
1066 wstatus = spawn_RCS(0, args[1], (char**)(args + 1));
1067# ifdef RCS_SHELL
1068 if (wstatus == -1 && errno == ENOEXEC) {
1069 args[0] = RCS_SHELL;
1070 wstatus = spawnv(0, args[0], (char**)args);
1071 }
1072# endif
1073 redirect(in, STDIN_FILENO);
1074 redirect(out, STDOUT_FILENO);
1075#else
1076#if has_fork
1077 pid_t pid;
1078 if (!(pid = vfork())) {
1079 char const *notfound;
1080 if (infd != -1 && infd != STDIN_FILENO && (
1081# ifdef F_DUPFD
1082 (VOID close(STDIN_FILENO),
1083 fcntl(infd, F_DUPFD, STDIN_FILENO) != STDIN_FILENO)
1084# else
1085 dup2(infd, STDIN_FILENO) != STDIN_FILENO
1086# endif
1087 )) {
1088 /* Avoid perror since it may misuse buffers. */
1089 write_stderr(args[1]);
1090 write_stderr(": I/O redirection failed\n");
1091 _exit(EXIT_TROUBLE);
1092 }
1093
1094 if (outname)
1095 if (fdreopen(
1096 STDOUT_FILENO, outname,
1097 O_CREAT | O_TRUNC | O_WRONLY
1098 ) < 0) {
1099 /* Avoid perror since it may misuse buffers. */
1100 write_stderr(args[1]);
1101 write_stderr(": ");
1102 write_stderr(outname);
1103 write_stderr(": cannot create\n");
1104 _exit(EXIT_TROUBLE);
1105 }
1106 VOID exec_RCS(args[1], (char**)(args + 1));
1107 notfound = args[1];
1108# ifdef RCS_SHELL
1109 if (errno == ENOEXEC) {
1110 args[0] = notfound = RCS_SHELL;
1111 VOID execv(args[0], (char**)args);
1112 }
1113# endif
1114
1115 /* Avoid perror since it may misuse buffers. */
1116 write_stderr(notfound);
1117 write_stderr(": not found\n");
1118 _exit(EXIT_TROUBLE);
1119 }
1120 if (pid < 0)
1121 efaterror("fork");
1122# if has_waitpid
1123 if (waitpid(pid, &wstatus, 0) < 0)
1124 efaterror("waitpid");
1125# else
1126 {
1127 pid_t w;
1128 do {
1129 if ((w = wait(&wstatus)) < 0)
1130 efaterror("wait");
1131 } while (w != pid);
1132 }
1133# endif
1134#else
1135 static struct buf b;
1136 char const *p;
1137
1138 /* Use system(). On many hosts system() discards signals. Yuck! */
1139 p = args + 1;
1140 bufscpy(&b, *p);
1141 while (*++p)
1142 bufargcat(&b, ' ', *p);
1143 if (infd != -1 && infd != STDIN_FILENO) {
1144 char redirection[32];
1145 VOID sprintf(redirection, "<&%d", infd);
1146 bufscat(&b, redirection);
1147 }
1148 if (outname)
1149 bufargcat(&b, '>', outname);
1150 wstatus = system(b.string);
1151#endif
1152#endif
1153 }
1154 if (!WIFEXITED(wstatus)) {
1155 if (WIFSIGNALED(wstatus)) {
1156 psignal(WTERMSIG(wstatus), args[1]);
1157 fatcleanup(1);
1158 }
1159 faterror("%s failed for unknown reason", args[1]);
1160 }
1161 return WEXITSTATUS(wstatus);
1162}
1163
1164#define CARGSMAX 20
1165/*
1166* Run a command.
1167* infd, if not -1, is the input file descriptor.
1168* outname, if nonzero, is the name of the output file.
1169* The remaining arguments specify the command and its arguments.
1170*/
1171 int
1172#if has_prototypes
1173run(int infd, char const *outname, ...)
1174#else
1175 /*VARARGS2*/
1176run(infd, outname, va_alist)
1177 int infd;
1178 char const *outname;
1179 va_dcl
1180#endif
1181{
1182 va_list ap;
1183 char const *rgargs[CARGSMAX];
1184 register int i;
1185 vararg_start(ap, outname);
1186 for (i = 1; (rgargs[i++] = va_arg(ap, char const*)); )
1187 if (CARGSMAX <= i)
1188 faterror("too many command arguments");
1189 va_end(ap);
1190 return runv(infd, outname, rgargs);
1191}
1192
1193
1194int RCSversion;
1195
1196 void
1197setRCSversion(str)
1198 char const *str;
1199{
1200 static int oldversion;
1201
1202 register char const *s = str + 2;
1203
1204 if (*s) {
1205 int v = VERSION_DEFAULT;
1206
1207 if (oldversion)
1208 redefined('V');
1209 oldversion = true;
1210 v = 0;
1211 while (isdigit(*s))
1212 v = 10*v + *s++ - '0';
1213 if (*s)
1214 error("%s isn't a number", str);
1215 else if (v < VERSION_min || VERSION_max < v)
1216 error("%s out of range %d..%d",
1217 str, VERSION_min, VERSION_max
1218 );
1219
1220 RCSversion = VERSION(v);
1221 } else {
1222 printf("RCS version %s\n", RCS_version_string);
1223 exit(0);
1224 }
1225}
1226
1227 int
1228getRCSINIT(argc, argv, newargv)
1229 int argc;
1230 char **argv, ***newargv;
1231{
1232 register char *p, *q, **pp;
190
191#if !has_memcmp
192 int
193memcmp(s1, s2, n)
194 void const *s1, *s2;
195 size_t n;
196{
197 register unsigned char const
198 *p1 = (unsigned char const*)s1,
199 *p2 = (unsigned char const*)s2;
200 register size_t i = n;
201 register int r = 0;
202 while (i-- && !(r = (*p1++ - *p2++)))
203 ;
204 return r;
205}
206#endif
207
208#if !has_memcpy
209 void *
210memcpy(s1, s2, n)
211 void *s1;
212 void const *s2;
213 size_t n;
214{
215 register char *p1 = (char*)s1;
216 register char const *p2 = (char const*)s2;
217 while (n--)
218 *p1++ = *p2++;
219 return s1;
220}
221#endif
222
223#if RCS_lint
224 malloc_type lintalloc;
225#endif
226
227/*
228 * list of blocks allocated with ftestalloc()
229 * These blocks can be freed by ffree when we're done with the current file.
230 * We could put the free block inside struct alloclist, rather than a pointer
231 * to the free block, but that would be less portable.
232 */
233struct alloclist {
234 malloc_type alloc;
235 struct alloclist *nextalloc;
236};
237static struct alloclist *alloced;
238
239
240 static malloc_type okalloc P((malloc_type));
241 static malloc_type
242okalloc(p)
243 malloc_type p;
244{
245 if (!p)
246 faterror("out of memory");
247 return p;
248}
249
250 malloc_type
251testalloc(size)
252 size_t size;
253/* Allocate a block, testing that the allocation succeeded. */
254{
255 return okalloc(malloc(size));
256}
257
258 malloc_type
259testrealloc(ptr, size)
260 malloc_type ptr;
261 size_t size;
262/* Reallocate a block, testing that the allocation succeeded. */
263{
264 return okalloc(realloc(ptr, size));
265}
266
267 malloc_type
268fremember(ptr)
269 malloc_type ptr;
270/* Remember PTR in 'alloced' so that it can be freed later. Yield PTR. */
271{
272 register struct alloclist *q = talloc(struct alloclist);
273 q->nextalloc = alloced;
274 alloced = q;
275 return q->alloc = ptr;
276}
277
278 malloc_type
279ftestalloc(size)
280 size_t size;
281/* Allocate a block, putting it in 'alloced' so it can be freed later. */
282{
283 return fremember(testalloc(size));
284}
285
286 void
287ffree()
288/* Free all blocks allocated with ftestalloc(). */
289{
290 register struct alloclist *p, *q;
291 for (p = alloced; p; p = q) {
292 q = p->nextalloc;
293 tfree(p->alloc);
294 tfree(p);
295 }
296 alloced = 0;
297}
298
299 void
300ffree1(f)
301 register char const *f;
302/* Free the block f, which was allocated by ftestalloc. */
303{
304 register struct alloclist *p, **a = &alloced;
305
306 while ((p = *a)->alloc != f)
307 a = &p->nextalloc;
308 *a = p->nextalloc;
309 tfree(p->alloc);
310 tfree(p);
311}
312
313 char *
314str_save(s)
315 char const *s;
316/* Save s in permanently allocated storage. */
317{
318 return strcpy(tnalloc(char, strlen(s)+1), s);
319}
320
321 char *
322fstr_save(s)
323 char const *s;
324/* Save s in storage that will be deallocated when we're done with this file. */
325{
326 return strcpy(ftnalloc(char, strlen(s)+1), s);
327}
328
329 char *
330cgetenv(name)
331 char const *name;
332/* Like getenv(), but yield a copy; getenv() can overwrite old results. */
333{
334 register char *p;
335
336 return (p=getenv(name)) ? str_save(p) : p;
337}
338
339 char const *
340getusername(suspicious)
341 int suspicious;
342/* Get the caller's login name. Trust only getwpuid if SUSPICIOUS. */
343{
344 static char *name;
345
346 if (!name) {
347 if (
348 /* Prefer getenv() unless suspicious; it's much faster. */
349# if getlogin_is_secure
350 (suspicious
351 || (
352 !(name = cgetenv("LOGNAME"))
353 && !(name = cgetenv("USER"))
354 ))
355 && !(name = getlogin())
356# else
357 suspicious
358 || (
359 !(name = cgetenv("LOGNAME"))
360 && !(name = cgetenv("USER"))
361 && !(name = getlogin())
362 )
363# endif
364 ) {
365#if has_getuid && has_getpwuid
366 struct passwd const *pw = getpwuid(ruid());
367 if (!pw)
368 faterror("no password entry for userid %lu",
369 (unsigned long)ruid()
370 );
371 name = pw->pw_name;
372#else
373#if has_setuid
374 faterror("setuid not supported");
375#else
376 faterror("Who are you? Please setenv LOGNAME.");
377#endif
378#endif
379 }
380 checksid(name);
381 }
382 return name;
383}
384
385
386
387
388#if has_signal
389
390/*
391 * Signal handling
392 *
393 * Standard C places too many restrictions on signal handlers.
394 * We obey as many of them as we can.
395 * Posix places fewer restrictions, and we are Posix-compatible here.
396 */
397
398static sig_atomic_t volatile heldsignal, holdlevel;
399#ifdef SA_SIGINFO
400 static int unsupported_SA_SIGINFO;
401 static siginfo_t bufsiginfo;
402 static siginfo_t *volatile heldsiginfo;
403#endif
404
405
406#if has_NFS && has_mmap && large_memory && mmap_signal
407 static char const *accessName;
408
409 void
410 readAccessFilenameBuffer(filename, p)
411 char const *filename;
412 unsigned char const *p;
413 {
414 unsigned char volatile t;
415 accessName = filename;
416 t = *p;
417 accessName = 0;
418 }
419#else
420# define accessName ((char const *) 0)
421#endif
422
423
424#if !has_psignal
425
426# define psignal my_psignal
427 static void my_psignal P((int,char const*));
428 static void
429my_psignal(sig, s)
430 int sig;
431 char const *s;
432{
433 char const *sname = "Unknown signal";
434# if has_sys_siglist && defined(NSIG)
435 if ((unsigned)sig < NSIG)
436 sname = sys_siglist[sig];
437# else
438 switch (sig) {
439# ifdef SIGHUP
440 case SIGHUP: sname = "Hangup"; break;
441# endif
442# ifdef SIGINT
443 case SIGINT: sname = "Interrupt"; break;
444# endif
445# ifdef SIGPIPE
446 case SIGPIPE: sname = "Broken pipe"; break;
447# endif
448# ifdef SIGQUIT
449 case SIGQUIT: sname = "Quit"; break;
450# endif
451# ifdef SIGTERM
452 case SIGTERM: sname = "Terminated"; break;
453# endif
454# ifdef SIGXCPU
455 case SIGXCPU: sname = "Cputime limit exceeded"; break;
456# endif
457# ifdef SIGXFSZ
458 case SIGXFSZ: sname = "Filesize limit exceeded"; break;
459# endif
460# if has_mmap && large_memory
461# if defined(SIGBUS) && mmap_signal==SIGBUS
462 case SIGBUS: sname = "Bus error"; break;
463# endif
464# if defined(SIGSEGV) && mmap_signal==SIGSEGV
465 case SIGSEGV: sname = "Segmentation fault"; break;
466# endif
467# endif
468 }
469# endif
470
471 /* Avoid calling sprintf etc., in case they're not reentrant. */
472 {
473 char const *p;
474 char buf[BUFSIZ], *b = buf;
475 for (p = s; *p; *b++ = *p++)
476 continue;
477 *b++ = ':';
478 *b++ = ' ';
479 for (p = sname; *p; *b++ = *p++)
480 continue;
481 *b++ = '\n';
482 VOID write(STDERR_FILENO, buf, b - buf);
483 }
484}
485#endif
486
487static signal_type catchsig P((int));
488#ifdef SA_SIGINFO
489 static signal_type catchsigaction P((int,siginfo_t*,void*));
490#endif
491
492 static signal_type
493catchsig(s)
494 int s;
495#ifdef SA_SIGINFO
496{
497 catchsigaction(s, (siginfo_t *)0, (void *)0);
498}
499 static signal_type
500catchsigaction(s, i, c)
501 int s;
502 siginfo_t *i;
503 void *c;
504#endif
505{
506# if sig_zaps_handler
507 /* If a signal arrives before we reset the handler, we lose. */
508 VOID signal(s, SIG_IGN);
509# endif
510
511# ifdef SA_SIGINFO
512 if (!unsupported_SA_SIGINFO)
513 i = 0;
514# endif
515
516 if (holdlevel) {
517 heldsignal = s;
518# ifdef SA_SIGINFO
519 if (i) {
520 bufsiginfo = *i;
521 heldsiginfo = &bufsiginfo;
522 }
523# endif
524 return;
525 }
526
527 ignoreints();
528 setrid();
529 if (!quietflag) {
530 /* Avoid calling sprintf etc., in case they're not reentrant. */
531 char const *p;
532 char buf[BUFSIZ], *b = buf;
533
534 if ( ! (
535# if has_mmap && large_memory && mmap_signal
536 /* Check whether this signal was planned. */
537 s == mmap_signal && accessName
538# else
539 0
540# endif
541 )) {
542 char const *nRCS = "\nRCS";
543# if defined(SA_SIGINFO) && has_si_errno && has_mmap && large_memory && mmap_signal
544 if (s == mmap_signal && i && i->si_errno) {
545 errno = i->si_errno;
546 perror(nRCS++);
547 }
548# endif
549# if defined(SA_SIGINFO) && has_psiginfo
550 if (i)
551 psiginfo(i, nRCS);
552 else
553 psignal(s, nRCS);
554# else
555 psignal(s, nRCS);
556# endif
557 }
558
559 for (p = "RCS: "; *p; *b++ = *p++)
560 continue;
561# if has_mmap && large_memory && mmap_signal
562 if (s == mmap_signal) {
563 p = accessName;
564 if (!p)
565 p = "Was a file changed by some other process? ";
566 else {
567 char const *p1;
568 for (p1 = p; *p1; p1++)
569 continue;
570 VOID write(STDERR_FILENO, buf, b - buf);
571 VOID write(STDERR_FILENO, p, p1 - p);
572 b = buf;
573 p = ": Permission denied. ";
574 }
575 while (*p)
576 *b++ = *p++;
577 }
578# endif
579 for (p = "Cleaning up.\n"; *p; *b++ = *p++)
580 continue;
581 VOID write(STDERR_FILENO, buf, b - buf);
582 }
583 exiterr();
584}
585
586 void
587ignoreints()
588{
589 ++holdlevel;
590}
591
592 void
593restoreints()
594{
595 if (!--holdlevel && heldsignal)
596# ifdef SA_SIGINFO
597 VOID catchsigaction(heldsignal, heldsiginfo, (void *)0);
598# else
599 VOID catchsig(heldsignal);
600# endif
601}
602
603
604static void setup_catchsig P((int const*,int));
605
606#if has_sigaction
607
608 static void check_sig P((int));
609 static void
610 check_sig(r)
611 int r;
612 {
613 if (r != 0)
614 efaterror("signal handling");
615 }
616
617 static void
618 setup_catchsig(sig, sigs)
619 int const *sig;
620 int sigs;
621 {
622 register int i, j;
623 struct sigaction act;
624
625 for (i=sigs; 0<=--i; ) {
626 check_sig(sigaction(sig[i], (struct sigaction*)0, &act));
627 if (act.sa_handler != SIG_IGN) {
628 act.sa_handler = catchsig;
629# ifdef SA_SIGINFO
630 if (!unsupported_SA_SIGINFO) {
631# if has_sa_sigaction
632 act.sa_sigaction = catchsigaction;
633# else
634 act.sa_handler = catchsigaction;
635# endif
636 act.sa_flags |= SA_SIGINFO;
637 }
638# endif
639 for (j=sigs; 0<=--j; )
640 check_sig(sigaddset(&act.sa_mask, sig[j]));
641 if (sigaction(sig[i], &act, (struct sigaction*)0) != 0) {
642# if defined(SA_SIGINFO) && defined(ENOTSUP)
643 if (errno == ENOTSUP && !unsupported_SA_SIGINFO) {
644 /* Turn off use of SA_SIGINFO and try again. */
645 unsupported_SA_SIGINFO = 1;
646 i++;
647 continue;
648 }
649# endif
650 check_sig(-1);
651 }
652 }
653 }
654 }
655
656#else
657#if has_sigblock
658
659 static void
660 setup_catchsig(sig, sigs)
661 int const *sig;
662 int sigs;
663 {
664 register int i;
665 int mask;
666
667 mask = 0;
668 for (i=sigs; 0<=--i; )
669 mask |= sigmask(sig[i]);
670 mask = sigblock(mask);
671 for (i=sigs; 0<=--i; )
672 if (
673 signal(sig[i], catchsig) == SIG_IGN &&
674 signal(sig[i], SIG_IGN) != catchsig
675 )
676 faterror("signal catcher failure");
677 VOID sigsetmask(mask);
678 }
679
680#else
681
682 static void
683 setup_catchsig(sig, sigs)
684 int const *sig;
685 int sigs;
686 {
687 register i;
688
689 for (i=sigs; 0<=--i; )
690 if (
691 signal(sig[i], SIG_IGN) != SIG_IGN &&
692 signal(sig[i], catchsig) != SIG_IGN
693 )
694 faterror("signal catcher failure");
695 }
696
697#endif
698#endif
699
700
701static int const regsigs[] = {
702# ifdef SIGHUP
703 SIGHUP,
704# endif
705# ifdef SIGINT
706 SIGINT,
707# endif
708# ifdef SIGPIPE
709 SIGPIPE,
710# endif
711# ifdef SIGQUIT
712 SIGQUIT,
713# endif
714# ifdef SIGTERM
715 SIGTERM,
716# endif
717# ifdef SIGXCPU
718 SIGXCPU,
719# endif
720# ifdef SIGXFSZ
721 SIGXFSZ,
722# endif
723};
724
725 void
726catchints()
727{
728 static int catching_ints;
729 if (!catching_ints) {
730 catching_ints = true;
731 setup_catchsig(regsigs, (int) (sizeof(regsigs)/sizeof(*regsigs)));
732 }
733}
734
735#if has_mmap && large_memory && mmap_signal
736
737 /*
738 * If you mmap an NFS file, and someone on another client removes the last
739 * link to that file, and you later reference an uncached part of that file,
740 * you'll get a SIGBUS or SIGSEGV (depending on the operating system).
741 * Catch the signal and report the problem to the user.
742 * Unfortunately, there's no portable way to differentiate between this
743 * problem and actual bugs in the program.
744 * This NFS problem is rare, thank goodness.
745 *
746 * This can also occur if someone truncates the file, even without NFS.
747 */
748
749 static int const mmapsigs[] = { mmap_signal };
750
751 void
752 catchmmapints()
753 {
754 static int catching_mmap_ints;
755 if (!catching_mmap_ints) {
756 catching_mmap_ints = true;
757 setup_catchsig(mmapsigs, (int)(sizeof(mmapsigs)/sizeof(*mmapsigs)));
758 }
759 }
760#endif
761
762#endif /* has_signal */
763
764
765 void
766fastcopy(inf,outf)
767 register RILE *inf;
768 FILE *outf;
769/* Function: copies the remainder of file inf to outf.
770 */
771{
772#if large_memory
773# if maps_memory
774 awrite((char const*)inf->ptr, (size_t)(inf->lim - inf->ptr), outf);
775 inf->ptr = inf->lim;
776# else
777 for (;;) {
778 awrite((char const*)inf->ptr, (size_t)(inf->readlim - inf->ptr), outf);
779 inf->ptr = inf->readlim;
780 if (inf->ptr == inf->lim)
781 break;
782 VOID Igetmore(inf);
783 }
784# endif
785#else
786 char buf[BUFSIZ*8];
787 register fread_type rcount;
788
789 /*now read the rest of the file in blocks*/
790 while (!feof(inf)) {
791 if (!(rcount = Fread(buf,sizeof(*buf),sizeof(buf),inf))) {
792 testIerror(inf);
793 return;
794 }
795 awrite(buf, (size_t)rcount, outf);
796 }
797#endif
798}
799
800#ifndef SSIZE_MAX
801 /* This does not work in #ifs, but it's good enough for us. */
802 /* Underestimating SSIZE_MAX may slow us down, but it won't break us. */
803# define SSIZE_MAX ((unsigned)-1 >> 1)
804#endif
805
806 void
807awrite(buf, chars, f)
808 char const *buf;
809 size_t chars;
810 FILE *f;
811{
812 /* Posix 1003.1-1990 ssize_t hack */
813 while (SSIZE_MAX < chars) {
814 if (Fwrite(buf, sizeof(*buf), SSIZE_MAX, f) != SSIZE_MAX)
815 Oerror();
816 buf += SSIZE_MAX;
817 chars -= SSIZE_MAX;
818 }
819
820 if (Fwrite(buf, sizeof(*buf), chars, f) != chars)
821 Oerror();
822}
823
824/* dup a file descriptor; the result must not be stdin, stdout, or stderr. */
825 static int dupSafer P((int));
826 static int
827dupSafer(fd)
828 int fd;
829{
830# ifdef F_DUPFD
831 return fcntl(fd, F_DUPFD, STDERR_FILENO + 1);
832# else
833 int e, f, i, used = 0;
834 while (STDIN_FILENO <= (f = dup(fd)) && f <= STDERR_FILENO)
835 used |= 1<<f;
836 e = errno;
837 for (i = STDIN_FILENO; i <= STDERR_FILENO; i++)
838 if (used & (1<<i))
839 VOID close(i);
840 errno = e;
841 return f;
842# endif
843}
844
845/* Renumber a file descriptor so that it's not stdin, stdout, or stderr. */
846 int
847fdSafer(fd)
848 int fd;
849{
850 if (STDIN_FILENO <= fd && fd <= STDERR_FILENO) {
851 int f = dupSafer(fd);
852 int e = errno;
853 VOID close(fd);
854 errno = e;
855 fd = f;
856 }
857 return fd;
858}
859
860/* Like fopen, except the result is never stdin, stdout, or stderr. */
861 FILE *
862fopenSafer(filename, type)
863 char const *filename;
864 char const *type;
865{
866 FILE *stream = fopen(filename, type);
867 if (stream) {
868 int fd = fileno(stream);
869 if (STDIN_FILENO <= fd && fd <= STDERR_FILENO) {
870 int f = dupSafer(fd);
871 if (f < 0) {
872 int e = errno;
873 VOID fclose(stream);
874 errno = e;
875 return 0;
876 }
877 if (fclose(stream) != 0) {
878 int e = errno;
879 VOID close(f);
880 errno = e;
881 return 0;
882 }
883 stream = fdopen(f, type);
884 }
885 }
886 return stream;
887}
888
889
890#ifdef F_DUPFD
891# undef dup
892# define dup(fd) fcntl(fd, F_DUPFD, 0)
893#endif
894
895
896#if has_fork || has_spawn
897
898 static int movefd P((int,int));
899 static int
900movefd(old, new)
901 int old, new;
902{
903 if (old < 0 || old == new)
904 return old;
905# ifdef F_DUPFD
906 new = fcntl(old, F_DUPFD, new);
907# else
908 new = dup2(old, new);
909# endif
910 return close(old)==0 ? new : -1;
911}
912
913 static int fdreopen P((int,char const*,int));
914 static int
915fdreopen(fd, file, flags)
916 int fd;
917 char const *file;
918 int flags;
919{
920 int newfd;
921 VOID close(fd);
922 newfd =
923#if !open_can_creat
924 flags&O_CREAT ? creat(file, S_IRUSR|S_IWUSR) :
925#endif
926 open(file, flags, S_IRUSR|S_IWUSR);
927 return movefd(newfd, fd);
928}
929
930#if has_spawn
931 static void redirect P((int,int));
932 static void
933redirect(old, new)
934 int old, new;
935/*
936* Move file descriptor OLD to NEW.
937* If OLD is -1, do nothing.
938* If OLD is -2, just close NEW.
939*/
940{
941 if ((old != -1 && close(new) != 0) || (0 <= old && movefd(old,new) < 0))
942 efaterror("spawn I/O redirection");
943}
944#endif
945
946
947#else /* !has_fork && !has_spawn */
948
949 static void bufargcat P((struct buf*,int,char const*));
950 static void
951bufargcat(b, c, s)
952 register struct buf *b;
953 int c;
954 register char const *s;
955/* Append to B a copy of C, plus a quoted copy of S. */
956{
957 register char *p;
958 register char const *t;
959 size_t bl, sl;
960
961 for (t=s, sl=0; *t; )
962 sl += 3*(*t++=='\'') + 1;
963 bl = strlen(b->string);
964 bufrealloc(b, bl + sl + 4);
965 p = b->string + bl;
966 *p++ = c;
967 *p++ = '\'';
968 while (*s) {
969 if (*s == '\'') {
970 *p++ = '\'';
971 *p++ = '\\';
972 *p++ = '\'';
973 }
974 *p++ = *s++;
975 }
976 *p++ = '\'';
977 *p = 0;
978}
979
980#endif
981
982#if !has_spawn && has_fork
983/*
984* Output the string S to stderr, without touching any I/O buffers.
985* This is useful if you are a child process, whose buffers are usually wrong.
986* Exit immediately if the write does not completely succeed.
987*/
988static void write_stderr P((char const *));
989 static void
990write_stderr(s)
991 char const *s;
992{
993 size_t slen = strlen(s);
994 if (write(STDERR_FILENO, s, slen) != slen)
995 _exit(EXIT_TROUBLE);
996}
997#endif
998
999/*
1000* Run a command.
1001* infd, if not -1, is the input file descriptor.
1002* outname, if nonzero, is the name of the output file.
1003* args[1..] form the command to be run; args[0] might be modified.
1004*/
1005 int
1006runv(infd, outname, args)
1007 int infd;
1008 char const *outname, **args;
1009{
1010 int wstatus;
1011
1012#if bad_wait_if_SIGCHLD_ignored
1013 static int fixed_SIGCHLD;
1014 if (!fixed_SIGCHLD) {
1015 fixed_SIGCHLD = true;
1016# ifndef SIGCHLD
1017# define SIGCHLD SIGCLD
1018# endif
1019 VOID signal(SIGCHLD, SIG_DFL);
1020 }
1021#endif
1022
1023 oflush();
1024 eflush();
1025 {
1026#if has_spawn
1027 int in, out;
1028 char const *file;
1029
1030 in = -1;
1031 if (infd != -1 && infd != STDIN_FILENO) {
1032 if ((in = dup(STDIN_FILENO)) < 0) {
1033 if (errno != EBADF)
1034 efaterror("spawn input setup");
1035 in = -2;
1036 } else {
1037# ifdef F_DUPFD
1038 if (close(STDIN_FILENO) != 0)
1039 efaterror("spawn input close");
1040# endif
1041 }
1042 if (
1043# ifdef F_DUPFD
1044 fcntl(infd, F_DUPFD, STDIN_FILENO) != STDIN_FILENO
1045# else
1046 dup2(infd, STDIN_FILENO) != STDIN_FILENO
1047# endif
1048 )
1049 efaterror("spawn input redirection");
1050 }
1051
1052 out = -1;
1053 if (outname) {
1054 if ((out = dup(STDOUT_FILENO)) < 0) {
1055 if (errno != EBADF)
1056 efaterror("spawn output setup");
1057 out = -2;
1058 }
1059 if (fdreopen(
1060 STDOUT_FILENO, outname,
1061 O_CREAT | O_TRUNC | O_WRONLY
1062 ) < 0)
1063 efaterror(outname);
1064 }
1065
1066 wstatus = spawn_RCS(0, args[1], (char**)(args + 1));
1067# ifdef RCS_SHELL
1068 if (wstatus == -1 && errno == ENOEXEC) {
1069 args[0] = RCS_SHELL;
1070 wstatus = spawnv(0, args[0], (char**)args);
1071 }
1072# endif
1073 redirect(in, STDIN_FILENO);
1074 redirect(out, STDOUT_FILENO);
1075#else
1076#if has_fork
1077 pid_t pid;
1078 if (!(pid = vfork())) {
1079 char const *notfound;
1080 if (infd != -1 && infd != STDIN_FILENO && (
1081# ifdef F_DUPFD
1082 (VOID close(STDIN_FILENO),
1083 fcntl(infd, F_DUPFD, STDIN_FILENO) != STDIN_FILENO)
1084# else
1085 dup2(infd, STDIN_FILENO) != STDIN_FILENO
1086# endif
1087 )) {
1088 /* Avoid perror since it may misuse buffers. */
1089 write_stderr(args[1]);
1090 write_stderr(": I/O redirection failed\n");
1091 _exit(EXIT_TROUBLE);
1092 }
1093
1094 if (outname)
1095 if (fdreopen(
1096 STDOUT_FILENO, outname,
1097 O_CREAT | O_TRUNC | O_WRONLY
1098 ) < 0) {
1099 /* Avoid perror since it may misuse buffers. */
1100 write_stderr(args[1]);
1101 write_stderr(": ");
1102 write_stderr(outname);
1103 write_stderr(": cannot create\n");
1104 _exit(EXIT_TROUBLE);
1105 }
1106 VOID exec_RCS(args[1], (char**)(args + 1));
1107 notfound = args[1];
1108# ifdef RCS_SHELL
1109 if (errno == ENOEXEC) {
1110 args[0] = notfound = RCS_SHELL;
1111 VOID execv(args[0], (char**)args);
1112 }
1113# endif
1114
1115 /* Avoid perror since it may misuse buffers. */
1116 write_stderr(notfound);
1117 write_stderr(": not found\n");
1118 _exit(EXIT_TROUBLE);
1119 }
1120 if (pid < 0)
1121 efaterror("fork");
1122# if has_waitpid
1123 if (waitpid(pid, &wstatus, 0) < 0)
1124 efaterror("waitpid");
1125# else
1126 {
1127 pid_t w;
1128 do {
1129 if ((w = wait(&wstatus)) < 0)
1130 efaterror("wait");
1131 } while (w != pid);
1132 }
1133# endif
1134#else
1135 static struct buf b;
1136 char const *p;
1137
1138 /* Use system(). On many hosts system() discards signals. Yuck! */
1139 p = args + 1;
1140 bufscpy(&b, *p);
1141 while (*++p)
1142 bufargcat(&b, ' ', *p);
1143 if (infd != -1 && infd != STDIN_FILENO) {
1144 char redirection[32];
1145 VOID sprintf(redirection, "<&%d", infd);
1146 bufscat(&b, redirection);
1147 }
1148 if (outname)
1149 bufargcat(&b, '>', outname);
1150 wstatus = system(b.string);
1151#endif
1152#endif
1153 }
1154 if (!WIFEXITED(wstatus)) {
1155 if (WIFSIGNALED(wstatus)) {
1156 psignal(WTERMSIG(wstatus), args[1]);
1157 fatcleanup(1);
1158 }
1159 faterror("%s failed for unknown reason", args[1]);
1160 }
1161 return WEXITSTATUS(wstatus);
1162}
1163
1164#define CARGSMAX 20
1165/*
1166* Run a command.
1167* infd, if not -1, is the input file descriptor.
1168* outname, if nonzero, is the name of the output file.
1169* The remaining arguments specify the command and its arguments.
1170*/
1171 int
1172#if has_prototypes
1173run(int infd, char const *outname, ...)
1174#else
1175 /*VARARGS2*/
1176run(infd, outname, va_alist)
1177 int infd;
1178 char const *outname;
1179 va_dcl
1180#endif
1181{
1182 va_list ap;
1183 char const *rgargs[CARGSMAX];
1184 register int i;
1185 vararg_start(ap, outname);
1186 for (i = 1; (rgargs[i++] = va_arg(ap, char const*)); )
1187 if (CARGSMAX <= i)
1188 faterror("too many command arguments");
1189 va_end(ap);
1190 return runv(infd, outname, rgargs);
1191}
1192
1193
1194int RCSversion;
1195
1196 void
1197setRCSversion(str)
1198 char const *str;
1199{
1200 static int oldversion;
1201
1202 register char const *s = str + 2;
1203
1204 if (*s) {
1205 int v = VERSION_DEFAULT;
1206
1207 if (oldversion)
1208 redefined('V');
1209 oldversion = true;
1210 v = 0;
1211 while (isdigit(*s))
1212 v = 10*v + *s++ - '0';
1213 if (*s)
1214 error("%s isn't a number", str);
1215 else if (v < VERSION_min || VERSION_max < v)
1216 error("%s out of range %d..%d",
1217 str, VERSION_min, VERSION_max
1218 );
1219
1220 RCSversion = VERSION(v);
1221 } else {
1222 printf("RCS version %s\n", RCS_version_string);
1223 exit(0);
1224 }
1225}
1226
1227 int
1228getRCSINIT(argc, argv, newargv)
1229 int argc;
1230 char **argv, ***newargv;
1231{
1232 register char *p, *q, **pp;
1233 char const *ev;
1233 size_t n;
1234
1234 size_t n;
1235
1236 if ((ev = cgetenv("RCSLOCALID")))
1237 setRCSLocalId(ev);
1238
1239 if ((ev = cgetenv("RCSINCEXC")))
1240 setIncExc(ev);
1241
1235 if (!(q = cgetenv("RCSINIT")))
1236 *newargv = argv;
1237 else {
1238 n = argc + 2;
1239 /*
1240 * Count spaces in RCSINIT to allocate a new arg vector.
1241 * This is an upper bound, but it's OK even if too large.
1242 */
1243 for (p = q; ; ) {
1244 switch (*p++) {
1245 default:
1246 continue;
1247
1248 case ' ':
1249 case '\b': case '\f': case '\n':
1250 case '\r': case '\t': case '\v':
1251 n++;
1252 continue;
1253
1254 case '\0':
1255 break;
1256 }
1257 break;
1258 }
1259 *newargv = pp = tnalloc(char*, n);
1260 *pp++ = *argv++; /* copy program name */
1261 for (p = q; ; ) {
1262 for (;;) {
1263 switch (*q) {
1264 case '\0':
1265 goto copyrest;
1266
1267 case ' ':
1268 case '\b': case '\f': case '\n':
1269 case '\r': case '\t': case '\v':
1270 q++;
1271 continue;
1272 }
1273 break;
1274 }
1275 *pp++ = p;
1276 ++argc;
1277 for (;;) {
1278 switch ((*p++ = *q++)) {
1279 case '\0':
1280 goto copyrest;
1281
1282 case '\\':
1283 if (!*q)
1284 goto copyrest;
1285 p[-1] = *q++;
1286 continue;
1287
1288 default:
1289 continue;
1290
1291 case ' ':
1292 case '\b': case '\f': case '\n':
1293 case '\r': case '\t': case '\v':
1294 break;
1295 }
1296 break;
1297 }
1298 p[-1] = '\0';
1299 }
1300 copyrest:
1301 while ((*pp++ = *argv++))
1302 continue;
1303 }
1304 return argc;
1305}
1306
1307
1308#define cacheid(E) static uid_t i; static int s; if (!s){ s=1; i=(E); } return i
1309
1310#if has_getuid
1311 uid_t ruid() { cacheid(getuid()); }
1312#endif
1313#if has_setuid
1314 uid_t euid() { cacheid(geteuid()); }
1315#endif
1316
1317
1318#if has_setuid
1319
1320/*
1321 * Setuid execution really works only with Posix 1003.1a Draft 5 seteuid(),
1322 * because it lets us switch back and forth between arbitrary users.
1323 * If seteuid() doesn't work, we fall back on setuid(),
1324 * which works if saved setuid is supported,
1325 * unless the real or effective user is root.
1326 * This area is such a mess that we always check switches at runtime.
1327 */
1328
1329 static void
1330#if has_prototypes
1331set_uid_to(uid_t u)
1332#else
1333 set_uid_to(u) uid_t u;
1334#endif
1335/* Become user u. */
1336{
1337 static int looping;
1338
1339 if (euid() == ruid())
1340 return;
1341#if (has_fork||has_spawn) && DIFF_ABSOLUTE
1342# if has_setreuid
1343 if (setreuid(u==euid() ? ruid() : euid(), u) != 0)
1344 efaterror("setuid");
1345# else
1346 if (seteuid(u) != 0)
1347 efaterror("setuid");
1348# endif
1349#endif
1350 if (geteuid() != u) {
1351 if (looping)
1352 return;
1353 looping = true;
1354 faterror("root setuid not supported" + (u?5:0));
1355 }
1356}
1357
1358static int stick_with_euid;
1359
1360 void
1361/* Ignore all calls to seteid() and setrid(). */
1362nosetid()
1363{
1364 stick_with_euid = true;
1365}
1366
1367 void
1368seteid()
1369/* Become effective user. */
1370{
1371 if (!stick_with_euid)
1372 set_uid_to(euid());
1373}
1374
1375 void
1376setrid()
1377/* Become real user. */
1378{
1379 if (!stick_with_euid)
1380 set_uid_to(ruid());
1381}
1382#endif
1383
1384 time_t
1385now()
1386{
1387 static time_t t;
1388 if (!t && time(&t) == -1)
1389 efaterror("time");
1390 return t;
1391}
1242 if (!(q = cgetenv("RCSINIT")))
1243 *newargv = argv;
1244 else {
1245 n = argc + 2;
1246 /*
1247 * Count spaces in RCSINIT to allocate a new arg vector.
1248 * This is an upper bound, but it's OK even if too large.
1249 */
1250 for (p = q; ; ) {
1251 switch (*p++) {
1252 default:
1253 continue;
1254
1255 case ' ':
1256 case '\b': case '\f': case '\n':
1257 case '\r': case '\t': case '\v':
1258 n++;
1259 continue;
1260
1261 case '\0':
1262 break;
1263 }
1264 break;
1265 }
1266 *newargv = pp = tnalloc(char*, n);
1267 *pp++ = *argv++; /* copy program name */
1268 for (p = q; ; ) {
1269 for (;;) {
1270 switch (*q) {
1271 case '\0':
1272 goto copyrest;
1273
1274 case ' ':
1275 case '\b': case '\f': case '\n':
1276 case '\r': case '\t': case '\v':
1277 q++;
1278 continue;
1279 }
1280 break;
1281 }
1282 *pp++ = p;
1283 ++argc;
1284 for (;;) {
1285 switch ((*p++ = *q++)) {
1286 case '\0':
1287 goto copyrest;
1288
1289 case '\\':
1290 if (!*q)
1291 goto copyrest;
1292 p[-1] = *q++;
1293 continue;
1294
1295 default:
1296 continue;
1297
1298 case ' ':
1299 case '\b': case '\f': case '\n':
1300 case '\r': case '\t': case '\v':
1301 break;
1302 }
1303 break;
1304 }
1305 p[-1] = '\0';
1306 }
1307 copyrest:
1308 while ((*pp++ = *argv++))
1309 continue;
1310 }
1311 return argc;
1312}
1313
1314
1315#define cacheid(E) static uid_t i; static int s; if (!s){ s=1; i=(E); } return i
1316
1317#if has_getuid
1318 uid_t ruid() { cacheid(getuid()); }
1319#endif
1320#if has_setuid
1321 uid_t euid() { cacheid(geteuid()); }
1322#endif
1323
1324
1325#if has_setuid
1326
1327/*
1328 * Setuid execution really works only with Posix 1003.1a Draft 5 seteuid(),
1329 * because it lets us switch back and forth between arbitrary users.
1330 * If seteuid() doesn't work, we fall back on setuid(),
1331 * which works if saved setuid is supported,
1332 * unless the real or effective user is root.
1333 * This area is such a mess that we always check switches at runtime.
1334 */
1335
1336 static void
1337#if has_prototypes
1338set_uid_to(uid_t u)
1339#else
1340 set_uid_to(u) uid_t u;
1341#endif
1342/* Become user u. */
1343{
1344 static int looping;
1345
1346 if (euid() == ruid())
1347 return;
1348#if (has_fork||has_spawn) && DIFF_ABSOLUTE
1349# if has_setreuid
1350 if (setreuid(u==euid() ? ruid() : euid(), u) != 0)
1351 efaterror("setuid");
1352# else
1353 if (seteuid(u) != 0)
1354 efaterror("setuid");
1355# endif
1356#endif
1357 if (geteuid() != u) {
1358 if (looping)
1359 return;
1360 looping = true;
1361 faterror("root setuid not supported" + (u?5:0));
1362 }
1363}
1364
1365static int stick_with_euid;
1366
1367 void
1368/* Ignore all calls to seteid() and setrid(). */
1369nosetid()
1370{
1371 stick_with_euid = true;
1372}
1373
1374 void
1375seteid()
1376/* Become effective user. */
1377{
1378 if (!stick_with_euid)
1379 set_uid_to(euid());
1380}
1381
1382 void
1383setrid()
1384/* Become real user. */
1385{
1386 if (!stick_with_euid)
1387 set_uid_to(ruid());
1388}
1389#endif
1390
1391 time_t
1392now()
1393{
1394 static time_t t;
1395 if (!t && time(&t) == -1)
1396 efaterror("time");
1397 return t;
1398}