1 /* RCS filename and pathname handling */
3 /****************************************************************************
4 * creation and deletion of /tmp temporaries
5 * pairing of RCS pathnames and working pathnames.
6 * Testprogram: define PAIRTEST
7 ****************************************************************************
10 /* Copyright 1982, 1988, 1989 Walter Tichy
11 Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
12 Distributed under license by the Free Software Foundation, Inc.
14 This file is part of RCS.
16 RCS is free software; you can redistribute it and/or modify
17 it under the terms of the GNU General Public License as published by
18 the Free Software Foundation; either version 2, or (at your option)
21 RCS is distributed in the hope that it will be useful,
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 GNU General Public License for more details.
26 You should have received a copy of the GNU General Public License
27 along with RCS; see the file COPYING.
28 If not, write to the Free Software Foundation,
29 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
31 Report problems and direct all questions to:
33 rcs-bugs@cs.purdue.edu
41 * $FreeBSD: src/gnu/usr.bin/rcs/lib/rcsfnms.c,v 1.10.2.1 2001/05/12 10:29:43 kris Exp $
42 * $DragonFly: src/gnu/usr.bin/rcs/lib/rcsfnms.c,v 1.2 2003/06/17 04:25:47 dillon Exp $
44 * Revision 5.16 1995/06/16 06:19:24 eggert
47 * Revision 5.15 1995/06/01 16:23:43 eggert
48 * (basefilename): Renamed from basename to avoid collisions.
49 * (dirlen): Remove (for similar reasons).
50 * (rcsreadopen): Open with FOPEN_RB.
51 * (SLASHSLASH_is_SLASH): Default is 0.
52 * (getcwd): Work around bad_wait_if_SIGCHLD_ignored bug.
54 * Revision 5.14 1994/03/17 14:05:48 eggert
55 * Strip trailing SLASHes from TMPDIR; some systems need this. Remove lint.
57 * Revision 5.13 1993/11/03 17:42:27 eggert
58 * Determine whether a file name is too long indirectly,
59 * by examining inode numbers, instead of trying to use operating system
60 * primitives like pathconf, which are not trustworthy in general.
61 * File names may now hold white space or $.
62 * Do not flatten ../X in pathnames; that may yield wrong answer for symlinks.
63 * Add getabsname hook. Improve quality of diagnostics.
65 * Revision 5.12 1992/07/28 16:12:44 eggert
66 * Add .sty. .pl now implies Perl, not Prolog. Fix fdlock initialization bug.
67 * Check that $PWD is really ".". Be consistent about pathnames vs filenames.
69 * Revision 5.11 1992/02/17 23:02:25 eggert
70 * `a/RCS/b/c' is now an RCS file with an empty extension, not just `a/b/RCS/c'.
72 * Revision 5.10 1992/01/24 18:44:19 eggert
73 * Fix bug: Expand and Ignored weren't reinitialized.
74 * Avoid `char const c=ch;' compiler bug.
75 * Add support for bad_creat0.
77 * Revision 5.9 1992/01/06 02:42:34 eggert
78 * Shorten long (>31 chars) name.
79 * while (E) ; -> while (E) continue;
81 * Revision 5.8 1991/09/24 00:28:40 eggert
82 * Don't export bindex().
84 * Revision 5.7 1991/08/19 03:13:55 eggert
85 * Fix messages when rcswriteopen fails.
86 * Look in $TMP and $TEMP if $TMPDIR isn't set. Tune.
88 * Revision 5.6 1991/04/21 11:58:23 eggert
89 * Fix errno bugs. Add -x, RCSINIT, MS-DOS support.
91 * Revision 5.5 1991/02/26 17:48:38 eggert
92 * Fix setuid bug. Support new link behavior.
93 * Define more portable getcwd().
95 * Revision 5.4 1990/11/01 05:03:43 eggert
96 * Permit arbitrary data in comment leaders.
98 * Revision 5.3 1990/09/14 22:56:16 hammer
99 * added more filename extensions and their comment leaders
101 * Revision 5.2 1990/09/04 08:02:23 eggert
102 * Fix typo when !RCSSEP.
104 * Revision 5.1 1990/08/29 07:13:59 eggert
105 * Work around buggy compilers with defective argument promotion.
107 * Revision 5.0 1990/08/22 08:12:50 eggert
108 * Ignore signals when manipulating the semaphore file.
109 * Modernize list of filename extensions.
110 * Permit paths of arbitrary length. Beware filenames beginning with "-".
111 * Remove compile-time limits; use malloc instead.
112 * Permit dates past 1999/12/31. Make lock and temp files faster and safer.
113 * Ansify and Posixate.
114 * Don't use access(). Fix test for non-regular files. Tune.
116 * Revision 4.8 89/05/01 15:09:41 narten
117 * changed getwd to not stat empty directories.
119 * Revision 4.7 88/08/09 19:12:53 eggert
120 * Fix troff macro comment leader bug; add Prolog; allow cc -R; remove lint.
122 * Revision 4.6 87/12/18 11:40:23 narten
123 * additional file types added from 4.3 BSD version, and SPARC assembler
124 * comment character added. Also, more lint cleanups. (Guy Harris)
126 * Revision 4.5 87/10/18 10:34:16 narten
127 * Updating version numbers. Changes relative to 1.1 actually relative
130 * Revision 1.3 87/03/27 14:22:21 jenkins
133 * Revision 1.2 85/06/26 07:34:28 svb
134 * Comment leader '% ' for '*.tex' files added.
136 * Revision 4.3 83/12/15 12:26:48 wft
137 * Added check for KDELIM in filenames to pairfilenames().
139 * Revision 4.2 83/12/02 22:47:45 wft
140 * Added csh, red, and sl filename suffixes.
142 * Revision 4.1 83/05/11 16:23:39 wft
143 * Added initialization of Dbranch to InitAdmin(). Canged pairfilenames():
144 * 1. added copying of path from workfile to RCS file, if RCS file is omitted;
145 * 2. added getting the file status of RCS and working files;
146 * 3. added ignoring of directories.
148 * Revision 3.7 83/05/11 15:01:58 wft
149 * Added comtable[] which pairs filename suffixes with comment leaders;
150 * updated InitAdmin() accordingly.
152 * Revision 3.6 83/04/05 14:47:36 wft
153 * fixed Suffix in InitAdmin().
155 * Revision 3.5 83/01/17 18:01:04 wft
156 * Added getwd() and rename(); these can be removed by defining
157 * V4_2BSD, since they are not needed in 4.2 bsd.
158 * Changed sys/param.h to sys/types.h.
160 * Revision 3.4 82/12/08 21:55:20 wft
161 * removed unused variable.
163 * Revision 3.3 82/11/28 20:31:37 wft
164 * Changed mktempfile() to store the generated filenames.
165 * Changed getfullRCSname() to store the file and pathname, and to
166 * delete leading "../" and "./".
168 * Revision 3.2 82/11/12 14:29:40 wft
169 * changed pairfilenames() to handle file.sfx,v; also deleted checkpathnosfx(),
170 * checksuffix(), checkfullpath(). Semaphore name generation updated.
171 * mktempfile() now checks for nil path; freefilename initialized properly.
172 * Added Suffix .h to InitAdmin. Added testprogram PAIRTEST.
173 * Moved rmsema, trysema, trydiraccess, getfullRCSname from rcsutil.c to here.
175 * Revision 3.1 82/10/18 14:51:28 wft
176 * InitAdmin() now initializes StrictLocks=STRICT_LOCKING (def. in rcsbase.h).
177 * renamed checkpath() to checkfullpath().
183 libId(fnmsId, "$DragonFly: src/gnu/usr.bin/rcs/lib/rcsfnms.c,v 1.2 2003/06/17 04:25:47 dillon Exp $")
185 static char const *bindex P((char const*,int));
186 static int fin2open P((char const*, size_t, char const*, size_t, char const*, size_t, RILE*(*)P((struct buf*,struct stat*,int)), int));
187 static int finopen P((RILE*(*)P((struct buf*,struct stat*,int)), int));
188 static int suffix_matches P((char const*,char const*));
189 static size_t dir_useful_len P((char const*));
190 static size_t suffixlen P((char const*));
191 static void InitAdmin P((void));
198 char const *suffixes;
200 static char const rcsdir[] = "RCS";
201 #define rcslen (sizeof(rcsdir)-1)
203 static struct buf RCSbuf, RCSb;
207 /* Temp names to be unlinked when done, if they are not 0. */
208 #define TEMPNAMES 5 /* must be at least DIRTEMPNAMES (see rcsedit.c) */
209 static char *volatile tpnames[TEMPNAMES];
213 char const *suffix, *comlead;
217 * This table is present only for backwards compatibility.
218 * Normally we ignore this table, and use the prefix of the `$Log' line instead.
220 static struct compair const comtable[] = {
221 { "a" , "-- " }, /* Ada */
225 { "asm" , ";; " }, /* assembler (MS-DOS) */
226 { "bat" , ":: " }, /* batch (MS-DOS) */
227 { "body", "-- " }, /* Ada */
228 { "c" , " * " }, /* C */
229 { "c++" , "// " }, /* C++ in all its infinite guises */
233 { "cl" , ";;; "}, /* Common Lisp */
234 { "cmd" , ":: " }, /* command (OS/2) */
235 { "cmf" , "c " }, /* CM Fortran */
236 { "cs" , " * " }, /* C* */
237 { "el" , "; " }, /* Emacs Lisp */
238 { "f" , "c " }, /* Fortran */
240 { "h" , " * " }, /* C-header */
241 { "hpp" , "// " }, /* C++ header */
243 { "l" , " * " }, /* lex (NOTE: franzlisp disagrees) */
244 { "lisp", ";;; "}, /* Lucid Lisp */
245 { "lsp" , ";; " }, /* Microsoft Lisp */
246 { "m" , "// " }, /* Objective C */
247 { "mac" , ";; " }, /* macro (DEC-10, MS-DOS, PDP-11, VMS, etc) */
248 { "me" , ".\\\" "}, /* troff -me */
249 { "ml" , "; " }, /* mocklisp */
250 { "mm" , ".\\\" "}, /* troff -mm */
251 { "ms" , ".\\\" "}, /* troff -ms */
252 { "p" , " * " }, /* Pascal */
254 { "ps" , "% " }, /* PostScript */
255 { "spec", "-- " }, /* Ada */
256 { "sty" , "% " }, /* LaTeX style */
257 { "tex" , "% " }, /* TeX */
258 { "y" , " * " }, /* yacc */
259 { 0 , "# " } /* default for unknown suffix; must be last */
263 static char const *tmp P((void));
266 /* Yield the name of the tmp directory. */
268 static char const *s;
270 && !(s = cgetenv("TMPDIR")) /* Unix tradition */
271 && !(s = cgetenv("TMP")) /* DOS tradition */
272 && !(s = cgetenv("TEMP")) /* another DOS tradition */
282 /* Create a unique pathname using n and the process id and store it
283 * into the nth slot in tpnames.
284 * Because of storage in tpnames, tempunlink() can unlink the file later.
285 * Return a pointer to the pathname created.
289 char const *t = tpnames[n];
300 char const *tp = tmp();
301 size_t tplen = dir_useful_len(tp);
302 p = testalloc(tplen + 10);
303 VOID sprintf(p, "%.*s%cT%cXXXXXX", (int)tplen, tp, SLASH, '0'+n);
306 faterror("can't make temporary pathname `%.*s%cT%cXXXXXX'",
307 (int)tplen, tp, SLASH, '0'+n
311 static char tpnamebuf[TEMPNAMES][L_tmpnam];
313 if (!tmpnam(p) || !*p)
315 faterror("can't make temporary pathname `%s...'",P_tmpdir);
317 faterror("can't make temporary pathname");
328 /* Clean up maketemp() files. May be invoked by signal handler.
334 for (i = TEMPNAMES; 0 <= --i; )
335 if ((p = tpnames[i])) {
338 * We would tfree(p) here,
339 * but this might dump core if we're handing a signal.
340 * We're about to exit anyway, so we won't bother.
349 register char const *sp;
351 /* Function: Finds the last occurrence of character c in string sp
352 * and returns a pointer to the character just beyond it. If the
353 * character doesn't occur in the string, sp is returned.
356 register char const *r;
359 if (*sp++ == c) r=sp;
367 suffix_matches(suffix, pattern)
368 register char const *suffix, *pattern;
374 switch (*suffix++ - (c = *pattern++)) {
381 if (ctab[c] == Letter)
392 /* function: initializes an admin node */
394 register char const *Suffix;
397 Head=0; Dbranch=0; AccessList=0; Symbols=0; Locks=0;
398 StrictLocks=STRICT_LOCKING;
400 /* guess the comment leader from the suffix*/
401 Suffix = bindex(workname, '.');
402 if (Suffix==workname) Suffix= ""; /* empty suffix; will get default*/
403 for (i=0; !suffix_matches(Suffix,comtable[i].suffix); i++)
405 Comment.string = comtable[i].comlead;
406 Comment.size = strlen(comtable[i].comlead);
407 Expand = KEYVAL_EXPAND;
409 Lexinit(); /* note: if !finptr, reads nothing; only initializes */
416 register struct buf *b;
418 /* Ensure *B is a name buffer of at least SIZE bytes.
419 * *B's old contents can be freed; *B's new contents are undefined.
422 if (b->size < size) {
426 b->size = sizeof(malloc_type);
427 while (b->size < size)
429 b->string = tnalloc(char, b->size);
435 register struct buf *b;
437 /* like bufalloc, except *B's old contents, if any, are preserved */
439 if (b->size < size) {
443 while ((b->size <<= 1) < size)
445 b->string = trealloc(char, b->string, b->size);
453 /* Free an auto buffer at block exit. */
464 * Free the buffer B with used size S.
465 * Yield a cbuf with identical contents.
466 * The cbuf will be reclaimed when this input file is finished.
472 cb.string = fremember(trealloc(char, b->string, s));
474 bufautoend(b); /* not really auto */
482 register struct buf *b;
484 /* Make *B larger. Set *ALIM to its new limit, and yield the relocated value
489 bufrealloc(b, s + 1);
490 *alim = b->string + b->size;
491 return b->string + s;
498 /* Concatenate S to B's end. */
500 size_t blen = b->string ? strlen(b->string) : 0;
501 bufrealloc(b, blen+strlen(s)+1);
502 VOID strcpy(b->string+blen, s);
511 bufalloc(b, strlen(s)+1);
512 VOID strcpy(b->string, s);
519 /* Yield the address of the base filename of the pathname P. */
521 register char const *b = p, *q = p;
524 case SLASHes: b = q; break;
533 /* Yield the length of X, an RCS pathname suffix. */
535 register char const *p;
540 case 0: case SLASHes:
552 /* Yield the suffix of NAME if it is an RCS pathname, 0 otherwise. */
554 char const *x, *p, *nz;
561 if ((xl = suffixlen(x))) {
562 if (xl <= nl && memcmp(p = nz-xl, x, xl) == 0)
565 for (p = name; p < nz - rcslen; p++)
568 && (p==name || isSLASH(p[-1]))
569 && memcmp(p, rcsdir, rcslen) == 0
578 rcsreadopen(RCSpath, status, mustread)
582 /* Open RCSPATH for reading and yield its FILE* descriptor.
583 * If successful, set *STATUS to its status.
584 * Pass this routine to pairnames() for read-only access to the file. */
586 return Iopen(RCSpath->string, FOPEN_RB, status);
590 finopen(rcsopen, mustread)
591 RILE *(*rcsopen)P((struct buf*,struct stat*,int));
594 * Use RCSOPEN to open an RCS file; MUSTREAD is set if the file must be read.
595 * Set finptr to the result and yield true if successful.
596 * RCSb holds the file's name.
597 * Set RCSbuf to the best RCS name found so far, and RCSerrno to its errno.
598 * Yield true if successful or if an unusual failure.
601 int interesting, preferold;
604 * We prefer an old name to that of a nonexisting new RCS file,
605 * unless we tried locking the old name and failed.
607 preferold = RCSbuf.string[0] && (mustread||0<=fdlock);
609 finptr = (*rcsopen)(&RCSb, &RCSstat, mustread);
610 interesting = finptr || errno!=ENOENT;
611 if (interesting || !preferold) {
612 /* Use the new name. */
614 bufscpy(&RCSbuf, RCSb.string);
620 fin2open(d, dlen, base, baselen, x, xlen, rcsopen, mustread)
621 char const *d, *base, *x;
622 size_t dlen, baselen, xlen;
623 RILE *(*rcsopen)P((struct buf*,struct stat*,int));
626 * D is a directory name with length DLEN (including trailing slash).
627 * BASE is a filename with length BASELEN.
628 * X is an RCS pathname suffix with length XLEN.
629 * Use RCSOPEN to open an RCS file; MUSTREAD is set if the file must be read.
630 * Yield true if successful.
631 * Try dRCS/basex first; if that fails and x is nonempty, try dbasex.
632 * Put these potential names in RCSb.
633 * Set RCSbuf to the best RCS name found so far, and RCSerrno to its errno.
634 * Yield true if successful or if an unusual failure.
639 bufalloc(&RCSb, dlen + rcslen + 1 + baselen + xlen + 1);
641 /* Try dRCS/basex. */
642 VOID memcpy(p = RCSb.string, d, dlen);
643 VOID memcpy(p += dlen, rcsdir, rcslen);
646 VOID memcpy(p, base, baselen);
647 VOID memcpy(p += baselen, x, xlen);
650 if (finopen(rcsopen, mustread))
654 /* Start from scratch, because finopen() may have changed RCSb. */
655 VOID memcpy(p = RCSb.string, d, dlen);
656 VOID memcpy(p += dlen, base, baselen);
657 VOID memcpy(p += baselen, x, xlen);
660 return finopen(rcsopen, mustread);
664 pairnames(argc, argv, rcsopen, mustread, quiet)
667 RILE *(*rcsopen)P((struct buf*,struct stat*,int));
670 * Pair the pathnames pointed to by argv; argc indicates
671 * how many there are.
672 * Place a pointer to the RCS pathname into RCSname,
673 * and a pointer to the pathname of the working file into workname.
674 * If both are given, and workstdout
675 * is set, a warning is printed.
677 * If the RCS file exists, places its status into RCSstat.
679 * If the RCS file exists, it is RCSOPENed for reading, the file pointer
680 * is placed into finptr, and the admin-node is read in; returns 1.
681 * If the RCS file does not exist and MUSTREAD,
682 * print an error unless QUIET and return 0.
683 * Otherwise, initialize the admin node and return -1.
685 * 0 is returned on all errors, e.g. files that are not regular files.
688 static struct buf tempbuf;
690 register char *p, *arg, *RCS1;
691 char const *base, *RCSbase, *x;
693 size_t arglen, dlen, baselen, xlen;
697 if (!(arg = *argv)) return 0; /* already paired pathname */
699 error("%s option is ignored after pathnames", arg);
703 base = basefilename(arg);
706 /* first check suffix to see whether it is an RCS file or not */
707 if ((x = rcssuffix(arg)))
709 /* RCS pathname given */
715 !rcssuffix(workname = p = argv[1]) &&
716 baselen <= (arglen = strlen(p)) &&
717 ((p+=arglen-baselen) == workname || isSLASH(p[-1])) &&
718 memcmp(base, p, baselen) == 0
723 bufscpy(&tempbuf, base);
724 workname = p = tempbuf.string;
728 /* working file given; now try to find RCS file */
730 baselen = strlen(base);
731 /* Derive RCS pathname. */
734 (x = rcssuffix(RCS1 = argv[1])) &&
735 baselen <= x - RCS1 &&
736 ((RCSbase=x-baselen)==RCS1 || isSLASH(RCSbase[-1])) &&
737 memcmp(base, RCSbase, baselen) == 0
744 /* Now we have a (tentative) RCS pathname in RCS1 and workname. */
745 /* Second, try to find the right RCS file */
747 /* a path for RCSfile is given; single RCS file to look for */
748 bufscpy(&RCSbuf, RCS1);
749 finptr = (*rcsopen)(&RCSbuf, &RCSstat, mustread);
752 bufscpy(&RCSbuf, "");
754 /* RCS filename was given without path. */
755 VOID fin2open(arg, (size_t)0, RCSbase, baselen,
756 x, strlen(x), rcsopen, mustread
759 /* No RCS pathname was given. */
760 /* Try each suffix in turn. */
763 while (! fin2open(arg, dlen, base, baselen,
764 x, xlen=suffixlen(x), rcsopen, mustread
772 RCSname = p = RCSbuf.string;
774 if (!S_ISREG(RCSstat.st_mode)) {
775 error("%s isn't a regular file -- ignored", p);
778 Lexinit(); getadmin();
780 if (RCSerrno!=ENOENT || mustread || fdlock<0) {
781 if (RCSerrno == EEXIST)
782 error("RCS file %s is in use", p);
783 else if (!quiet || RCSerrno!=ENOENT)
784 enerror(RCSerrno, p);
790 if (paired && workstdout)
791 workwarn("Working file ignored due to -p option");
794 return finptr ? 1 : -1;
801 * Return a pointer to the full pathname of the RCS file.
802 * Remove leading `./'.
805 if (ROOTPATH(RCSname)) {
808 static struct buf rcsbuf;
809 # if needs_getabsname
810 bufalloc(&rcsbuf, SIZEABLE_PATH + 1);
811 while (getabsname(RCSname, rcsbuf.string, rcsbuf.size) != 0)
813 bufalloc(&rcsbuf, rcsbuf.size<<1);
815 efaterror("getabsname");
817 static char const *wdptr;
818 static struct buf wdbuf;
821 register char const *r;
822 register size_t dlen;
824 register char const *wd;
827 /* Get working directory for the first time. */
828 char *PWD = cgetenv("PWD");
829 struct stat PWDstat, dotstat;
833 stat(PWD, &PWDstat) == 0 &&
834 stat(".", &dotstat) == 0 &&
835 same_file(PWDstat, dotstat, 1)
837 bufalloc(&wdbuf, SIZEABLE_PATH + 1);
838 # if has_getcwd || !has_getwd
839 while (!(d = getcwd(wdbuf.string, wdbuf.size)))
841 bufalloc(&wdbuf, wdbuf.size<<1);
847 d = getwd(wdbuf.string);
848 if (!d && !(d = PWD))
852 wdlen = dir_useful_len(d);
857 * Remove leading `./'s from RCSname.
858 * Do not try to handle `../', since removing it may yield
859 * the wrong answer in the presence of symbolic links.
861 for (r = RCSname; r[0]=='.' && isSLASH(r[1]); r += 2)
862 /* `.////' is equivalent to `./'. */
863 while (isSLASH(r[2]))
865 /* Build full pathname. */
867 bufalloc(&rcsbuf, dlen + strlen(r) + 2);
869 VOID memcpy(d, wd, dlen);
874 return rcsbuf.string;
878 /* Derived from code from the XFree86 project */
881 /* Function: returns a pointer to the path name of the RCS file with the
882 * CVSROOT part stripped off, and with 'Attic/' stripped off (if present).
886 #define ATTICDIR "/Attic"
888 char const *namebuf = getfullRCSname();
889 char *cvsroot = cgetenv("CVSROOT");
892 int alen = strlen(ATTICDIR);
894 if ((c = strrchr(namebuf, '/')) != NULL) {
895 if (namebuf - c >= alen) {
896 if (!strncmp(c - alen, ATTICDIR, alen)) {
910 cvsrootlen = strlen(cvsroot);
911 if (!strncmp(namebuf, cvsroot, cvsrootlen) &&
912 namebuf[cvsrootlen] == '/')
913 return(namebuf + cvsrootlen + 1);
923 * D names a directory; yield the number of characters of D's useful part.
924 * To create a file in D, append a SLASH and a file name to D's useful part.
925 * Ignore trailing slashes if possible; not only are they ugly,
926 * but some non-Posix systems misbehave unless the slashes are omitted.
929 # ifndef SLASHSLASH_is_SLASH
930 # define SLASHSLASH_is_SLASH 0
932 size_t dlen = strlen(d);
933 if (!SLASHSLASH_is_SLASH && dlen==2 && isSLASH(d[0]) && isSLASH(d[1]))
936 while (dlen && isSLASH(d[dlen-1]))
956 #if !has_getcwd && !has_getwd
963 static char const usrbinpwd[] = "/usr/bin/pwd";
964 # define binpwd (usrbinpwd+4)
968 register char *p, *lim;
969 int closeerrno, closeerror, e, fd[2], readerror, toolong, wstatus;
978 # if bad_wait_if_SIGCHLD_ignored
980 # define SIGCHLD SIGCLD
982 VOID signal(SIGCHLD, SIG_DFL);
984 if (!(child = vfork())) {
987 (fd[1] == STDOUT_FILENO ||
989 (VOID close(STDOUT_FILENO),
990 fcntl(fd[1], F_DUPFD, STDOUT_FILENO))
992 dup2(fd[1], STDOUT_FILENO)
998 VOID close(STDERR_FILENO);
999 VOID execl(binpwd, binpwd, (char *)0);
1000 VOID execl(usrbinpwd, usrbinpwd, (char *)0);
1002 _exit(EXIT_FAILURE);
1005 closeerror = close(fd[1]);
1008 readerror = toolong = wstatus = 0;
1011 fp = fdopen(fd[0], "r");
1015 for (p = path; ; *p++ = c) {
1016 if ((c=getc(fp)) < 0) {
1032 if (waitpid(child, &wstatus, 0) < 0)
1038 if ((w = wait(&wstatus)) < 0) {
1042 } while (w != child);
1051 if (fclose(fp) != 0)
1065 if (wstatus || p == path || *--p != '\n') {
1076 /* test program for pairnames() and getfullRCSname() */
1078 char const cmdid[] = "pair";
1081 int argc; char *argv[];
1085 quietflag = initflag = false;
1087 while(--argc, ++argv, argc>=1 && ((*argv)[0] == '-')) {
1088 switch ((*argv)[1]) {
1090 case 'p': workstdout = stdout;
1092 case 'i': initflag=true;
1094 case 'q': quietflag=true;
1096 default: error("unknown option: %s", *argv);
1102 RCSname = workname = 0;
1103 result = pairnames(argc,argv,rcsreadopen,!initflag,quietflag);
1105 diagnose("RCS pathname: %s; working pathname: %s\nFull RCS pathname: %s\n",
1106 RCSname, workname, getfullRCSname()
1110 case 0: continue; /* already paired file */
1112 case 1: if (initflag) {
1113 rcserror("already exists");
1115 diagnose("RCS file %s exists\n", RCSname);
1120 case -1:diagnose("RCS file doesn't exist\n");
1124 } while (++argv, --argc>=1);
1133 _exit(EXIT_FAILURE);