1/* $NetBSD: rcsutil.c,v 1.2 2016/01/14 04:22:39 christos Exp $ */ 2 3/* RCS utility functions */ 4 5/* Copyright 1982, 1988, 1989 Walter Tichy 6 Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert 7 Distributed under license by the Free Software Foundation, Inc. 8 9This file is part of RCS. 10 11RCS is free software; you can redistribute it and/or modify 12it under the terms of the GNU General Public License as published by 13the Free Software Foundation; either version 2, or (at your option) 14any later version. 15 16RCS is distributed in the hope that it will be useful, 17but WITHOUT ANY WARRANTY; without even the implied warranty of 18MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19GNU General Public License for more details. 20 21You should have received a copy of the GNU General Public License 22along with RCS; see the file COPYING. 23If not, write to the Free Software Foundation, 2459 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 25 26Report problems and direct all questions to: 27 28 rcs-bugs@cs.purdue.edu 29 30*/ 31 32 33 34 35/* 36 * Log: rcsutil.c,v 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 192libId(utilId, "Id: rcsutil.c,v 5.20 1995/06/16 06:19:24 eggert Exp ") 193 194#if !has_memcmp 195 int 196memcmp(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 * 213memcpy(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 */ 236struct alloclist { 237 malloc_type alloc; 238 struct alloclist *nextalloc; 239}; 240static struct alloclist *alloced; 241 242 243 static malloc_type okalloc P((malloc_type)); 244 static malloc_type 245okalloc(p) 246 malloc_type p; 247{ 248 if (!p) 249 faterror("out of memory"); 250 return p; 251} 252 253 malloc_type 254testalloc(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 262testrealloc(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 271fremember(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 282ftestalloc(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 290ffree() 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 303ffree1(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 * 317str_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 * 325fstr_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 * 333cgetenv(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 * 343getusername(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 = strdup(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 401static 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 432my_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 490static 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 496catchsig(s) 497 int s; 498#ifdef SA_SIGINFO 499{ 500 catchsigaction(s, (siginfo_t *)0, (void *)0); 501} 502 static signal_type 503catchsigaction(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 590ignoreints() 591{ 592 ++holdlevel; 593} 594 595 void 596restoreints() 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 607static 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 = catchsigaction; 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 704static 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 729catchints() 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 769fastcopy(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 810awrite(buf, chars, f) 811 char const *buf; 812 size_t chars; 813 FILE *f; 814{ 815 if (buf == NULL) 816 return; 817 818 /* Posix 1003.1-1990 ssize_t hack */ 819 while (SSIZE_MAX < chars) { 820 if (Fwrite(buf, sizeof(*buf), SSIZE_MAX, f) != SSIZE_MAX) 821 Oerror(); 822 buf += SSIZE_MAX; 823 chars -= SSIZE_MAX; 824 } 825 826 if (Fwrite(buf, sizeof(*buf), chars, f) != chars) 827 Oerror(); 828} 829 830/* dup a file descriptor; the result must not be stdin, stdout, or stderr. */ 831 static int dupSafer P((int)); 832 static int 833dupSafer(fd) 834 int fd; 835{ 836# ifdef F_DUPFD 837 return fcntl(fd, F_DUPFD, STDERR_FILENO + 1); 838# else 839 int e, f, i, used = 0; 840 while (STDIN_FILENO <= (f = dup(fd)) && f <= STDERR_FILENO) 841 used |= 1<<f; 842 e = errno; 843 for (i = STDIN_FILENO; i <= STDERR_FILENO; i++) 844 if (used & (1<<i)) 845 VOID close(i); 846 errno = e; 847 return f; 848# endif 849} 850 851/* Renumber a file descriptor so that it's not stdin, stdout, or stderr. */ 852 int 853fdSafer(fd) 854 int fd; 855{ 856 if (STDIN_FILENO <= fd && fd <= STDERR_FILENO) { 857 int f = dupSafer(fd); 858 int e = errno; 859 VOID close(fd); 860 errno = e; 861 fd = f; 862 } 863 return fd; 864} 865 866/* Like fopen, except the result is never stdin, stdout, or stderr. */ 867 FILE * 868fopenSafer(filename, type) 869 char const *filename; 870 char const *type; 871{ 872 FILE *stream = fopen(filename, type); 873 if (stream) { 874 int fd = fileno(stream); 875 if (STDIN_FILENO <= fd && fd <= STDERR_FILENO) { 876 int f = dupSafer(fd); 877 if (f < 0) { 878 int e = errno; 879 VOID fclose(stream); 880 errno = e; 881 return 0; 882 } 883 if (fclose(stream) != 0) { 884 int e = errno; 885 VOID close(f); 886 errno = e; 887 return 0; 888 } 889 stream = fdopen(f, type); 890 } 891 } 892 return stream; 893} 894 895 896#ifdef F_DUPFD 897# undef dup 898# define dup(fd) fcntl(fd, F_DUPFD, 0) 899#endif 900 901 902#if has_fork || has_spawn 903 904 static int movefd P((int,int)); 905 static int 906movefd(old, new) 907 int old, new; 908{ 909 if (old < 0 || old == new) 910 return old; 911# ifdef F_DUPFD 912 new = fcntl(old, F_DUPFD, new); 913# else 914 new = dup2(old, new); 915# endif 916 return close(old)==0 ? new : -1; 917} 918 919 static int fdreopen P((int,char const*,int)); 920 static int 921fdreopen(fd, file, flags) 922 int fd; 923 char const *file; 924 int flags; 925{ 926 int newfd; 927 VOID close(fd); 928 newfd = 929#if !open_can_creat 930 flags&O_CREAT ? creat(file, S_IRUSR|S_IWUSR) : 931#endif 932 open(file, flags, S_IRUSR|S_IWUSR); 933 return movefd(newfd, fd); 934} 935 936#if has_spawn 937 static void redirect P((int,int)); 938 static void 939redirect(old, new) 940 int old, new; 941/* 942* Move file descriptor OLD to NEW. 943* If OLD is -1, do nothing. 944* If OLD is -2, just close NEW. 945*/ 946{ 947 if ((old != -1 && close(new) != 0) || (0 <= old && movefd(old,new) < 0)) 948 efaterror("spawn I/O redirection"); 949} 950#endif 951 952 953#else /* !has_fork && !has_spawn */ 954 955 static void bufargcat P((struct buf*,int,char const*)); 956 static void 957bufargcat(b, c, s) 958 register struct buf *b; 959 int c; 960 register char const *s; 961/* Append to B a copy of C, plus a quoted copy of S. */ 962{ 963 register char *p; 964 register char const *t; 965 size_t bl, sl; 966 967 for (t=s, sl=0; *t; ) 968 sl += 3*(*t++=='\'') + 1; 969 bl = strlen(b->string); 970 bufrealloc(b, bl + sl + 4); 971 p = b->string + bl; 972 *p++ = c; 973 *p++ = '\''; 974 while (*s) { 975 if (*s == '\'') { 976 *p++ = '\''; 977 *p++ = '\\'; 978 *p++ = '\''; 979 } 980 *p++ = *s++; 981 } 982 *p++ = '\''; 983 *p = 0; 984} 985 986#endif 987 988#if !has_spawn && has_fork 989/* 990* Output the string S to stderr, without touching any I/O buffers. 991* This is useful if you are a child process, whose buffers are usually wrong. 992* Exit immediately if the write does not completely succeed. 993*/ 994static void write_stderr P((char const *)); 995 static void 996write_stderr(s) 997 char const *s; 998{ 999 size_t slen = strlen(s); 1000 if (write(STDERR_FILENO, s, slen) != slen) 1001 _exit(EXIT_TROUBLE); 1002} 1003#endif 1004 1005/* 1006* Run a command. 1007* infd, if not -1, is the input file descriptor. 1008* outname, if nonzero, is the name of the output file. 1009* args[1..] form the command to be run; args[0] might be modified. 1010*/ 1011 int 1012runv(infd, outname, args) 1013 int infd; 1014 char const *outname, **args; 1015{ 1016 int wstatus; 1017 1018#if bad_wait_if_SIGCHLD_ignored 1019 static int fixed_SIGCHLD; 1020 if (!fixed_SIGCHLD) { 1021 fixed_SIGCHLD = true; 1022# ifndef SIGCHLD 1023# define SIGCHLD SIGCLD 1024# endif 1025 VOID signal(SIGCHLD, SIG_DFL); 1026 } 1027#endif 1028 1029 oflush(); 1030 eflush(); 1031 { 1032#if has_spawn 1033 int in, out; 1034 char const *file; 1035 1036 in = -1; 1037 if (infd != -1 && infd != STDIN_FILENO) { 1038 if ((in = dup(STDIN_FILENO)) < 0) { 1039 if (errno != EBADF) 1040 efaterror("spawn input setup"); 1041 in = -2; 1042 } else { 1043# ifdef F_DUPFD 1044 if (close(STDIN_FILENO) != 0) 1045 efaterror("spawn input close"); 1046# endif 1047 } 1048 if ( 1049# ifdef F_DUPFD 1050 fcntl(infd, F_DUPFD, STDIN_FILENO) != STDIN_FILENO 1051# else 1052 dup2(infd, STDIN_FILENO) != STDIN_FILENO 1053# endif 1054 ) 1055 efaterror("spawn input redirection"); 1056 } 1057 1058 out = -1; 1059 if (outname) { 1060 if ((out = dup(STDOUT_FILENO)) < 0) { 1061 if (errno != EBADF) 1062 efaterror("spawn output setup"); 1063 out = -2; 1064 } 1065 if (fdreopen( 1066 STDOUT_FILENO, outname, 1067 O_CREAT | O_TRUNC | O_WRONLY 1068 ) < 0) 1069 efaterror(outname); 1070 } 1071 1072 wstatus = spawn_RCS(0, args[1], (char**)(args + 1)); 1073# ifdef RCS_SHELL 1074 if (wstatus == -1 && errno == ENOEXEC) { 1075 args[0] = RCS_SHELL; 1076 wstatus = spawnv(0, args[0], (char**)args); 1077 } 1078# endif 1079 redirect(in, STDIN_FILENO); 1080 redirect(out, STDOUT_FILENO); 1081#else 1082#if has_fork 1083 pid_t pid; 1084 if (!(pid = vfork())) { 1085 char const *notfound; 1086 if (infd != -1 && infd != STDIN_FILENO && ( 1087# ifdef F_DUPFD 1088 (VOID close(STDIN_FILENO), 1089 fcntl(infd, F_DUPFD, STDIN_FILENO) != STDIN_FILENO) 1090# else 1091 dup2(infd, STDIN_FILENO) != STDIN_FILENO 1092# endif 1093 )) { 1094 /* Avoid perror since it may misuse buffers. */ 1095 write_stderr(args[1]); 1096 write_stderr(": I/O redirection failed\n"); 1097 _exit(EXIT_TROUBLE); 1098 } 1099 1100 if (outname) 1101 if (fdreopen( 1102 STDOUT_FILENO, outname, 1103 O_CREAT | O_TRUNC | O_WRONLY 1104 ) < 0) { 1105 /* Avoid perror since it may misuse buffers. */ 1106 write_stderr(args[1]); 1107 write_stderr(": "); 1108 write_stderr(outname); 1109 write_stderr(": cannot create\n"); 1110 _exit(EXIT_TROUBLE); 1111 } 1112 VOID exec_RCS(args[1], (char**)(args + 1)); 1113 notfound = args[1]; 1114# ifdef RCS_SHELL 1115 if (errno == ENOEXEC) { 1116 args[0] = notfound = RCS_SHELL; 1117 VOID execv(args[0], (char**)args); 1118 } 1119# endif 1120 1121 /* Avoid perror since it may misuse buffers. */ 1122 write_stderr(notfound); 1123 write_stderr(": not found\n"); 1124 _exit(EXIT_TROUBLE); 1125 } 1126 if (pid < 0) 1127 efaterror("fork"); 1128# if has_waitpid 1129 if (waitpid(pid, &wstatus, 0) < 0) 1130 efaterror("waitpid"); 1131# else 1132 { 1133 pid_t w; 1134 do { 1135 if ((w = wait(&wstatus)) < 0) 1136 efaterror("wait"); 1137 } while (w != pid); 1138 } 1139# endif 1140#else 1141 static struct buf b; 1142 char const *p; 1143 1144 /* Use system(). On many hosts system() discards signals. Yuck! */ 1145 p = args + 1; 1146 bufscpy(&b, *p); 1147 while (*++p) 1148 bufargcat(&b, ' ', *p); 1149 if (infd != -1 && infd != STDIN_FILENO) { 1150 char redirection[32]; 1151 VOID sprintf(redirection, "<&%d", infd); 1152 bufscat(&b, redirection); 1153 } 1154 if (outname) 1155 bufargcat(&b, '>', outname); 1156 wstatus = system(b.string); 1157#endif 1158#endif 1159 } 1160 if (!WIFEXITED(wstatus)) { 1161 if (WIFSIGNALED(wstatus)) { 1162 psignal(WTERMSIG(wstatus), args[1]); 1163 fatcleanup(1); 1164 } 1165 faterror("%s failed for unknown reason", args[1]); 1166 } 1167 return WEXITSTATUS(wstatus); 1168} 1169 1170#define CARGSMAX 20 1171/* 1172* Run a command. 1173* infd, if not -1, is the input file descriptor. 1174* outname, if nonzero, is the name of the output file. 1175* The remaining arguments specify the command and its arguments. 1176*/ 1177 int 1178#if has_prototypes 1179run(int infd, char const *outname, ...) 1180#else 1181 /*VARARGS2*/ 1182run(infd, outname, va_alist) 1183 int infd; 1184 char const *outname; 1185 va_dcl 1186#endif 1187{ 1188 va_list ap; 1189 char const *rgargs[CARGSMAX]; 1190 register int i; 1191 vararg_start(ap, outname); 1192 for (i = 1; (rgargs[i++] = va_arg(ap, char const*)); ) 1193 if (CARGSMAX <= i) 1194 faterror("too many command arguments"); 1195 va_end(ap); 1196 return runv(infd, outname, rgargs); 1197} 1198 1199 1200int RCSversion; 1201 1202 void 1203setRCSversion(str) 1204 char const *str; 1205{ 1206 static int oldversion; 1207 1208 register char const *s = str + 2; 1209 1210 if (*s) { 1211 int v = VERSION_DEFAULT; 1212 1213 if (oldversion) 1214 redefined('V'); 1215 oldversion = true; 1216 v = 0; 1217 while (isdigit(*s)) 1218 v = 10*v + *s++ - '0'; 1219 if (*s) 1220 error("%s isn't a number", str); 1221 else if (v < VERSION_min || VERSION_max < v) 1222 error("%s out of range %d..%d", 1223 str, VERSION_min, VERSION_max 1224 ); 1225 1226 RCSversion = VERSION(v); 1227 } else { 1228 printf("RCS version %s\n", RCS_version_string); 1229 exit(0); 1230 } 1231} 1232 1233 int 1234getRCSINIT(argc, argv, newargv) 1235 int argc; 1236 char **argv, ***newargv; 1237{ 1238 register char *p, *q, **pp; 1239 size_t n; 1240 1241 if (!(q = cgetenv("RCSINIT"))) 1242 *newargv = argv; 1243 else { 1244 n = argc + 2; 1245 /* 1246 * Count spaces in RCSINIT to allocate a new arg vector. 1247 * This is an upper bound, but it's OK even if too large. 1248 */ 1249 for (p = q; ; ) { 1250 switch (*p++) { 1251 default: 1252 continue; 1253 1254 case ' ': 1255 case '\b': case '\f': case '\n': 1256 case '\r': case '\t': case '\v': 1257 n++; 1258 continue; 1259 1260 case '\0': 1261 break; 1262 } 1263 break; 1264 } 1265 *newargv = pp = tnalloc(char*, n); 1266 *pp++ = *argv++; /* copy program name */ 1267 for (p = q; ; ) { 1268 for (;;) { 1269 switch (*q) { 1270 case '\0': 1271 goto copyrest; 1272 1273 case ' ': 1274 case '\b': case '\f': case '\n': 1275 case '\r': case '\t': case '\v': 1276 q++; 1277 continue; 1278 } 1279 break; 1280 } 1281 *pp++ = p; 1282 ++argc; 1283 for (;;) { 1284 switch ((*p++ = *q++)) { 1285 case '\0': 1286 goto copyrest; 1287 1288 case '\\': 1289 if (!*q) 1290 goto copyrest; 1291 p[-1] = *q++; 1292 continue; 1293 1294 default: 1295 continue; 1296 1297 case ' ': 1298 case '\b': case '\f': case '\n': 1299 case '\r': case '\t': case '\v': 1300 break; 1301 } 1302 break; 1303 } 1304 p[-1] = '\0'; 1305 } 1306 copyrest: 1307 while ((*pp++ = *argv++)) 1308 continue; 1309 } 1310 return argc; 1311} 1312 1313 1314#define cacheid(E) static uid_t i; static int s; if (!s){ s=1; i=(E); } return i 1315 1316#if has_getuid 1317 uid_t ruid() { cacheid(getuid()); } 1318#endif 1319#if has_setuid 1320 uid_t euid() { cacheid(geteuid()); } 1321#endif 1322 1323 1324#if has_setuid 1325 1326/* 1327 * Setuid execution really works only with Posix 1003.1a Draft 5 seteuid(), 1328 * because it lets us switch back and forth between arbitrary users. 1329 * If seteuid() doesn't work, we fall back on setuid(), 1330 * which works if saved setuid is supported, 1331 * unless the real or effective user is root. 1332 * This area is such a mess that we always check switches at runtime. 1333 */ 1334 1335 static void 1336#if has_prototypes 1337set_uid_to(uid_t u) 1338#else 1339 set_uid_to(u) uid_t u; 1340#endif 1341/* Become user u. */ 1342{ 1343 static int looping; 1344 1345 if (euid() == ruid()) 1346 return; 1347#if (has_fork||has_spawn) && DIFF_ABSOLUTE 1348# if has_setreuid 1349 if (setreuid(u==euid() ? ruid() : euid(), u) != 0) 1350 efaterror("setuid"); 1351# else 1352 if (seteuid(u) != 0) 1353 efaterror("setuid"); 1354# endif 1355#endif 1356 if (geteuid() != u) { 1357 if (looping) 1358 return; 1359 looping = true; 1360 faterror("root setuid not supported" + (u?5:0)); 1361 } 1362} 1363 1364static int stick_with_euid; 1365 1366 void 1367/* Ignore all calls to seteid() and setrid(). */ 1368nosetid() 1369{ 1370 stick_with_euid = true; 1371} 1372 1373 void 1374seteid() 1375/* Become effective user. */ 1376{ 1377 if (!stick_with_euid) 1378 set_uid_to(euid()); 1379} 1380 1381 void 1382setrid() 1383/* Become real user. */ 1384{ 1385 if (!stick_with_euid) 1386 set_uid_to(ruid()); 1387} 1388#endif 1389 1390 time_t 1391now() 1392{ 1393 static time_t t; 1394 if (!t && time(&t) == -1) 1395 efaterror("time"); 1396 return t; 1397} 1398