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 * 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 189 libId(utilId, "$FreeBSD: src/gnu/usr.bin/rcs/lib/rcsutil.c,v 1.8 1999/08/27 23:36:49 peter Exp $") 190 191 #if !has_memcmp 192 int 193 memcmp(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 * 210 memcpy(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 */ 233 struct alloclist { 234 malloc_type alloc; 235 struct alloclist *nextalloc; 236 }; 237 static struct alloclist *alloced; 238 239 240 static malloc_type okalloc P((malloc_type)); 241 static malloc_type 242 okalloc(p) 243 malloc_type p; 244 { 245 if (!p) 246 faterror("out of memory"); 247 return p; 248 } 249 250 malloc_type 251 testalloc(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 259 testrealloc(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 268 fremember(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 279 ftestalloc(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 287 ffree() 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 300 ffree1(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 * 314 str_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 * 322 fstr_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 * 330 cgetenv(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 * 340 getusername(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 398 static 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 429 my_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 487 static 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 493 catchsig(s) 494 int s; 495 #ifdef SA_SIGINFO 496 { 497 catchsigaction(s, (siginfo_t *)0, (void *)0); 498 } 499 static signal_type 500 catchsigaction(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 587 ignoreints() 588 { 589 ++holdlevel; 590 } 591 592 void 593 restoreints() 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 604 static 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 701 static 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 726 catchints() 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 766 fastcopy(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 807 awrite(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 827 dupSafer(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 847 fdSafer(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 * 862 fopenSafer(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 900 movefd(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 915 fdreopen(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 933 redirect(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 951 bufargcat(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 */ 988 static void write_stderr P((char const *)); 989 static void 990 write_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 1006 runv(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 1173 run(int infd, char const *outname, ...) 1174 #else 1175 /*VARARGS2*/ 1176 run(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 1194 int RCSversion; 1195 1196 void 1197 setRCSversion(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 1228 getRCSINIT(argc, argv, newargv) 1229 int argc; 1230 char **argv, ***newargv; 1231 { 1232 register char *p, *q, **pp; 1233 char const *ev; 1234 size_t n; 1235 1236 if ((ev = cgetenv("RCSLOCALID"))) 1237 setRCSLocalId(ev); 1238 1239 if ((ev = cgetenv("RCSINCEXC"))) 1240 setIncExc(ev); 1241 1242 if (!(q = cgetenv("RCSINIT"))) 1243 *newargv = argv; 1244 else { 1245 n = argc + 2; 1246 /* 1247 * Count spaces in RCSINIT to allocate a new arg vector. 1248 * This is an upper bound, but it's OK even if too large. 1249 */ 1250 for (p = q; ; ) { 1251 switch (*p++) { 1252 default: 1253 continue; 1254 1255 case ' ': 1256 case '\b': case '\f': case '\n': 1257 case '\r': case '\t': case '\v': 1258 n++; 1259 continue; 1260 1261 case '\0': 1262 break; 1263 } 1264 break; 1265 } 1266 *newargv = pp = tnalloc(char*, n); 1267 *pp++ = *argv++; /* copy program name */ 1268 for (p = q; ; ) { 1269 for (;;) { 1270 switch (*q) { 1271 case '\0': 1272 goto copyrest; 1273 1274 case ' ': 1275 case '\b': case '\f': case '\n': 1276 case '\r': case '\t': case '\v': 1277 q++; 1278 continue; 1279 } 1280 break; 1281 } 1282 *pp++ = p; 1283 ++argc; 1284 for (;;) { 1285 switch ((*p++ = *q++)) { 1286 case '\0': 1287 goto copyrest; 1288 1289 case '\\': 1290 if (!*q) 1291 goto copyrest; 1292 p[-1] = *q++; 1293 continue; 1294 1295 default: 1296 continue; 1297 1298 case ' ': 1299 case '\b': case '\f': case '\n': 1300 case '\r': case '\t': case '\v': 1301 break; 1302 } 1303 break; 1304 } 1305 p[-1] = '\0'; 1306 } 1307 copyrest: 1308 while ((*pp++ = *argv++)) 1309 continue; 1310 } 1311 return argc; 1312 } 1313 1314 1315 #define cacheid(E) static uid_t i; static int s; if (!s){ s=1; i=(E); } return i 1316 1317 #if has_getuid 1318 uid_t ruid() { cacheid(getuid()); } 1319 #endif 1320 #if has_setuid 1321 uid_t euid() { cacheid(geteuid()); } 1322 #endif 1323 1324 1325 #if has_setuid 1326 1327 /* 1328 * Setuid execution really works only with Posix 1003.1a Draft 5 seteuid(), 1329 * because it lets us switch back and forth between arbitrary users. 1330 * If seteuid() doesn't work, we fall back on setuid(), 1331 * which works if saved setuid is supported, 1332 * unless the real or effective user is root. 1333 * This area is such a mess that we always check switches at runtime. 1334 */ 1335 1336 static void 1337 #if has_prototypes 1338 set_uid_to(uid_t u) 1339 #else 1340 set_uid_to(u) uid_t u; 1341 #endif 1342 /* Become user u. */ 1343 { 1344 static int looping; 1345 1346 if (euid() == ruid()) 1347 return; 1348 #if (has_fork||has_spawn) && DIFF_ABSOLUTE 1349 # if has_setreuid 1350 if (setreuid(u==euid() ? ruid() : euid(), u) != 0) 1351 efaterror("setuid"); 1352 # else 1353 if (seteuid(u) != 0) 1354 efaterror("setuid"); 1355 # endif 1356 #endif 1357 if (geteuid() != u) { 1358 if (looping) 1359 return; 1360 looping = true; 1361 faterror("root setuid not supported" + (u?5:0)); 1362 } 1363 } 1364 1365 static int stick_with_euid; 1366 1367 void 1368 /* Ignore all calls to seteid() and setrid(). */ 1369 nosetid() 1370 { 1371 stick_with_euid = true; 1372 } 1373 1374 void 1375 seteid() 1376 /* Become effective user. */ 1377 { 1378 if (!stick_with_euid) 1379 set_uid_to(euid()); 1380 } 1381 1382 void 1383 setrid() 1384 /* Become real user. */ 1385 { 1386 if (!stick_with_euid) 1387 set_uid_to(ruid()); 1388 } 1389 #endif 1390 1391 time_t 1392 now() 1393 { 1394 static time_t t; 1395 if (!t && time(&t) == -1) 1396 efaterror("time"); 1397 return t; 1398 } 1399