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
|
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;
|