1 /* $NetBSD: rcsfnms.c,v 1.5 1996/10/15 07:00:19 veego Exp $ */
3 /* RCS filename and pathname handling */
5 /****************************************************************************
6 * creation and deletion of /tmp temporaries
7 * pairing of RCS pathnames and working pathnames.
8 * Testprogram: define PAIRTEST
9 ****************************************************************************
12 /* Copyright 1982, 1988, 1989 Walter Tichy
13 Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
14 Distributed under license by the Free Software Foundation, Inc.
16 This file is part of RCS.
18 RCS is free software; you can redistribute it and/or modify
19 it under the terms of the GNU General Public License as published by
20 the Free Software Foundation; either version 2, or (at your option)
23 RCS is distributed in the hope that it will be useful,
24 but WITHOUT ANY WARRANTY; without even the implied warranty of
25 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 GNU General Public License for more details.
28 You should have received a copy of the GNU General Public License
29 along with RCS; see the file COPYING.
30 If not, write to the Free Software Foundation,
31 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
33 Report problems and direct all questions to:
35 rcs-bugs@cs.purdue.edu
44 * Revision 1.5 1996/10/15 07:00:19 veego
47 * Revision 5.16 1995/06/16 06:19:24 eggert
50 * Revision 5.15 1995/06/01 16:23:43 eggert
51 * (basefilename): Renamed from basename to avoid collisions.
52 * (dirlen): Remove (for similar reasons).
53 * (rcsreadopen): Open with FOPEN_RB.
54 * (SLASHSLASH_is_SLASH): Default is 0.
55 * (getcwd): Work around bad_wait_if_SIGCHLD_ignored bug.
57 * Revision 5.14 1994/03/17 14:05:48 eggert
58 * Strip trailing SLASHes from TMPDIR; some systems need this. Remove lint.
60 * Revision 5.13 1993/11/03 17:42:27 eggert
61 * Determine whether a file name is too long indirectly,
62 * by examining inode numbers, instead of trying to use operating system
63 * primitives like pathconf, which are not trustworthy in general.
64 * File names may now hold white space or $.
65 * Do not flatten ../X in pathnames; that may yield wrong answer for symlinks.
66 * Add getabsname hook. Improve quality of diagnostics.
68 * Revision 5.12 1992/07/28 16:12:44 eggert
69 * Add .sty. .pl now implies Perl, not Prolog. Fix fdlock initialization bug.
70 * Check that $PWD is really ".". Be consistent about pathnames vs filenames.
72 * Revision 5.11 1992/02/17 23:02:25 eggert
73 * `a/RCS/b/c' is now an RCS file with an empty extension, not just `a/b/RCS/c'.
75 * Revision 5.10 1992/01/24 18:44:19 eggert
76 * Fix bug: Expand and Ignored weren't reinitialized.
77 * Avoid `char const c=ch;' compiler bug.
78 * Add support for bad_creat0.
80 * Revision 5.9 1992/01/06 02:42:34 eggert
81 * Shorten long (>31 chars) name.
82 * while (E) ; -> while (E) continue;
84 * Revision 5.8 1991/09/24 00:28:40 eggert
85 * Don't export bindex().
87 * Revision 5.7 1991/08/19 03:13:55 eggert
88 * Fix messages when rcswriteopen fails.
89 * Look in $TMP and $TEMP if $TMPDIR isn't set. Tune.
91 * Revision 5.6 1991/04/21 11:58:23 eggert
92 * Fix errno bugs. Add -x, RCSINIT, MS-DOS support.
94 * Revision 5.5 1991/02/26 17:48:38 eggert
95 * Fix setuid bug. Support new link behavior.
96 * Define more portable getcwd().
98 * Revision 5.4 1990/11/01 05:03:43 eggert
99 * Permit arbitrary data in comment leaders.
101 * Revision 5.3 1990/09/14 22:56:16 hammer
102 * added more filename extensions and their comment leaders
104 * Revision 5.2 1990/09/04 08:02:23 eggert
105 * Fix typo when !RCSSEP.
107 * Revision 5.1 1990/08/29 07:13:59 eggert
108 * Work around buggy compilers with defective argument promotion.
110 * Revision 5.0 1990/08/22 08:12:50 eggert
111 * Ignore signals when manipulating the semaphore file.
112 * Modernize list of filename extensions.
113 * Permit paths of arbitrary length. Beware filenames beginning with "-".
114 * Remove compile-time limits; use malloc instead.
115 * Permit dates past 1999/12/31. Make lock and temp files faster and safer.
116 * Ansify and Posixate.
117 * Don't use access(). Fix test for non-regular files. Tune.
119 * Revision 4.8 89/05/01 15:09:41 narten
120 * changed getwd to not stat empty directories.
122 * Revision 4.7 88/08/09 19:12:53 eggert
123 * Fix troff macro comment leader bug; add Prolog; allow cc -R; remove lint.
125 * Revision 4.6 87/12/18 11:40:23 narten
126 * additional file types added from 4.3 BSD version, and SPARC assembler
127 * comment character added. Also, more lint cleanups. (Guy Harris)
129 * Revision 4.5 87/10/18 10:34:16 narten
130 * Updating version numbers. Changes relative to 1.1 actually relative
133 * Revision 1.3 87/03/27 14:22:21 jenkins
136 * Revision 1.2 85/06/26 07:34:28 svb
137 * Comment leader '% ' for '*.tex' files added.
139 * Revision 4.3 83/12/15 12:26:48 wft
140 * Added check for KDELIM in filenames to pairfilenames().
142 * Revision 4.2 83/12/02 22:47:45 wft
143 * Added csh, red, and sl filename suffixes.
145 * Revision 4.1 83/05/11 16:23:39 wft
146 * Added initialization of Dbranch to InitAdmin(). Canged pairfilenames():
147 * 1. added copying of path from workfile to RCS file, if RCS file is omitted;
148 * 2. added getting the file status of RCS and working files;
149 * 3. added ignoring of directories.
151 * Revision 3.7 83/05/11 15:01:58 wft
152 * Added comtable[] which pairs filename suffixes with comment leaders;
153 * updated InitAdmin() accordingly.
155 * Revision 3.6 83/04/05 14:47:36 wft
156 * fixed Suffix in InitAdmin().
158 * Revision 3.5 83/01/17 18:01:04 wft
159 * Added getwd() and rename(); these can be removed by defining
160 * V4_2BSD, since they are not needed in 4.2 bsd.
161 * Changed sys/param.h to sys/types.h.
163 * Revision 3.4 82/12/08 21:55:20 wft
164 * removed unused variable.
166 * Revision 3.3 82/11/28 20:31:37 wft
167 * Changed mktempfile() to store the generated filenames.
168 * Changed getfullRCSname() to store the file and pathname, and to
169 * delete leading "../" and "./".
171 * Revision 3.2 82/11/12 14:29:40 wft
172 * changed pairfilenames() to handle file.sfx,v; also deleted checkpathnosfx(),
173 * checksuffix(), checkfullpath(). Semaphore name generation updated.
174 * mktempfile() now checks for nil path; freefilename initialized properly.
175 * Added Suffix .h to InitAdmin. Added testprogram PAIRTEST.
176 * Moved rmsema, trysema, trydiraccess, getfullRCSname from rcsutil.c to here.
178 * Revision 3.1 82/10/18 14:51:28 wft
179 * InitAdmin() now initializes StrictLocks=STRICT_LOCKING (def. in rcsbase.h).
180 * renamed checkpath() to checkfullpath().
186 libId(fnmsId
, "Id: rcsfnms.c,v 5.16 1995/06/16 06:19:24 eggert Exp")
188 static char const *bindex
P((char const*,int));
189 static int fin2open
P((char const*, size_t, char const*, size_t, char const*, size_t, RILE
*(*)P((struct buf
*,struct stat
*,int)), int));
190 static int finopen
P((RILE
*(*)P((struct buf
*,struct stat
*,int)), int));
191 static int suffix_matches
P((char const*,char const*));
192 static size_t dir_useful_len
P((char const*));
193 static size_t suffixlen
P((char const*));
194 static void InitAdmin
P((void));
201 char const *suffixes
;
203 static char const rcsdir
[] = "RCS";
204 #define rcslen (sizeof(rcsdir)-1)
206 static struct buf RCSbuf
, RCSb
;
210 /* Temp names to be unlinked when done, if they are not 0. */
211 #define TEMPNAMES 5 /* must be at least DIRTEMPNAMES (see rcsedit.c) */
212 static char *volatile tpnames
[TEMPNAMES
];
216 char const *suffix
, *comlead
;
220 * This table is present only for backwards compatibility.
221 * Normally we ignore this table, and use the prefix of the `$Log' line instead.
223 static struct compair
const comtable
[] = {
224 { "a" , "-- " }, /* Ada */
228 { "asm" , ";; " }, /* assembler (MS-DOS) */
229 { "bat" , ":: " }, /* batch (MS-DOS) */
230 { "body", "-- " }, /* Ada */
231 { "c" , " * " }, /* C */
232 { "c++" , "// " }, /* C++ in all its infinite guises */
236 { "cl" , ";;; "}, /* Common Lisp */
237 { "cmd" , ":: " }, /* command (OS/2) */
238 { "cmf" , "c " }, /* CM Fortran */
239 { "cs" , " * " }, /* C* */
240 { "el" , "; " }, /* Emacs Lisp */
241 { "f" , "c " }, /* Fortran */
243 { "h" , " * " }, /* C-header */
244 { "hpp" , "// " }, /* C++ header */
246 { "l" , " * " }, /* lex (NOTE: franzlisp disagrees) */
247 { "lisp", ";;; "}, /* Lucid Lisp */
248 { "lsp" , ";; " }, /* Microsoft Lisp */
249 { "m" , "// " }, /* Objective C */
250 { "mac" , ";; " }, /* macro (DEC-10, MS-DOS, PDP-11, VMS, etc) */
251 { "me" , ".\\\" "}, /* troff -me */
252 { "ml" , "; " }, /* mocklisp */
253 { "mm" , ".\\\" "}, /* troff -mm */
254 { "ms" , ".\\\" "}, /* troff -ms */
255 { "p" , " * " }, /* Pascal */
257 { "ps" , "% " }, /* PostScript */
258 { "spec", "-- " }, /* Ada */
259 { "sty" , "% " }, /* LaTeX style */
260 { "tex" , "% " }, /* TeX */
261 { "y" , " * " }, /* yacc */
262 { 0 , "# " } /* default for unknown suffix; must be last */
266 static char const *tmp
P((void));
269 /* Yield the name of the tmp directory. */
271 static char const *s
;
273 && !(s
= cgetenv("TMPDIR")) /* Unix tradition */
274 && !(s
= cgetenv("TMP")) /* DOS tradition */
275 && !(s
= cgetenv("TEMP")) /* another DOS tradition */
285 /* Create a unique pathname using n and the process id and store it
286 * into the nth slot in tpnames.
287 * Because of storage in tpnames, tempunlink() can unlink the file later.
288 * Return a pointer to the pathname created.
292 char const *t
= tpnames
[n
];
303 char const *tp
= tmp();
304 size_t tplen
= dir_useful_len(tp
);
305 p
= testalloc(tplen
+ 10);
306 VOID
sprintf(p
, "%.*s%cT%cXXXXXX", (int)tplen
, tp
, SLASH
, '0'+n
);
308 if ((fd
= mkstemp(p
)) == -1)
310 if (!mktemp(p
) || !*p
)
312 faterror("can't make temporary pathname `%.*s%cT%cXXXXXX'",
313 (int)tplen
, tp
, SLASH
, '0'+n
319 static char tpnamebuf
[TEMPNAMES
][L_tmpnam
];
321 if (!tmpnam(p
) || !*p
)
323 faterror("can't make temporary pathname `%s...'",P_tmpdir
);
325 faterror("can't make temporary pathname");
336 /* Clean up maketemp() files. May be invoked by signal handler.
342 for (i
= TEMPNAMES
; 0 <= --i
; )
343 if ((p
= tpnames
[i
])) {
346 * We would tfree(p) here,
347 * but this might dump core if we're handing a signal.
348 * We're about to exit anyway, so we won't bother.
357 register char const *sp
;
359 /* Function: Finds the last occurrence of character c in string sp
360 * and returns a pointer to the character just beyond it. If the
361 * character doesn't occur in the string, sp is returned.
364 register char const *r
;
367 if (*sp
++ == c
) r
=sp
;
375 suffix_matches(suffix
, pattern
)
376 register char const *suffix
, *pattern
;
382 switch (*suffix
++ - (c
= *pattern
++)) {
389 if (ctab
[c
] == Letter
)
400 /* function: initializes an admin node */
402 register char const *Suffix
;
405 Head
=0; Dbranch
=0; AccessList
=0; Symbols
=0; Locks
=0;
406 StrictLocks
=STRICT_LOCKING
;
408 /* guess the comment leader from the suffix*/
409 Suffix
= bindex(workname
, '.');
410 if (Suffix
==workname
) Suffix
= ""; /* empty suffix; will get default*/
411 for (i
=0; !suffix_matches(Suffix
,comtable
[i
].suffix
); i
++)
413 Comment
.string
= comtable
[i
].comlead
;
414 Comment
.size
= strlen(comtable
[i
].comlead
);
415 Expand
= KEYVAL_EXPAND
;
417 Lexinit(); /* note: if !finptr, reads nothing; only initializes */
424 register struct buf
*b
;
426 /* Ensure *B is a name buffer of at least SIZE bytes.
427 * *B's old contents can be freed; *B's new contents are undefined.
430 if (b
->size
< size
) {
434 b
->size
= sizeof(malloc_type
);
435 while (b
->size
< size
)
437 b
->string
= tnalloc(char, b
->size
);
443 register struct buf
*b
;
445 /* like bufalloc, except *B's old contents, if any, are preserved */
447 if (b
->size
< size
) {
451 while ((b
->size
<<= 1) < size
)
453 b
->string
= trealloc(char, b
->string
, b
->size
);
461 /* Free an auto buffer at block exit. */
472 * Free the buffer B with used size S.
473 * Yield a cbuf with identical contents.
474 * The cbuf will be reclaimed when this input file is finished.
480 cb
.string
= fremember(trealloc(char, b
->string
, s
));
482 bufautoend(b
); /* not really auto */
490 register struct buf
*b
;
492 /* Make *B larger. Set *ALIM to its new limit, and yield the relocated value
497 bufrealloc(b
, s
+ 1);
498 *alim
= b
->string
+ b
->size
;
499 return b
->string
+ s
;
506 /* Concatenate S to B's end. */
508 size_t blen
= b
->string
? strlen(b
->string
) : 0;
509 bufrealloc(b
, blen
+strlen(s
)+1);
510 VOID
strcpy(b
->string
+blen
, s
);
519 bufalloc(b
, strlen(s
)+1);
520 VOID
strcpy(b
->string
, s
);
527 /* Yield the address of the base filename of the pathname P. */
529 register char const *b
= p
, *q
= p
;
532 case SLASHes
: b
= q
; break;
541 /* Yield the length of X, an RCS pathname suffix. */
543 register char const *p
;
548 case 0: case SLASHes
:
560 /* Yield the suffix of NAME if it is an RCS pathname, 0 otherwise. */
562 char const *x
, *p
, *nz
;
569 if ((xl
= suffixlen(x
))) {
570 if (xl
<= nl
&& memcmp(p
= nz
-xl
, x
, xl
) == 0)
573 for (p
= name
; p
< nz
- rcslen
; p
++)
576 && (p
==name
|| isSLASH(p
[-1]))
577 && memcmp(p
, rcsdir
, rcslen
) == 0
586 rcsreadopen(RCSpath
, status
, mustread
)
590 /* Open RCSPATH for reading and yield its FILE* descriptor.
591 * If successful, set *STATUS to its status.
592 * Pass this routine to pairnames() for read-only access to the file. */
594 return Iopen(RCSpath
->string
, FOPEN_RB
, status
);
598 finopen(rcsopen
, mustread
)
599 RILE
*(*rcsopen
)P((struct buf
*,struct stat
*,int));
602 * Use RCSOPEN to open an RCS file; MUSTREAD is set if the file must be read.
603 * Set finptr to the result and yield true if successful.
604 * RCSb holds the file's name.
605 * Set RCSbuf to the best RCS name found so far, and RCSerrno to its errno.
606 * Yield true if successful or if an unusual failure.
609 int interesting
, preferold
;
612 * We prefer an old name to that of a nonexisting new RCS file,
613 * unless we tried locking the old name and failed.
615 preferold
= RCSbuf
.string
[0] && (mustread
||0<=fdlock
);
617 finptr
= (*rcsopen
)(&RCSb
, &RCSstat
, mustread
);
618 interesting
= finptr
|| errno
!=ENOENT
;
619 if (interesting
|| !preferold
) {
620 /* Use the new name. */
622 bufscpy(&RCSbuf
, RCSb
.string
);
628 fin2open(d
, dlen
, base
, baselen
, x
, xlen
, rcsopen
, mustread
)
629 char const *d
, *base
, *x
;
630 size_t dlen
, baselen
, xlen
;
631 RILE
*(*rcsopen
)P((struct buf
*,struct stat
*,int));
634 * D is a directory name with length DLEN (including trailing slash).
635 * BASE is a filename with length BASELEN.
636 * X is an RCS pathname suffix with length XLEN.
637 * Use RCSOPEN to open an RCS file; MUSTREAD is set if the file must be read.
638 * Yield true if successful.
639 * Try dRCS/basex first; if that fails and x is nonempty, try dbasex.
640 * Put these potential names in RCSb.
641 * Set RCSbuf to the best RCS name found so far, and RCSerrno to its errno.
642 * Yield true if successful or if an unusual failure.
647 bufalloc(&RCSb
, dlen
+ rcslen
+ 1 + baselen
+ xlen
+ 1);
649 /* Try dRCS/basex. */
650 VOID
memcpy(p
= RCSb
.string
, d
, dlen
);
651 VOID
memcpy(p
+= dlen
, rcsdir
, rcslen
);
654 VOID
memcpy(p
, base
, baselen
);
655 VOID
memcpy(p
+= baselen
, x
, xlen
);
658 if (finopen(rcsopen
, mustread
))
662 /* Start from scratch, because finopen() may have changed RCSb. */
663 VOID
memcpy(p
= RCSb
.string
, d
, dlen
);
664 VOID
memcpy(p
+= dlen
, base
, baselen
);
665 VOID
memcpy(p
+= baselen
, x
, xlen
);
668 return finopen(rcsopen
, mustread
);
672 pairnames(argc
, argv
, rcsopen
, mustread
, quiet
)
675 RILE
*(*rcsopen
)P((struct buf
*,struct stat
*,int));
678 * Pair the pathnames pointed to by argv; argc indicates
679 * how many there are.
680 * Place a pointer to the RCS pathname into RCSname,
681 * and a pointer to the pathname of the working file into workname.
682 * If both are given, and workstdout
683 * is set, a warning is printed.
685 * If the RCS file exists, places its status into RCSstat.
687 * If the RCS file exists, it is RCSOPENed for reading, the file pointer
688 * is placed into finptr, and the admin-node is read in; returns 1.
689 * If the RCS file does not exist and MUSTREAD,
690 * print an error unless QUIET and return 0.
691 * Otherwise, initialize the admin node and return -1.
693 * 0 is returned on all errors, e.g. files that are not regular files.
696 static struct buf tempbuf
;
698 register char *p
, *arg
, *RCS1
;
699 char const *base
, *RCSbase
, *x
;
701 size_t arglen
, dlen
, baselen
, xlen
;
705 if (!(arg
= *argv
)) return 0; /* already paired pathname */
707 error("%s option is ignored after pathnames", arg
);
711 base
= basefilename(arg
);
714 /* first check suffix to see whether it is an RCS file or not */
715 if ((x
= rcssuffix(arg
)))
717 /* RCS pathname given */
723 !rcssuffix(workname
= p
= argv
[1]) &&
724 baselen
<= (arglen
= strlen(p
)) &&
725 ((p
+=arglen
-baselen
) == workname
|| isSLASH(p
[-1])) &&
726 memcmp(base
, p
, baselen
) == 0
731 bufscpy(&tempbuf
, base
);
732 workname
= p
= tempbuf
.string
;
736 /* working file given; now try to find RCS file */
738 baselen
= strlen(base
);
739 /* Derive RCS pathname. */
742 (x
= rcssuffix(RCS1
= argv
[1])) &&
743 baselen
<= x
- RCS1
&&
744 ((RCSbase
=x
-baselen
)==RCS1
|| isSLASH(RCSbase
[-1])) &&
745 memcmp(base
, RCSbase
, baselen
) == 0
752 /* Now we have a (tentative) RCS pathname in RCS1 and workname. */
753 /* Second, try to find the right RCS file */
755 /* a path for RCSfile is given; single RCS file to look for */
756 bufscpy(&RCSbuf
, RCS1
);
757 finptr
= (*rcsopen
)(&RCSbuf
, &RCSstat
, mustread
);
760 bufscpy(&RCSbuf
, "");
762 /* RCS filename was given without path. */
763 VOID
fin2open(arg
, (size_t)0, RCSbase
, baselen
,
764 x
, strlen(x
), rcsopen
, mustread
767 /* No RCS pathname was given. */
768 /* Try each suffix in turn. */
771 while (! fin2open(arg
, dlen
, base
, baselen
,
772 x
, xlen
=suffixlen(x
), rcsopen
, mustread
780 RCSname
= p
= RCSbuf
.string
;
782 if (!S_ISREG(RCSstat
.st_mode
)) {
783 error("%s isn't a regular file -- ignored", p
);
786 Lexinit(); getadmin();
788 if (RCSerrno
!=ENOENT
|| mustread
|| fdlock
<0) {
789 if (RCSerrno
== EEXIST
)
790 error("RCS file %s is in use", p
);
791 else if (!quiet
|| RCSerrno
!=ENOENT
)
792 enerror(RCSerrno
, p
);
798 if (paired
&& workstdout
)
799 workwarn("Working file ignored due to -p option");
802 return finptr
? 1 : -1;
809 * Return a pointer to the full pathname of the RCS file.
810 * Remove leading `./'.
813 if (ROOTPATH(RCSname
)) {
816 static struct buf rcsbuf
;
817 # if needs_getabsname
818 bufalloc(&rcsbuf
, SIZEABLE_PATH
+ 1);
819 while (getabsname(RCSname
, rcsbuf
.string
, rcsbuf
.size
) != 0)
821 bufalloc(&rcsbuf
, rcsbuf
.size
<<1);
823 efaterror("getabsname");
825 static char const *wdptr
;
826 static struct buf wdbuf
;
829 register char const *r
;
830 register size_t dlen
;
832 register char const *wd
;
835 /* Get working directory for the first time. */
836 char *PWD
= cgetenv("PWD");
837 struct stat PWDstat
, dotstat
;
841 stat(PWD
, &PWDstat
) == 0 &&
842 stat(".", &dotstat
) == 0 &&
843 same_file(PWDstat
, dotstat
, 1)
845 bufalloc(&wdbuf
, SIZEABLE_PATH
+ 1);
846 # if has_getcwd || !has_getwd
847 while (!(d
= getcwd(wdbuf
.string
, wdbuf
.size
)))
849 bufalloc(&wdbuf
, wdbuf
.size
<<1);
855 d
= getwd(wdbuf
.string
);
856 if (!d
&& !(d
= PWD
))
860 wdlen
= dir_useful_len(d
);
865 * Remove leading `./'s from RCSname.
866 * Do not try to handle `../', since removing it may yield
867 * the wrong answer in the presence of symbolic links.
869 for (r
= RCSname
; r
[0]=='.' && isSLASH(r
[1]); r
+= 2)
870 /* `.////' is equivalent to `./'. */
871 while (isSLASH(r
[2]))
873 /* Build full pathname. */
875 bufalloc(&rcsbuf
, dlen
+ strlen(r
) + 2);
877 VOID
memcpy(d
, wd
, dlen
);
882 return rcsbuf
.string
;
890 * D names a directory; yield the number of characters of D's useful part.
891 * To create a file in D, append a SLASH and a file name to D's useful part.
892 * Ignore trailing slashes if possible; not only are they ugly,
893 * but some non-Posix systems misbehave unless the slashes are omitted.
896 # ifndef SLASHSLASH_is_SLASH
897 # define SLASHSLASH_is_SLASH 0
899 size_t dlen
= strlen(d
);
900 if (!SLASHSLASH_is_SLASH
&& dlen
==2 && isSLASH(d
[0]) && isSLASH(d
[1]))
903 while (dlen
&& isSLASH(d
[dlen
-1]))
923 #if !has_getcwd && !has_getwd
930 static char const usrbinpwd
[] = "/usr/bin/pwd";
931 # define binpwd (usrbinpwd+4)
935 register char *p
, *lim
;
936 int closeerrno
, closeerror
, e
, fd
[2], readerror
, toolong
, wstatus
;
945 # if bad_wait_if_SIGCHLD_ignored
947 # define SIGCHLD SIGCLD
949 VOID
signal(SIGCHLD
, SIG_DFL
);
951 if (!(child
= vfork())) {
954 (fd
[1] == STDOUT_FILENO
||
956 (VOID
close(STDOUT_FILENO
),
957 fcntl(fd
[1], F_DUPFD
, STDOUT_FILENO
))
959 dup2(fd
[1], STDOUT_FILENO
)
965 VOID
close(STDERR_FILENO
);
966 VOID
execl(binpwd
, binpwd
, (char *)0);
967 VOID
execl(usrbinpwd
, usrbinpwd
, (char *)0);
972 closeerror
= close(fd
[1]);
975 readerror
= toolong
= wstatus
= 0;
978 fp
= fdopen(fd
[0], "r");
982 for (p
= path
; ; *p
++ = c
) {
983 if ((c
=getc(fp
)) < 0) {
999 if (waitpid(child
, &wstatus
, 0) < 0)
1005 if ((w
= wait(&wstatus
)) < 0) {
1009 } while (w
!= child
);
1018 if (fclose(fp
) != 0)
1032 if (wstatus
|| p
== path
|| *--p
!= '\n') {
1043 /* test program for pairnames() and getfullRCSname() */
1045 char const cmdid
[] = "pair";
1048 int argc
; char *argv
[];
1052 quietflag
= initflag
= false;
1054 while(--argc
, ++argv
, argc
>=1 && ((*argv
)[0] == '-')) {
1055 switch ((*argv
)[1]) {
1057 case 'p': workstdout
= stdout
;
1059 case 'i': initflag
=true;
1061 case 'q': quietflag
=true;
1063 default: error("unknown option: %s", *argv
);
1069 RCSname
= workname
= 0;
1070 result
= pairnames(argc
,argv
,rcsreadopen
,!initflag
,quietflag
);
1072 diagnose("RCS pathname: %s; working pathname: %s\nFull RCS pathname: %s\n",
1073 RCSname
, workname
, getfullRCSname()
1077 case 0: continue; /* already paired file */
1079 case 1: if (initflag
) {
1080 rcserror("already exists");
1082 diagnose("RCS file %s exists\n", RCSname
);
1087 case -1:diagnose("RCS file doesn't exist\n");
1091 } while (++argv
, --argc
>=1);
1100 _exit(EXIT_FAILURE
);