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