rcsutil.c revision 10
1/* 2 * RCS utilities 3 */ 4 5/* Copyright (C) 1982, 1988, 1989 Walter Tichy 6 Copyright 1990, 1991 by 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. If not, write to 23the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 24 25Report problems and direct all questions to: 26 27 rcs-bugs@cs.purdue.edu 28 29*/ 30 31 32 33 34/* $Log: rcsutil.c,v $ 35 * Revision 5.10 1991/10/07 17:32:46 eggert 36 * Support piece tables even if !has_mmap. 37 * 38 * Revision 5.9 1991/08/19 03:13:55 eggert 39 * Add spawn() support. Explicate assumptions about getting invoker's name. 40 * Standardize user-visible dates. Tune. 41 * 42 * Revision 5.8 1991/04/21 11:58:30 eggert 43 * Plug setuid security hole. 44 * 45 * Revision 5.6 1991/02/26 17:48:39 eggert 46 * Fix setuid bug. Use fread, fwrite more portably. 47 * Support waitpid. Don't assume -1 is acceptable to W* macros. 48 * strsave -> str_save (DG/UX name clash) 49 * 50 * Revision 5.5 1990/12/04 05:18:49 eggert 51 * Don't output a blank line after a signal diagnostic. 52 * Use -I for prompts and -q for diagnostics. 53 * 54 * Revision 5.4 1990/11/01 05:03:53 eggert 55 * Remove unneeded setid check. Add awrite(), fremember(). 56 * 57 * Revision 5.3 1990/10/06 00:16:45 eggert 58 * Don't fread F if feof(F). 59 * 60 * Revision 5.2 1990/09/04 08:02:31 eggert 61 * Store fread()'s result in an fread_type object. 62 * 63 * Revision 5.1 1990/08/29 07:14:07 eggert 64 * Declare getpwuid() more carefully. 65 * 66 * Revision 5.0 1990/08/22 08:13:46 eggert 67 * Add setuid support. Permit multiple locks per user. 68 * Remove compile-time limits; use malloc instead. 69 * Switch to GMT. Permit dates past 1999/12/31. 70 * Add -V. Remove snooping. Ansify and Posixate. 71 * Tune. Some USG hosts define NSIG but not sys_siglist. 72 * Don't run /bin/sh if it's hopeless. 73 * Don't leave garbage behind if the output is an empty pipe. 74 * Clean up after SIGXCPU or SIGXFSZ. Print name of signal that caused cleanup. 75 * 76 * Revision 4.6 89/05/01 15:13:40 narten 77 * changed copyright header to reflect current distribution rules 78 * 79 * Revision 4.5 88/11/08 16:01:02 narten 80 * corrected use of varargs routines 81 * 82 * Revision 4.4 88/08/09 19:13:24 eggert 83 * Check for memory exhaustion. 84 * Permit signal handlers to yield either 'void' or 'int'; fix oldSIGINT botch. 85 * Use execv(), not system(); yield exit status like diff(1)'s. 86 * 87 * Revision 4.3 87/10/18 10:40:22 narten 88 * Updating version numbers. Changes relative to 1.1 actually 89 * relative to 4.1 90 * 91 * Revision 1.3 87/09/24 14:01:01 narten 92 * Sources now pass through lint (if you ignore printf/sprintf/fprintf 93 * warnings) 94 * 95 * Revision 1.2 87/03/27 14:22:43 jenkins 96 * Port to suns 97 * 98 * Revision 4.1 83/05/10 15:53:13 wft 99 * Added getcaller() and findlock(). 100 * Changed catchints() to check SIGINT for SIG_IGN before setting up the signal 101 * (needed for background jobs in older shells). Added restoreints(). 102 * Removed printing of full RCS path from logcommand(). 103 * 104 * Revision 3.8 83/02/15 15:41:49 wft 105 * Added routine fastcopy() to copy remainder of a file in blocks. 106 * 107 * Revision 3.7 82/12/24 15:25:19 wft 108 * added catchints(), ignoreints() for catching and ingnoring interrupts; 109 * fixed catchsig(). 110 * 111 * Revision 3.6 82/12/08 21:52:05 wft 112 * Using DATEFORM to format dates. 113 * 114 * Revision 3.5 82/12/04 18:20:49 wft 115 * Replaced SNOOPDIR with SNOOPFILE; changed addlock() to update 116 * lockedby-field. 117 * 118 * Revision 3.4 82/12/03 17:17:43 wft 119 * Added check to addlock() ensuring only one lock per person. 120 * Addlock also returns a pointer to the lock created. Deleted fancydate(). 121 * 122 * Revision 3.3 82/11/27 12:24:37 wft 123 * moved rmsema(), trysema(), trydiraccess(), getfullRCSname() to rcsfnms.c. 124 * Introduced macro SNOOP so that snoop can be placed in directory other than 125 * TARGETDIR. Changed %02d to %.2d for compatibility reasons. 126 * 127 * Revision 3.2 82/10/18 21:15:11 wft 128 * added function getfullRCSname(). 129 * 130 * Revision 3.1 82/10/13 16:17:37 wft 131 * Cleanup message is now suppressed in quiet mode. 132 */ 133 134 135 136 137#include "rcsbase.h" 138 139libId(utilId, "$Id: rcsutil.c,v 5.10 1991/10/07 17:32:46 eggert Exp $") 140 141#if !has_memcmp 142 int 143memcmp(s1, s2, n) 144 void const *s1, *s2; 145 size_t n; 146{ 147 register unsigned char const 148 *p1 = (unsigned char const*)s1, 149 *p2 = (unsigned char const*)s2; 150 register size_t i = n; 151 register int r = 0; 152 while (i-- && !(r = (*p1++ - *p2++))) 153 ; 154 return r; 155} 156#endif 157 158#if !has_memcpy 159 void * 160memcpy(s1, s2, n) 161 void *s1; 162 void const *s2; 163 size_t n; 164{ 165 register char *p1 = (char*)s1; 166 register char const *p2 = (char const*)s2; 167 while (n--) 168 *p1++ = *p2++; 169 return s1; 170} 171#endif 172 173#if lint 174 malloc_type lintalloc; 175#endif 176 177/* 178 * list of blocks allocated with ftestalloc() 179 * These blocks can be freed by ffree when we're done with the current file. 180 * We could put the free block inside struct alloclist, rather than a pointer 181 * to the free block, but that would be less portable. 182 */ 183struct alloclist { 184 malloc_type alloc; 185 struct alloclist *nextalloc; 186}; 187static struct alloclist *alloced; 188 189 190 static malloc_type 191okalloc(p) 192 malloc_type p; 193{ 194 if (!p) 195 faterror("out of memory"); 196 return p; 197} 198 199 malloc_type 200testalloc(size) 201 size_t size; 202/* Allocate a block, testing that the allocation succeeded. */ 203{ 204 return okalloc(malloc(size)); 205} 206 207 malloc_type 208testrealloc(ptr, size) 209 malloc_type ptr; 210 size_t size; 211/* Reallocate a block, testing that the allocation succeeded. */ 212{ 213 return okalloc(realloc(ptr, size)); 214} 215 216 malloc_type 217fremember(ptr) 218 malloc_type ptr; 219/* Remember PTR in 'alloced' so that it can be freed later. Yield PTR. */ 220{ 221 register struct alloclist *q = talloc(struct alloclist); 222 q->nextalloc = alloced; 223 alloced = q; 224 return q->alloc = ptr; 225} 226 227 malloc_type 228ftestalloc(size) 229 size_t size; 230/* Allocate a block, putting it in 'alloced' so it can be freed later. */ 231{ 232 return fremember(testalloc(size)); 233} 234 235 void 236ffree() 237/* Free all blocks allocated with ftestalloc(). */ 238{ 239 register struct alloclist *p, *q; 240 for (p = alloced; p; p = q) { 241 q = p->nextalloc; 242 tfree(p->alloc); 243 tfree(p); 244 } 245 alloced = nil; 246} 247 248 void 249ffree1(f) 250 register char const *f; 251/* Free the block f, which was allocated by ftestalloc. */ 252{ 253 register struct alloclist *p, **a = &alloced; 254 255 while ((p = *a)->alloc != f) 256 a = &p->nextalloc; 257 *a = p->nextalloc; 258 tfree(p->alloc); 259 tfree(p); 260} 261 262 char * 263str_save(s) 264 char const *s; 265/* Save s in permanently allocated storage. */ 266{ 267 return strcpy(tnalloc(char, strlen(s)+1), s); 268} 269 270 char * 271fstr_save(s) 272 char const *s; 273/* Save s in storage that will be deallocated when we're done with this file. */ 274{ 275 return strcpy(ftnalloc(char, strlen(s)+1), s); 276} 277 278 char * 279cgetenv(name) 280 char const *name; 281/* Like getenv(), but yield a copy; getenv() can overwrite old results. */ 282{ 283 register char *p; 284 285 return (p=getenv(name)) ? str_save(p) : p; 286} 287 288 char const * 289getusername(suspicious) 290 int suspicious; 291/* Get the caller's login name. Trust only getwpuid if SUSPICIOUS. */ 292{ 293 static char *name; 294 295 if (!name) { 296 if ( 297 /* Prefer getenv() unless suspicious; it's much faster. */ 298# if getlogin_is_secure 299 (suspicious 300 || 301 !(name = cgetenv("LOGNAME")) 302 && !(name = cgetenv("USER"))) 303 && !(name = getlogin()) 304# else 305 suspicious 306 || 307 !(name = cgetenv("LOGNAME")) 308 && !(name = cgetenv("USER")) 309 && !(name = getlogin()) 310# endif 311 ) { 312#if has_getuid && has_getpwuid 313 struct passwd const *pw = getpwuid(ruid()); 314 if (!pw) 315 faterror("no password entry for userid %lu", 316 (unsigned long)ruid() 317 ); 318 name = pw->pw_name; 319#else 320#if has_setuid 321 faterror("setuid not supported"); 322#else 323 faterror("Who are you? Please set LOGNAME."); 324#endif 325#endif 326 } 327 checksid(name); 328 } 329 return name; 330} 331 332 333 334 335#if has_signal 336 337/* 338 * Signal handling 339 * 340 * Standard C places too many restrictions on signal handlers. 341 * We obey as many of them as we can. 342 * Posix places fewer restrictions, and we are Posix-compatible here. 343 */ 344 345static sig_atomic_t volatile heldsignal, holdlevel; 346 347 static signal_type 348catchsig(s) 349 int s; 350{ 351 char const *sname; 352 char buf[BUFSIZ]; 353 354#if sig_zaps_handler 355 /* If a signal arrives before we reset the signal handler, we lose. */ 356 VOID signal(s, SIG_IGN); 357#endif 358 if (holdlevel) { 359 heldsignal = s; 360 return; 361 } 362 ignoreints(); 363 setrid(); 364 if (!quietflag) { 365 sname = nil; 366#if has_sys_siglist && defined(NSIG) 367 if ((unsigned)s < NSIG) { 368# ifndef sys_siglist 369 extern char const *sys_siglist[]; 370# endif 371 sname = sys_siglist[s]; 372 } 373#else 374 switch (s) { 375#ifdef SIGHUP 376 case SIGHUP: sname = "Hangup"; break; 377#endif 378#ifdef SIGINT 379 case SIGINT: sname = "Interrupt"; break; 380#endif 381#ifdef SIGPIPE 382 case SIGPIPE: sname = "Broken pipe"; break; 383#endif 384#ifdef SIGQUIT 385 case SIGQUIT: sname = "Quit"; break; 386#endif 387#ifdef SIGTERM 388 case SIGTERM: sname = "Terminated"; break; 389#endif 390#ifdef SIGXCPU 391 case SIGXCPU: sname = "Cputime limit exceeded"; break; 392#endif 393#ifdef SIGXFSZ 394 case SIGXFSZ: sname = "Filesize limit exceeded"; break; 395#endif 396 } 397#endif 398 if (sname) 399 VOID sprintf(buf, "\nRCS: %s. Cleaning up.\n", sname); 400 else 401 VOID sprintf(buf, "\nRCS: Signal %d. Cleaning up.\n", s); 402 VOID write(STDERR_FILENO, buf, strlen(buf)); 403 } 404 exiterr(); 405} 406 407 void 408ignoreints() 409{ 410 ++holdlevel; 411} 412 413 void 414restoreints() 415{ 416 if (!--holdlevel && heldsignal) 417 VOID catchsig(heldsignal); 418} 419 420 421static int const sig[] = { 422#ifdef SIGHUP 423 SIGHUP, 424#endif 425#ifdef SIGINT 426 SIGINT, 427#endif 428#ifdef SIGPIPE 429 SIGPIPE, 430#endif 431#ifdef SIGQUIT 432 SIGQUIT, 433#endif 434#ifdef SIGTERM 435 SIGTERM, 436#endif 437#ifdef SIGXCPU 438 SIGXCPU, 439#endif 440#ifdef SIGXFSZ 441 SIGXFSZ, 442#endif 443}; 444#define SIGS (sizeof(sig)/sizeof(*sig)) 445 446 447#if has_sigaction 448 449 static void 450 check_sig(r) 451 int r; 452 { 453 if (r != 0) 454 efaterror("signal"); 455 } 456 457 static void 458 setup_catchsig() 459 { 460 register int i; 461 sigset_t blocked; 462 struct sigaction act; 463 464 check_sig(sigemptyset(&blocked)); 465 for (i=SIGS; 0<=--i; ) 466 check_sig(sigaddset(&blocked, sig[i])); 467 for (i=SIGS; 0<=--i; ) { 468 check_sig(sigaction(sig[i], (struct sigaction*)nil, &act)); 469 if (act.sa_handler != SIG_IGN) { 470 act.sa_handler = catchsig; 471 act.sa_mask = blocked; 472 check_sig(sigaction(sig[i], &act, (struct sigaction*)nil)); 473 } 474 } 475 } 476 477#else 478#if has_sigblock 479 480 static void 481 setup_catchsig() 482 { 483 register int i; 484 int mask; 485 486 mask = 0; 487 for (i=SIGS; 0<=--i; ) 488 mask |= sigmask(sig[i]); 489 mask = sigblock(mask); 490 for (i=SIGS; 0<=--i; ) 491 if ( 492 signal(sig[i], catchsig) == SIG_IGN && 493 signal(sig[i], SIG_IGN) != catchsig 494 ) 495 faterror("signal catcher failure"); 496 VOID sigsetmask(mask); 497 } 498 499#else 500 501 static void 502 setup_catchsig() 503 { 504 register i; 505 506 for (i=SIGS; 0<=--i; ) 507 if ( 508 signal(sig[i], SIG_IGN) != SIG_IGN && 509 signal(sig[i], catchsig) != SIG_IGN 510 ) 511 faterror("signal catcher failure"); 512 } 513 514#endif 515#endif 516 517 void 518catchints() 519{ 520 static int catching_ints; 521 if (!catching_ints) { 522 catching_ints = true; 523 setup_catchsig(); 524 } 525} 526 527#endif /* has_signal */ 528 529 530 void 531fastcopy(inf,outf) 532 register RILE *inf; 533 FILE *outf; 534/* Function: copies the remainder of file inf to outf. 535 */ 536{ 537#if large_memory 538# if has_mmap 539 awrite((char const*)inf->ptr, (size_t)(inf->lim - inf->ptr), outf); 540 inf->ptr = inf->lim; 541# else 542 for (;;) { 543 awrite((char const*)inf->ptr, (size_t)(inf->readlim - inf->ptr), outf); 544 inf->ptr = inf->readlim; 545 if (inf->ptr == inf->lim) 546 break; 547 VOID Igetmore(inf); 548 } 549# endif 550#else 551 char buf[BUFSIZ*8]; 552 register fread_type rcount; 553 554 /*now read the rest of the file in blocks*/ 555 while (!feof(inf)) { 556 if (!(rcount = Fread(buf,sizeof(*buf),sizeof(buf),inf))) { 557 testIerror(inf); 558 return; 559 } 560 awrite(buf, (size_t)rcount, outf); 561 } 562#endif 563} 564 565#ifndef SSIZE_MAX 566 /* This does not work in #ifs, but it's good enough for us. */ 567 /* Underestimating SSIZE_MAX may slow us down, but it won't break us. */ 568# define SSIZE_MAX ((unsigned)-1 >> 1) 569#endif 570 571 void 572awrite(buf, chars, f) 573 char const *buf; 574 size_t chars; 575 FILE *f; 576{ 577 /* Posix 1003.1-1990 ssize_t hack */ 578 while (SSIZE_MAX < chars) { 579 if (Fwrite(buf, sizeof(*buf), SSIZE_MAX, f) != SSIZE_MAX) 580 Oerror(); 581 buf += SSIZE_MAX; 582 chars -= SSIZE_MAX; 583 } 584 585 if (Fwrite(buf, sizeof(*buf), chars, f) != chars) 586 Oerror(); 587} 588 589 590 591 592 593 static int 594movefd(old, new) 595 int old, new; 596{ 597 if (old < 0 || old == new) 598 return old; 599# ifdef F_DUPFD 600 new = fcntl(old, F_DUPFD, new); 601# else 602 new = dup2(old, new); 603# endif 604 return close(old)==0 ? new : -1; 605} 606 607 static int 608fdreopen(fd, file, flags) 609 int fd; 610 char const *file; 611 int flags; 612{ 613 int newfd; 614 VOID close(fd); 615 newfd = 616#if !open_can_creat 617 flags&O_CREAT ? creat(file, S_IRUSR|S_IWUSR) : 618#endif 619 open(file, flags, S_IRUSR|S_IWUSR); 620 return movefd(newfd, fd); 621} 622 623#if !has_spawn 624 static void 625tryopen(fd,file,flags) 626 int fd, flags; 627 char const *file; 628{ 629 if (file && fdreopen(fd,file,flags) != fd) 630 efaterror(file); 631} 632#else 633 static int 634tryopen(fd,file,flags) 635 int fd, flags; 636 char const *file; 637{ 638 int newfd = -1; 639 if (file && ((newfd=dup(fd)) < 0 || fdreopen(fd,file,flags) != fd)) 640 efaterror(file); 641 return newfd; 642} 643 static void 644redirect(old, new) 645 int old, new; 646{ 647 if (0 <= old && (close(new) != 0 || movefd(old,new) < 0)) 648 efaterror("spawn I/O redirection"); 649} 650#endif 651 652 653 654#if !has_fork && !has_spawn 655 static void 656bufargcat(b, c, s) 657 register struct buf *b; 658 int c; 659 register char const *s; 660/* Append to B a copy of C, plus a quoted copy of S. */ 661{ 662 register char *p; 663 register char const *t; 664 size_t bl, sl; 665 666 for (t=s, sl=0; *t; ) 667 sl += 3*(*t++=='\'') + 1; 668 bl = strlen(b->string); 669 bufrealloc(b, bl + sl + 4); 670 p = b->string + bl; 671 *p++ = c; 672 *p++ = '\''; 673 while (*s) { 674 if (*s == '\'') { 675 *p++ = '\''; 676 *p++ = '\\'; 677 *p++ = '\''; 678 } 679 *p++ = *s++; 680 } 681 *p++ = '\''; 682 *p = 0; 683} 684#endif 685 686/* 687* Run a command specified by the strings in 'inoutargs'. 688* inoutargs[0], if nonnil, is the name of the input file. 689* inoutargs[1], if nonnil, is the name of the output file. 690* inoutargs[2..] form the command to be run. 691*/ 692 int 693runv(inoutargs) 694 char const **inoutargs; 695{ 696 register char const **p; 697 int wstatus; 698 699 oflush(); 700 eflush(); 701 { 702#if has_spawn 703 int in, out; 704 p = inoutargs; 705 in = tryopen(STDIN_FILENO, *p++, O_BINARY|O_RDONLY); 706 out = tryopen(STDOUT_FILENO, *p++, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY); 707 wstatus = spawn_RCS(0, *p, (char*const*)p); 708 if (wstatus == -1 && errno == ENOEXEC) { 709 *--p = RCS_SHELL; 710 wstatus = spawnv(0, *p, (char*const*)p); 711 } 712 redirect(in, STDIN_FILENO); 713 redirect(out, STDOUT_FILENO); 714#else 715#if has_fork 716 pid_t pid; 717# if !has_waitpid 718 pid_t w; 719# endif 720 if (!(pid = vfork())) { 721 p = inoutargs; 722 tryopen(STDIN_FILENO, *p++, O_BINARY|O_RDONLY); 723 tryopen(STDOUT_FILENO, *p++, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY); 724 VOID exec_RCS(*p, (char*const*)p); 725 if (errno == ENOEXEC) { 726 *--p = RCS_SHELL; 727 VOID execv(*p, (char*const*)p); 728 } 729 VOID write(STDERR_FILENO, *p, strlen(*p)); 730 VOID write(STDERR_FILENO, ": not found\n", 12); 731 _exit(EXIT_TROUBLE); 732 } 733 if (pid < 0) 734 efaterror("fork"); 735# if has_waitpid 736 if (waitpid(pid, &wstatus, 0) < 0) 737 efaterror("waitpid"); 738# else 739 do { 740 if ((w = wait(&wstatus)) < 0) 741 efaterror("wait"); 742 } while (w != pid); 743# endif 744#else 745 static struct buf b; 746 747 /* Use system(). On many hosts system() discards signals. Yuck! */ 748 p = inoutargs+2; 749 bufscpy(&b, *p); 750 while (*++p) 751 bufargcat(&b, ' ', *p); 752 if (inoutargs[0]) 753 bufargcat(&b, '<', inoutargs[0]); 754 if (inoutargs[1]) 755 bufargcat(&b, '>', inoutargs[1]); 756 wstatus = system(b.string); 757#endif 758#endif 759 } 760 if (!WIFEXITED(wstatus)) 761 faterror("%s failed", inoutargs[2]); 762 return WEXITSTATUS(wstatus); 763} 764 765#define CARGSMAX 20 766/* 767* Run a command. 768* The first two arguments are the input and output files (if nonnil); 769* the rest specify the command and its arguments. 770*/ 771 int 772#if has_prototypes 773run(char const *infile, char const *outfile, ...) 774#else 775 /*VARARGS2*/ 776run(infile, outfile, va_alist) 777 char const *infile; 778 char const *outfile; 779 va_dcl 780#endif 781{ 782 va_list ap; 783 char const *rgargs[CARGSMAX]; 784 register i = 0; 785 rgargs[0] = infile; 786 rgargs[1] = outfile; 787 vararg_start(ap, outfile); 788 for (i = 2; (rgargs[i++] = va_arg(ap, char const*)); ) 789 if (CARGSMAX <= i) 790 faterror("too many command arguments"); 791 va_end(ap); 792 return runv(rgargs); 793} 794 795 796 char const * 797date2str(date, datebuf) 798 char const date[datesize]; 799 char datebuf[datesize]; 800/* 801* Format a user-readable form of the RCS format DATE into the buffer DATEBUF. 802* Yield DATEBUF. 803*/ 804{ 805 register char const *p = date; 806 807 while (*p++ != '.') 808 ; 809 VOID sprintf(datebuf, 810 "19%.*s/%.2s/%.2s %.2s:%.2s:%s" + 811 (date[2]=='.' && VERSION(5)<=RCSversion ? 0 : 2), 812 (int)(p-date-1), date, 813 p, p+3, p+6, p+9, p+12 814 ); 815 return datebuf; 816} 817 818 819int RCSversion; 820 821 void 822setRCSversion(str) 823 char const *str; 824{ 825 static int oldversion; 826 827 register char const *s = str + 2; 828 int v = VERSION_DEFAULT; 829 830 if (oldversion) 831 redefined('V'); 832 oldversion = true; 833 834 if (*s) { 835 v = 0; 836 while (isdigit(*s)) 837 v = 10*v + *s++ - '0'; 838 if (*s) 839 faterror("%s isn't a number", str); 840 if (v < VERSION_min || VERSION_max < v) 841 faterror("%s out of range %d..%d", str, VERSION_min, VERSION_max); 842 } 843 844 RCSversion = VERSION(v); 845} 846 847 int 848getRCSINIT(argc, argv, newargv) 849 int argc; 850 char **argv, ***newargv; 851{ 852 register char *p, *q, **pp; 853 unsigned n; 854 855 if (!(q = cgetenv("RCSINIT"))) 856 *newargv = argv; 857 else { 858 n = argc + 2; 859 /* 860 * Count spaces in RCSINIT to allocate a new arg vector. 861 * This is an upper bound, but it's OK even if too large. 862 */ 863 for (p = q; ; ) { 864 switch (*p++) { 865 default: 866 continue; 867 868 case ' ': 869 case '\b': case '\f': case '\n': 870 case '\r': case '\t': case '\v': 871 n++; 872 continue; 873 874 case '\0': 875 break; 876 } 877 break; 878 } 879 *newargv = pp = tnalloc(char*, n); 880 *pp++ = *argv++; /* copy program name */ 881 for (p = q; ; ) { 882 for (;;) { 883 switch (*q) { 884 case '\0': 885 goto copyrest; 886 887 case ' ': 888 case '\b': case '\f': case '\n': 889 case '\r': case '\t': case '\v': 890 q++; 891 continue; 892 } 893 break; 894 } 895 *pp++ = p; 896 ++argc; 897 for (;;) { 898 switch ((*p++ = *q++)) { 899 case '\0': 900 goto copyrest; 901 902 case '\\': 903 if (!*q) 904 goto copyrest; 905 p[-1] = *q++; 906 continue; 907 908 default: 909 continue; 910 911 case ' ': 912 case '\b': case '\f': case '\n': 913 case '\r': case '\t': case '\v': 914 break; 915 } 916 break; 917 } 918 p[-1] = '\0'; 919 } 920 copyrest: 921 while ((*pp++ = *argv++)) 922 ; 923 } 924 return argc; 925} 926 927 928#define cacheid(E) static uid_t i; static int s; if (!s){ s=1; i=(E); } return i 929 930#if has_getuid 931 uid_t ruid() { cacheid(getuid()); } 932#endif 933#if has_setuid 934 uid_t euid() { cacheid(geteuid()); } 935#endif 936 937 938#if has_setuid 939 940/* 941 * Setuid execution really works only with Posix 1003.1a Draft 5 seteuid(), 942 * because it lets us switch back and forth between arbitrary users. 943 * If seteuid() doesn't work, we fall back on setuid(), 944 * which works if saved setuid is supported, 945 * unless the real or effective user is root. 946 * This area is such a mess that we always check switches at runtime. 947 */ 948 949 static void 950set_uid_to(u) 951 uid_t u; 952/* Become user u. */ 953{ 954 static int looping; 955 956 if (euid() == ruid()) 957 return; 958#if (has_fork||has_spawn) && DIFF_ABSOLUTE 959 if (seteuid(u) != 0) 960 efaterror("setuid"); 961#endif 962 if (geteuid() != u) { 963 if (looping) 964 return; 965 looping = true; 966 faterror("root setuid not supported" + (u?5:0)); 967 } 968} 969 970static int stick_with_euid; 971 972 void 973/* Ignore all calls to seteid() and setrid(). */ 974nosetid() 975{ 976 stick_with_euid = true; 977} 978 979 void 980seteid() 981/* Become effective user. */ 982{ 983 if (!stick_with_euid) 984 set_uid_to(euid()); 985} 986 987 void 988setrid() 989/* Become real user. */ 990{ 991 if (!stick_with_euid) 992 set_uid_to(ruid()); 993} 994#endif 995