No empty .Rs/.Re
[netbsd-mini2440.git] / gnu / usr.bin / rcs / lib / rcsfnms.c
blobcce48417e66230a3344a71da6455ab020b809b66
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)
21 any later version.
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
43 * $Log: rcsfnms.c,v $
44 * Revision 1.5 1996/10/15 07:00:19 veego
45 * Merge rcs 5.7.
47 * Revision 5.16 1995/06/16 06:19:24 eggert
48 * Update FSF address.
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
131 * to verion 4.3
133 * Revision 1.3 87/03/27 14:22:21 jenkins
134 * Port to suns
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().
184 #include "rcsbase.h"
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));
196 char const *RCSname;
197 char *workname;
198 int fdlock;
199 FILE *workstdout;
200 struct stat RCSstat;
201 char const *suffixes;
203 static char const rcsdir[] = "RCS";
204 #define rcslen (sizeof(rcsdir)-1)
206 static struct buf RCSbuf, RCSb;
207 static int RCSerrno;
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];
215 struct compair {
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 */
225 { "ada" , "-- " },
226 { "adb" , "-- " },
227 { "ads" , "-- " },
228 { "asm" , ";; " }, /* assembler (MS-DOS) */
229 { "bat" , ":: " }, /* batch (MS-DOS) */
230 { "body", "-- " }, /* Ada */
231 { "c" , " * " }, /* C */
232 { "c++" , "// " }, /* C++ in all its infinite guises */
233 { "cc" , "// " },
234 { "cpp" , "// " },
235 { "cxx" , "// " },
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 */
242 { "for" , "c " },
243 { "h" , " * " }, /* C-header */
244 { "hpp" , "// " }, /* C++ header */
245 { "hxx" , "// " },
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 */
256 { "pas" , " * " },
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 */
265 #if has_mktemp
266 static char const *tmp P((void));
267 static char const *
268 tmp()
269 /* Yield the name of the tmp directory. */
271 static char const *s;
272 if (!s
273 && !(s = cgetenv("TMPDIR")) /* Unix tradition */
274 && !(s = cgetenv("TMP")) /* DOS tradition */
275 && !(s = cgetenv("TEMP")) /* another DOS tradition */
277 s = TMPDIR;
278 return s;
280 #endif
282 char const *
283 maketemp(n)
284 int n;
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.
291 char *p;
292 char const *t = tpnames[n];
294 if (t)
295 return t;
297 catchints();
299 # if has_mktemp
300 # if has_mkstemp
301 int fd;
302 # endif
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);
307 # if has_mkstemp
308 if ((fd = mkstemp(p)) == -1)
309 # else
310 if (!mktemp(p) || !*p)
311 # endif
312 faterror("can't make temporary pathname `%.*s%cT%cXXXXXX'",
313 (int)tplen, tp, SLASH, '0'+n
315 # if has_mkstemp
316 close(fd);
317 # endif
318 # else
319 static char tpnamebuf[TEMPNAMES][L_tmpnam];
320 p = tpnamebuf[n];
321 if (!tmpnam(p) || !*p)
322 # ifdef P_tmpdir
323 faterror("can't make temporary pathname `%s...'",P_tmpdir);
324 # else
325 faterror("can't make temporary pathname");
326 # endif
327 # endif
330 tpnames[n] = p;
331 return p;
334 void
335 tempunlink()
336 /* Clean up maketemp() files. May be invoked by signal handler.
339 register int i;
340 register char *p;
342 for (i = TEMPNAMES; 0 <= --i; )
343 if ((p = tpnames[i])) {
344 VOID unlink(p);
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.
350 tpnames[i] = 0;
355 static char const *
356 bindex(sp, c)
357 register char const *sp;
358 register int c;
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;
365 r = sp;
366 while (*sp) {
367 if (*sp++ == c) r=sp;
369 return r;
374 static int
375 suffix_matches(suffix, pattern)
376 register char const *suffix, *pattern;
378 register int c;
379 if (!pattern)
380 return true;
381 for (;;)
382 switch (*suffix++ - (c = *pattern++)) {
383 case 0:
384 if (!c)
385 return true;
386 break;
388 case 'A'-'a':
389 if (ctab[c] == Letter)
390 break;
391 /* fall into */
392 default:
393 return false;
398 static void
399 InitAdmin()
400 /* function: initializes an admin node */
402 register char const *Suffix;
403 register int i;
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++)
412 continue;
413 Comment.string = comtable[i].comlead;
414 Comment.size = strlen(comtable[i].comlead);
415 Expand = KEYVAL_EXPAND;
416 clear_buf(&Ignored);
417 Lexinit(); /* note: if !finptr, reads nothing; only initializes */
422 void
423 bufalloc(b, size)
424 register struct buf *b;
425 size_t size;
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) {
431 if (b->size)
432 tfree(b->string);
433 else
434 b->size = sizeof(malloc_type);
435 while (b->size < size)
436 b->size <<= 1;
437 b->string = tnalloc(char, b->size);
441 void
442 bufrealloc(b, size)
443 register struct buf *b;
444 size_t size;
445 /* like bufalloc, except *B's old contents, if any, are preserved */
447 if (b->size < size) {
448 if (!b->size)
449 bufalloc(b, size);
450 else {
451 while ((b->size <<= 1) < size)
452 continue;
453 b->string = trealloc(char, b->string, b->size);
458 void
459 bufautoend(b)
460 struct buf *b;
461 /* Free an auto buffer at block exit. */
463 if (b->size)
464 tfree(b->string);
467 struct cbuf
468 bufremember(b, s)
469 struct buf *b;
470 size_t s;
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.
477 struct cbuf cb;
479 if ((cb.size = s))
480 cb.string = fremember(trealloc(char, b->string, s));
481 else {
482 bufautoend(b); /* not really auto */
483 cb.string = "";
485 return cb;
488 char *
489 bufenlarge(b, alim)
490 register struct buf *b;
491 char const **alim;
492 /* Make *B larger. Set *ALIM to its new limit, and yield the relocated value
493 * of its old limit.
496 size_t s = b->size;
497 bufrealloc(b, s + 1);
498 *alim = b->string + b->size;
499 return b->string + s;
502 void
503 bufscat(b, s)
504 struct buf *b;
505 char const *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);
513 void
514 bufscpy(b, s)
515 struct buf *b;
516 char const *s;
517 /* Copy S into B. */
519 bufalloc(b, strlen(s)+1);
520 VOID strcpy(b->string, s);
524 char const *
525 basefilename(p)
526 char const *p;
527 /* Yield the address of the base filename of the pathname P. */
529 register char const *b = p, *q = p;
530 for (;;)
531 switch (*q++) {
532 case SLASHes: b = q; break;
533 case 0: return b;
538 static size_t
539 suffixlen(x)
540 char const *x;
541 /* Yield the length of X, an RCS pathname suffix. */
543 register char const *p;
545 p = x;
546 for (;;)
547 switch (*p) {
548 case 0: case SLASHes:
549 return p - x;
551 default:
552 ++p;
553 continue;
557 char const *
558 rcssuffix(name)
559 char const *name;
560 /* Yield the suffix of NAME if it is an RCS pathname, 0 otherwise. */
562 char const *x, *p, *nz;
563 size_t nl, xl;
565 nl = strlen(name);
566 nz = name + nl;
567 x = suffixes;
568 do {
569 if ((xl = suffixlen(x))) {
570 if (xl <= nl && memcmp(p = nz-xl, x, xl) == 0)
571 return p;
572 } else
573 for (p = name; p < nz - rcslen; p++)
574 if (
575 isSLASH(p[rcslen])
576 && (p==name || isSLASH(p[-1]))
577 && memcmp(p, rcsdir, rcslen) == 0
579 return nz;
580 x += xl;
581 } while (*x++);
582 return 0;
585 /*ARGSUSED*/ RILE *
586 rcsreadopen(RCSpath, status, mustread)
587 struct buf *RCSpath;
588 struct stat *status;
589 int 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);
597 static int
598 finopen(rcsopen, mustread)
599 RILE *(*rcsopen)P((struct buf*,struct stat*,int));
600 int mustread;
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. */
621 RCSerrno = errno;
622 bufscpy(&RCSbuf, RCSb.string);
624 return interesting;
627 static int
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));
632 int mustread;
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.
645 register char *p;
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);
652 p += rcslen;
653 *p++ = SLASH;
654 VOID memcpy(p, base, baselen);
655 VOID memcpy(p += baselen, x, xlen);
656 p[xlen] = 0;
657 if (xlen) {
658 if (finopen(rcsopen, mustread))
659 return true;
661 /* Try dbasex. */
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);
666 p[xlen] = 0;
668 return finopen(rcsopen, mustread);
672 pairnames(argc, argv, rcsopen, mustread, quiet)
673 int argc;
674 char **argv;
675 RILE *(*rcsopen)P((struct buf*,struct stat*,int));
676 int mustread, quiet;
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;
700 int paired;
701 size_t arglen, dlen, baselen, xlen;
703 fdlock = -1;
705 if (!(arg = *argv)) return 0; /* already paired pathname */
706 if (*arg == '-') {
707 error("%s option is ignored after pathnames", arg);
708 return 0;
711 base = basefilename(arg);
712 paired = false;
714 /* first check suffix to see whether it is an RCS file or not */
715 if ((x = rcssuffix(arg)))
717 /* RCS pathname given */
718 RCS1 = arg;
719 RCSbase = base;
720 baselen = x - base;
721 if (
722 1 < argc &&
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
728 argv[1] = 0;
729 paired = true;
730 } else {
731 bufscpy(&tempbuf, base);
732 workname = p = tempbuf.string;
733 p[baselen] = 0;
735 } else {
736 /* working file given; now try to find RCS file */
737 workname = arg;
738 baselen = strlen(base);
739 /* Derive RCS pathname. */
740 if (
741 1 < argc &&
742 (x = rcssuffix(RCS1 = argv[1])) &&
743 baselen <= x - RCS1 &&
744 ((RCSbase=x-baselen)==RCS1 || isSLASH(RCSbase[-1])) &&
745 memcmp(base, RCSbase, baselen) == 0
747 argv[1] = 0;
748 paired = true;
749 } else
750 RCSbase = RCS1 = 0;
752 /* Now we have a (tentative) RCS pathname in RCS1 and workname. */
753 /* Second, try to find the right RCS file */
754 if (RCSbase!=RCS1) {
755 /* a path for RCSfile is given; single RCS file to look for */
756 bufscpy(&RCSbuf, RCS1);
757 finptr = (*rcsopen)(&RCSbuf, &RCSstat, mustread);
758 RCSerrno = errno;
759 } else {
760 bufscpy(&RCSbuf, "");
761 if (RCS1)
762 /* RCS filename was given without path. */
763 VOID fin2open(arg, (size_t)0, RCSbase, baselen,
764 x, strlen(x), rcsopen, mustread
766 else {
767 /* No RCS pathname was given. */
768 /* Try each suffix in turn. */
769 dlen = base-arg;
770 x = suffixes;
771 while (! fin2open(arg, dlen, base, baselen,
772 x, xlen=suffixlen(x), rcsopen, mustread
773 )) {
774 x += xlen;
775 if (!*x++)
776 break;
780 RCSname = p = RCSbuf.string;
781 if (finptr) {
782 if (!S_ISREG(RCSstat.st_mode)) {
783 error("%s isn't a regular file -- ignored", p);
784 return 0;
786 Lexinit(); getadmin();
787 } else {
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);
793 return 0;
795 InitAdmin();
798 if (paired && workstdout)
799 workwarn("Working file ignored due to -p option");
801 prevkeys = false;
802 return finptr ? 1 : -1;
806 char const *
807 getfullRCSname()
809 * Return a pointer to the full pathname of the RCS file.
810 * Remove leading `./'.
813 if (ROOTPATH(RCSname)) {
814 return RCSname;
815 } else {
816 static struct buf rcsbuf;
817 # if needs_getabsname
818 bufalloc(&rcsbuf, SIZEABLE_PATH + 1);
819 while (getabsname(RCSname, rcsbuf.string, rcsbuf.size) != 0)
820 if (errno == ERANGE)
821 bufalloc(&rcsbuf, rcsbuf.size<<1);
822 else
823 efaterror("getabsname");
824 # else
825 static char const *wdptr;
826 static struct buf wdbuf;
827 static size_t wdlen;
829 register char const *r;
830 register size_t dlen;
831 register char *d;
832 register char const *wd;
834 if (!(wd = wdptr)) {
835 /* Get working directory for the first time. */
836 char *PWD = cgetenv("PWD");
837 struct stat PWDstat, dotstat;
838 if (! (
839 (d = PWD) &&
840 ROOTPATH(PWD) &&
841 stat(PWD, &PWDstat) == 0 &&
842 stat(".", &dotstat) == 0 &&
843 same_file(PWDstat, dotstat, 1)
844 )) {
845 bufalloc(&wdbuf, SIZEABLE_PATH + 1);
846 # if has_getcwd || !has_getwd
847 while (!(d = getcwd(wdbuf.string, wdbuf.size)))
848 if (errno == ERANGE)
849 bufalloc(&wdbuf, wdbuf.size<<1);
850 else if ((d = PWD))
851 break;
852 else
853 efaterror("getcwd");
854 # else
855 d = getwd(wdbuf.string);
856 if (!d && !(d = PWD))
857 efaterror("getwd");
858 # endif
860 wdlen = dir_useful_len(d);
861 d[wdlen] = 0;
862 wdptr = wd = 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]))
872 r++;
873 /* Build full pathname. */
874 dlen = wdlen;
875 bufalloc(&rcsbuf, dlen + strlen(r) + 2);
876 d = rcsbuf.string;
877 VOID memcpy(d, wd, dlen);
878 d += dlen;
879 *d++ = SLASH;
880 VOID strcpy(d, r);
881 # endif
882 return rcsbuf.string;
886 static size_t
887 dir_useful_len(d)
888 char const *d;
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
898 # endif
899 size_t dlen = strlen(d);
900 if (!SLASHSLASH_is_SLASH && dlen==2 && isSLASH(d[0]) && isSLASH(d[1]))
901 --dlen;
902 else
903 while (dlen && isSLASH(d[dlen-1]))
904 --dlen;
905 return dlen;
908 #ifndef isSLASH
910 isSLASH(c)
911 int c;
913 switch (c) {
914 case SLASHes:
915 return true;
916 default:
917 return false;
920 #endif
923 #if !has_getcwd && !has_getwd
925 char *
926 getcwd(path, size)
927 char *path;
928 size_t size;
930 static char const usrbinpwd[] = "/usr/bin/pwd";
931 # define binpwd (usrbinpwd+4)
933 register FILE *fp;
934 register int c;
935 register char *p, *lim;
936 int closeerrno, closeerror, e, fd[2], readerror, toolong, wstatus;
937 pid_t child;
939 if (!size) {
940 errno = EINVAL;
941 return 0;
943 if (pipe(fd) != 0)
944 return 0;
945 # if bad_wait_if_SIGCHLD_ignored
946 # ifndef SIGCHLD
947 # define SIGCHLD SIGCLD
948 # endif
949 VOID signal(SIGCHLD, SIG_DFL);
950 # endif
951 if (!(child = vfork())) {
952 if (
953 close(fd[0]) == 0 &&
954 (fd[1] == STDOUT_FILENO ||
955 # ifdef F_DUPFD
956 (VOID close(STDOUT_FILENO),
957 fcntl(fd[1], F_DUPFD, STDOUT_FILENO))
958 # else
959 dup2(fd[1], STDOUT_FILENO)
960 # endif
961 == STDOUT_FILENO &&
962 close(fd[1]) == 0
965 VOID close(STDERR_FILENO);
966 VOID execl(binpwd, binpwd, (char *)0);
967 VOID execl(usrbinpwd, usrbinpwd, (char *)0);
969 _exit(EXIT_FAILURE);
971 e = errno;
972 closeerror = close(fd[1]);
973 closeerrno = errno;
974 fp = 0;
975 readerror = toolong = wstatus = 0;
976 p = path;
977 if (0 <= child) {
978 fp = fdopen(fd[0], "r");
979 e = errno;
980 if (fp) {
981 lim = p + size;
982 for (p = path; ; *p++ = c) {
983 if ((c=getc(fp)) < 0) {
984 if (feof(fp))
985 break;
986 if (ferror(fp)) {
987 readerror = 1;
988 e = errno;
989 break;
992 if (p == lim) {
993 toolong = 1;
994 break;
998 # if has_waitpid
999 if (waitpid(child, &wstatus, 0) < 0)
1000 wstatus = 1;
1001 # else
1003 pid_t w;
1004 do {
1005 if ((w = wait(&wstatus)) < 0) {
1006 wstatus = 1;
1007 break;
1009 } while (w != child);
1011 # endif
1013 if (!fp) {
1014 VOID close(fd[0]);
1015 errno = e;
1016 return 0;
1018 if (fclose(fp) != 0)
1019 return 0;
1020 if (readerror) {
1021 errno = e;
1022 return 0;
1024 if (closeerror) {
1025 errno = closeerrno;
1026 return 0;
1028 if (toolong) {
1029 errno = ERANGE;
1030 return 0;
1032 if (wstatus || p == path || *--p != '\n') {
1033 errno = EACCES;
1034 return 0;
1036 *p = '\0';
1037 return path;
1039 #endif
1042 #ifdef PAIRTEST
1043 /* test program for pairnames() and getfullRCSname() */
1045 char const cmdid[] = "pair";
1047 main(argc, argv)
1048 int argc; char *argv[];
1050 int result;
1051 int initflag;
1052 quietflag = initflag = false;
1054 while(--argc, ++argv, argc>=1 && ((*argv)[0] == '-')) {
1055 switch ((*argv)[1]) {
1057 case 'p': workstdout = stdout;
1058 break;
1059 case 'i': initflag=true;
1060 break;
1061 case 'q': quietflag=true;
1062 break;
1063 default: error("unknown option: %s", *argv);
1064 break;
1068 do {
1069 RCSname = workname = 0;
1070 result = pairnames(argc,argv,rcsreadopen,!initflag,quietflag);
1071 if (result!=0) {
1072 diagnose("RCS pathname: %s; working pathname: %s\nFull RCS pathname: %s\n",
1073 RCSname, workname, getfullRCSname()
1076 switch (result) {
1077 case 0: continue; /* already paired file */
1079 case 1: if (initflag) {
1080 rcserror("already exists");
1081 } else {
1082 diagnose("RCS file %s exists\n", RCSname);
1084 Ifclose(finptr);
1085 break;
1087 case -1:diagnose("RCS file doesn't exist\n");
1088 break;
1091 } while (++argv, --argc>=1);
1095 void
1096 exiterr()
1098 dirtempunlink();
1099 tempunlink();
1100 _exit(EXIT_FAILURE);
1102 #endif