1 /* RCS filename and pathname handling */
2
3 /****************************************************************************
4 * creation and deletion of /tmp temporaries
5 * pairing of RCS pathnames and working pathnames.
6 * Testprogram: define PAIRTEST
7 ****************************************************************************
8 */
9
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.
13
14 This file is part of RCS.
15
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)
19 any later version.
20
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.
25
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.
30
31 Report problems and direct all questions to:
32
33 rcs-bugs@cs.purdue.edu
34
35 */
36
37
38
39
40 /*
41 * Revision 5.16 1995/06/16 06:19:24 eggert
42 * Update FSF address.
43 *
44 * Revision 5.15 1995/06/01 16:23:43 eggert
45 * (basefilename): Renamed from basename to avoid collisions.
46 * (dirlen): Remove (for similar reasons).
47 * (rcsreadopen): Open with FOPEN_RB.
48 * (SLASHSLASH_is_SLASH): Default is 0.
49 * (getcwd): Work around bad_wait_if_SIGCHLD_ignored bug.
50 *
51 * Revision 5.14 1994/03/17 14:05:48 eggert
52 * Strip trailing SLASHes from TMPDIR; some systems need this. Remove lint.
53 *
54 * Revision 5.13 1993/11/03 17:42:27 eggert
55 * Determine whether a file name is too long indirectly,
56 * by examining inode numbers, instead of trying to use operating system
57 * primitives like pathconf, which are not trustworthy in general.
58 * File names may now hold white space or $.
59 * Do not flatten ../X in pathnames; that may yield wrong answer for symlinks.
60 * Add getabsname hook. Improve quality of diagnostics.
61 *
62 * Revision 5.12 1992/07/28 16:12:44 eggert
63 * Add .sty. .pl now implies Perl, not Prolog. Fix fdlock initialization bug.
64 * Check that $PWD is really ".". Be consistent about pathnames vs filenames.
65 *
66 * Revision 5.11 1992/02/17 23:02:25 eggert
67 * `a/RCS/b/c' is now an RCS file with an empty extension, not just `a/b/RCS/c'.
68 *
69 * Revision 5.10 1992/01/24 18:44:19 eggert
70 * Fix bug: Expand and Ignored weren't reinitialized.
71 * Avoid `char const c=ch;' compiler bug.
72 * Add support for bad_creat0.
73 *
74 * Revision 5.9 1992/01/06 02:42:34 eggert
75 * Shorten long (>31 chars) name.
76 * while (E) ; -> while (E) continue;
77 *
78 * Revision 5.8 1991/09/24 00:28:40 eggert
79 * Don't export bindex().
80 *
81 * Revision 5.7 1991/08/19 03:13:55 eggert
82 * Fix messages when rcswriteopen fails.
83 * Look in $TMP and $TEMP if $TMPDIR isn't set. Tune.
84 *
85 * Revision 5.6 1991/04/21 11:58:23 eggert
86 * Fix errno bugs. Add -x, RCSINIT, MS-DOS support.
87 *
88 * Revision 5.5 1991/02/26 17:48:38 eggert
89 * Fix setuid bug. Support new link behavior.
90 * Define more portable getcwd().
91 *
92 * Revision 5.4 1990/11/01 05:03:43 eggert
93 * Permit arbitrary data in comment leaders.
94 *
95 * Revision 5.3 1990/09/14 22:56:16 hammer
96 * added more filename extensions and their comment leaders
97 *
98 * Revision 5.2 1990/09/04 08:02:23 eggert
99 * Fix typo when !RCSSEP.
100 *
101 * Revision 5.1 1990/08/29 07:13:59 eggert
102 * Work around buggy compilers with defective argument promotion.
103 *
104 * Revision 5.0 1990/08/22 08:12:50 eggert
105 * Ignore signals when manipulating the semaphore file.
106 * Modernize list of filename extensions.
107 * Permit paths of arbitrary length. Beware filenames beginning with "-".
108 * Remove compile-time limits; use malloc instead.
109 * Permit dates past 1999/12/31. Make lock and temp files faster and safer.
110 * Ansify and Posixate.
111 * Don't use access(). Fix test for non-regular files. Tune.
112 *
113 * Revision 4.8 89/05/01 15:09:41 narten
114 * changed getwd to not stat empty directories.
115 *
116 * Revision 4.7 88/08/09 19:12:53 eggert
117 * Fix troff macro comment leader bug; add Prolog; allow cc -R; remove lint.
118 *
119 * Revision 4.6 87/12/18 11:40:23 narten
120 * additional file types added from 4.3 BSD version, and SPARC assembler
121 * comment character added. Also, more lint cleanups. (Guy Harris)
122 *
123 * Revision 4.5 87/10/18 10:34:16 narten
124 * Updating version numbers. Changes relative to 1.1 actually relative
125 * to verion 4.3
126 *
127 * Revision 1.3 87/03/27 14:22:21 jenkins
128 * Port to suns
129 *
130 * Revision 1.2 85/06/26 07:34:28 svb
131 * Comment leader '% ' for '*.tex' files added.
132 *
133 * Revision 4.3 83/12/15 12:26:48 wft
134 * Added check for KDELIM in filenames to pairfilenames().
135 *
136 * Revision 4.2 83/12/02 22:47:45 wft
137 * Added csh, red, and sl filename suffixes.
138 *
139 * Revision 4.1 83/05/11 16:23:39 wft
140 * Added initialization of Dbranch to InitAdmin(). Canged pairfilenames():
141 * 1. added copying of path from workfile to RCS file, if RCS file is omitted;
142 * 2. added getting the file status of RCS and working files;
143 * 3. added ignoring of directories.
144 *
145 * Revision 3.7 83/05/11 15:01:58 wft
146 * Added comtable[] which pairs filename suffixes with comment leaders;
147 * updated InitAdmin() accordingly.
148 *
149 * Revision 3.6 83/04/05 14:47:36 wft
150 * fixed Suffix in InitAdmin().
151 *
152 * Revision 3.5 83/01/17 18:01:04 wft
153 * Added getwd() and rename(); these can be removed by defining
154 * V4_2BSD, since they are not needed in 4.2 bsd.
155 * Changed sys/param.h to sys/types.h.
156 *
157 * Revision 3.4 82/12/08 21:55:20 wft
158 * removed unused variable.
159 *
160 * Revision 3.3 82/11/28 20:31:37 wft
161 * Changed mktempfile() to store the generated filenames.
162 * Changed getfullRCSname() to store the file and pathname, and to
163 * delete leading "../" and "./".
164 *
165 * Revision 3.2 82/11/12 14:29:40 wft
166 * changed pairfilenames() to handle file.sfx,v; also deleted checkpathnosfx(),
167 * checksuffix(), checkfullpath(). Semaphore name generation updated.
168 * mktempfile() now checks for nil path; freefilename initialized properly.
169 * Added Suffix .h to InitAdmin. Added testprogram PAIRTEST.
170 * Moved rmsema, trysema, trydiraccess, getfullRCSname from rcsutil.c to here.
171 *
172 * Revision 3.1 82/10/18 14:51:28 wft
173 * InitAdmin() now initializes StrictLocks=STRICT_LOCKING (def. in rcsbase.h).
174 * renamed checkpath() to checkfullpath().
175 */
176
177
178 #include "rcsbase.h"
179
180 libId(fnmsId, "$FreeBSD: stable/9/gnu/usr.bin/rcs/lib/rcsfnms.c 76301 2001-05-06 03:07:12Z kris $")
181
182 static char const *bindex P((char const*,int));
183 static int fin2open P((char const*, size_t, char const*, size_t, char const*, size_t, RILE*(*)P((struct buf*,struct stat*,int)), int));
184 static int finopen P((RILE*(*)P((struct buf*,struct stat*,int)), int));
185 static int suffix_matches P((char const*,char const*));
186 static size_t dir_useful_len P((char const*));
187 static size_t suffixlen P((char const*));
188 static void InitAdmin P((void));
189
190 char const *RCSname;
191 char *workname;
192 int fdlock;
193 FILE *workstdout;
194 struct stat RCSstat;
195 char const *suffixes;
196
197 static char const rcsdir[] = "RCS";
198 #define rcslen (sizeof(rcsdir)-1)
199
200 static struct buf RCSbuf, RCSb;
201 static int RCSerrno;
202
203
204 /* Temp names to be unlinked when done, if they are not 0. */
205 #define TEMPNAMES 5 /* must be at least DIRTEMPNAMES (see rcsedit.c) */
206 static char *volatile tpnames[TEMPNAMES];
207
208
209 struct compair {
210 char const *suffix, *comlead;
211 };
212
213 /*
214 * This table is present only for backwards compatibility.
215 * Normally we ignore this table, and use the prefix of the `$Log' line instead.
216 */
217 static struct compair const comtable[] = {
218 { "a" , "-- " }, /* Ada */
219 { "ada" , "-- " },
220 { "adb" , "-- " },
221 { "ads" , "-- " },
222 { "asm" , ";; " }, /* assembler (MS-DOS) */
223 { "bat" , ":: " }, /* batch (MS-DOS) */
224 { "body", "-- " }, /* Ada */
225 { "c" , " * " }, /* C */
226 { "c++" , "// " }, /* C++ in all its infinite guises */
227 { "cc" , "// " },
228 { "cpp" , "// " },
229 { "cxx" , "// " },
230 { "cl" , ";;; "}, /* Common Lisp */
231 { "cmd" , ":: " }, /* command (OS/2) */
232 { "cmf" , "c " }, /* CM Fortran */
233 { "cs" , " * " }, /* C* */
234 { "el" , "; " }, /* Emacs Lisp */
235 { "f" , "c " }, /* Fortran */
236 { "for" , "c " },
237 { "h" , " * " }, /* C-header */
238 { "hpp" , "// " }, /* C++ header */
239 { "hxx" , "// " },
240 { "l" , " * " }, /* lex (NOTE: franzlisp disagrees) */
241 { "lisp", ";;; "}, /* Lucid Lisp */
242 { "lsp" , ";; " }, /* Microsoft Lisp */
243 { "m" , "// " }, /* Objective C */
244 { "mac" , ";; " }, /* macro (DEC-10, MS-DOS, PDP-11, VMS, etc) */
245 { "me" , ".\\\" "}, /* troff -me */
246 { "ml" , "; " }, /* mocklisp */
247 { "mm" , ".\\\" "}, /* troff -mm */
248 { "ms" , ".\\\" "}, /* troff -ms */
249 { "p" , " * " }, /* Pascal */
250 { "pas" , " * " },
251 { "ps" , "% " }, /* PostScript */
252 { "spec", "-- " }, /* Ada */
253 { "sty" , "% " }, /* LaTeX style */
254 { "tex" , "% " }, /* TeX */
255 { "y" , " * " }, /* yacc */
256 { 0 , "# " } /* default for unknown suffix; must be last */
257 };
258
259 #if has_mktemp
260 static char const *tmp P((void));
261 static char const *
tmp()262 tmp()
263 /* Yield the name of the tmp directory. */
264 {
265 static char const *s;
266 if (!s
267 && !(s = cgetenv("TMPDIR")) /* Unix tradition */
268 && !(s = cgetenv("TMP")) /* DOS tradition */
269 && !(s = cgetenv("TEMP")) /* another DOS tradition */
270 )
271 s = TMPDIR;
272 return s;
273 }
274 #endif
275
276 char const *
maketemp(n)277 maketemp(n)
278 int n;
279 /* Create a unique pathname using n and the process id and store it
280 * into the nth slot in tpnames.
281 * Because of storage in tpnames, tempunlink() can unlink the file later.
282 * Return a pointer to the pathname created.
283 */
284 {
285 char *p;
286 char const *t = tpnames[n];
287 # if has_mktemp
288 int fd;
289 # endif
290
291 if (t)
292 return t;
293
294 catchints();
295 {
296 # if has_mktemp
297 char const *tp = tmp();
298 size_t tplen = dir_useful_len(tp);
299 p = testalloc(tplen + 10);
300 VOID sprintf(p, "%.*s%cT%cXXXXXX", (int)tplen, tp, SLASH, '0'+n);
301 fd = mkstemp(p);
302 if (fd < 0 || !*p)
303 faterror("can't make temporary pathname `%.*s%cT%cXXXXXX'",
304 (int)tplen, tp, SLASH, '0'+n
305 );
306 close(fd);
307 # else
308 static char tpnamebuf[TEMPNAMES][L_tmpnam];
309 p = tpnamebuf[n];
310 if (!tmpnam(p) || !*p)
311 # ifdef P_tmpdir
312 faterror("can't make temporary pathname `%s...'",P_tmpdir);
313 # else
314 faterror("can't make temporary pathname");
315 # endif
316 # endif
317 }
318
319 tpnames[n] = p;
320 return p;
321 }
322
323 void
tempunlink()324 tempunlink()
325 /* Clean up maketemp() files. May be invoked by signal handler.
326 */
327 {
328 register int i;
329 register char *p;
330
331 for (i = TEMPNAMES; 0 <= --i; )
332 if ((p = tpnames[i])) {
333 VOID unlink(p);
334 /*
335 * We would tfree(p) here,
336 * but this might dump core if we're handing a signal.
337 * We're about to exit anyway, so we won't bother.
338 */
339 tpnames[i] = 0;
340 }
341 }
342
343
344 static char const *
bindex(sp,c)345 bindex(sp, c)
346 register char const *sp;
347 register int c;
348 /* Function: Finds the last occurrence of character c in string sp
349 * and returns a pointer to the character just beyond it. If the
350 * character doesn't occur in the string, sp is returned.
351 */
352 {
353 register char const *r;
354 r = sp;
355 while (*sp) {
356 if (*sp++ == c) r=sp;
357 }
358 return r;
359 }
360
361
362
363 static int
suffix_matches(suffix,pattern)364 suffix_matches(suffix, pattern)
365 register char const *suffix, *pattern;
366 {
367 register int c;
368 if (!pattern)
369 return true;
370 for (;;)
371 switch (*suffix++ - (c = *pattern++)) {
372 case 0:
373 if (!c)
374 return true;
375 break;
376
377 case 'A'-'a':
378 if (ctab[c] == Letter)
379 break;
380 /* fall into */
381 default:
382 return false;
383 }
384 }
385
386
387 static void
InitAdmin()388 InitAdmin()
389 /* function: initializes an admin node */
390 {
391 register char const *Suffix;
392 register int i;
393
394 Head=0; Dbranch=0; AccessList=0; Symbols=0; Locks=0;
395 StrictLocks=STRICT_LOCKING;
396
397 /* guess the comment leader from the suffix*/
398 Suffix = bindex(workname, '.');
399 if (Suffix==workname) Suffix= ""; /* empty suffix; will get default*/
400 for (i=0; !suffix_matches(Suffix,comtable[i].suffix); i++)
401 continue;
402 Comment.string = comtable[i].comlead;
403 Comment.size = strlen(comtable[i].comlead);
404 Expand = KEYVAL_EXPAND;
405 clear_buf(&Ignored);
406 Lexinit(); /* note: if !finptr, reads nothing; only initializes */
407 }
408
409
410
411 void
bufalloc(b,size)412 bufalloc(b, size)
413 register struct buf *b;
414 size_t size;
415 /* Ensure *B is a name buffer of at least SIZE bytes.
416 * *B's old contents can be freed; *B's new contents are undefined.
417 */
418 {
419 if (b->size < size) {
420 if (b->size)
421 tfree(b->string);
422 else
423 b->size = sizeof(malloc_type);
424 while (b->size < size)
425 b->size <<= 1;
426 b->string = tnalloc(char, b->size);
427 }
428 }
429
430 void
bufrealloc(b,size)431 bufrealloc(b, size)
432 register struct buf *b;
433 size_t size;
434 /* like bufalloc, except *B's old contents, if any, are preserved */
435 {
436 if (b->size < size) {
437 if (!b->size)
438 bufalloc(b, size);
439 else {
440 while ((b->size <<= 1) < size)
441 continue;
442 b->string = trealloc(char, b->string, b->size);
443 }
444 }
445 }
446
447 void
bufautoend(b)448 bufautoend(b)
449 struct buf *b;
450 /* Free an auto buffer at block exit. */
451 {
452 if (b->size)
453 tfree(b->string);
454 }
455
456 struct cbuf
bufremember(b,s)457 bufremember(b, s)
458 struct buf *b;
459 size_t s;
460 /*
461 * Free the buffer B with used size S.
462 * Yield a cbuf with identical contents.
463 * The cbuf will be reclaimed when this input file is finished.
464 */
465 {
466 struct cbuf cb;
467
468 if ((cb.size = s))
469 cb.string = fremember(trealloc(char, b->string, s));
470 else {
471 bufautoend(b); /* not really auto */
472 cb.string = "";
473 }
474 return cb;
475 }
476
477 char *
bufenlarge(b,alim)478 bufenlarge(b, alim)
479 register struct buf *b;
480 char const **alim;
481 /* Make *B larger. Set *ALIM to its new limit, and yield the relocated value
482 * of its old limit.
483 */
484 {
485 size_t s = b->size;
486 bufrealloc(b, s + 1);
487 *alim = b->string + b->size;
488 return b->string + s;
489 }
490
491 void
bufscat(b,s)492 bufscat(b, s)
493 struct buf *b;
494 char const *s;
495 /* Concatenate S to B's end. */
496 {
497 size_t blen = b->string ? strlen(b->string) : 0;
498 bufrealloc(b, blen+strlen(s)+1);
499 VOID strcpy(b->string+blen, s);
500 }
501
502 void
bufscpy(b,s)503 bufscpy(b, s)
504 struct buf *b;
505 char const *s;
506 /* Copy S into B. */
507 {
508 bufalloc(b, strlen(s)+1);
509 VOID strcpy(b->string, s);
510 }
511
512
513 char const *
basefilename(p)514 basefilename(p)
515 char const *p;
516 /* Yield the address of the base filename of the pathname P. */
517 {
518 register char const *b = p, *q = p;
519 for (;;)
520 switch (*q++) {
521 case SLASHes: b = q; break;
522 case 0: return b;
523 }
524 }
525
526
527 static size_t
suffixlen(x)528 suffixlen(x)
529 char const *x;
530 /* Yield the length of X, an RCS pathname suffix. */
531 {
532 register char const *p;
533
534 p = x;
535 for (;;)
536 switch (*p) {
537 case 0: case SLASHes:
538 return p - x;
539
540 default:
541 ++p;
542 continue;
543 }
544 }
545
546 char const *
rcssuffix(name)547 rcssuffix(name)
548 char const *name;
549 /* Yield the suffix of NAME if it is an RCS pathname, 0 otherwise. */
550 {
551 char const *x, *p, *nz;
552 size_t nl, xl;
553
554 nl = strlen(name);
555 nz = name + nl;
556 x = suffixes;
557 do {
558 if ((xl = suffixlen(x))) {
559 if (xl <= nl && memcmp(p = nz-xl, x, xl) == 0)
560 return p;
561 } else
562 for (p = name; p < nz - rcslen; p++)
563 if (
564 isSLASH(p[rcslen])
565 && (p==name || isSLASH(p[-1]))
566 && memcmp(p, rcsdir, rcslen) == 0
567 )
568 return nz;
569 x += xl;
570 } while (*x++);
571 return 0;
572 }
573
574 /*ARGSUSED*/ RILE *
rcsreadopen(RCSpath,status,mustread)575 rcsreadopen(RCSpath, status, mustread)
576 struct buf *RCSpath;
577 struct stat *status;
578 int mustread;
579 /* Open RCSPATH for reading and yield its FILE* descriptor.
580 * If successful, set *STATUS to its status.
581 * Pass this routine to pairnames() for read-only access to the file. */
582 {
583 return Iopen(RCSpath->string, FOPEN_RB, status);
584 }
585
586 static int
587 finopen(rcsopen, mustread)
588 RILE *(*rcsopen)P((struct buf*,struct stat*,int));
589 int mustread;
590 /*
591 * Use RCSOPEN to open an RCS file; MUSTREAD is set if the file must be read.
592 * Set finptr to the result and yield true if successful.
593 * RCSb holds the file's name.
594 * Set RCSbuf to the best RCS name found so far, and RCSerrno to its errno.
595 * Yield true if successful or if an unusual failure.
596 */
597 {
598 int interesting, preferold;
599
600 /*
601 * We prefer an old name to that of a nonexisting new RCS file,
602 * unless we tried locking the old name and failed.
603 */
604 preferold = RCSbuf.string[0] && (mustread||0<=fdlock);
605
606 finptr = (*rcsopen)(&RCSb, &RCSstat, mustread);
607 interesting = finptr || errno!=ENOENT;
608 if (interesting || !preferold) {
609 /* Use the new name. */
610 RCSerrno = errno;
611 bufscpy(&RCSbuf, RCSb.string);
612 }
613 return interesting;
614 }
615
616 static int
fin2open(d,dlen,base,baselen,x,xlen,rcsopen,mustread)617 fin2open(d, dlen, base, baselen, x, xlen, rcsopen, mustread)
618 char const *d, *base, *x;
619 size_t dlen, baselen, xlen;
620 RILE *(*rcsopen)P((struct buf*,struct stat*,int));
621 int mustread;
622 /*
623 * D is a directory name with length DLEN (including trailing slash).
624 * BASE is a filename with length BASELEN.
625 * X is an RCS pathname suffix with length XLEN.
626 * Use RCSOPEN to open an RCS file; MUSTREAD is set if the file must be read.
627 * Yield true if successful.
628 * Try dRCS/basex first; if that fails and x is nonempty, try dbasex.
629 * Put these potential names in RCSb.
630 * Set RCSbuf to the best RCS name found so far, and RCSerrno to its errno.
631 * Yield true if successful or if an unusual failure.
632 */
633 {
634 register char *p;
635
636 bufalloc(&RCSb, dlen + rcslen + 1 + baselen + xlen + 1);
637
638 /* Try dRCS/basex. */
639 VOID memcpy(p = RCSb.string, d, dlen);
640 VOID memcpy(p += dlen, rcsdir, rcslen);
641 p += rcslen;
642 *p++ = SLASH;
643 VOID memcpy(p, base, baselen);
644 VOID memcpy(p += baselen, x, xlen);
645 p[xlen] = 0;
646 if (xlen) {
647 if (finopen(rcsopen, mustread))
648 return true;
649
650 /* Try dbasex. */
651 /* Start from scratch, because finopen() may have changed RCSb. */
652 VOID memcpy(p = RCSb.string, d, dlen);
653 VOID memcpy(p += dlen, base, baselen);
654 VOID memcpy(p += baselen, x, xlen);
655 p[xlen] = 0;
656 }
657 return finopen(rcsopen, mustread);
658 }
659
660 int
pairnames(argc,argv,rcsopen,mustread,quiet)661 pairnames(argc, argv, rcsopen, mustread, quiet)
662 int argc;
663 char **argv;
664 RILE *(*rcsopen)P((struct buf*,struct stat*,int));
665 int mustread, quiet;
666 /*
667 * Pair the pathnames pointed to by argv; argc indicates
668 * how many there are.
669 * Place a pointer to the RCS pathname into RCSname,
670 * and a pointer to the pathname of the working file into workname.
671 * If both are given, and workstdout
672 * is set, a warning is printed.
673 *
674 * If the RCS file exists, places its status into RCSstat.
675 *
676 * If the RCS file exists, it is RCSOPENed for reading, the file pointer
677 * is placed into finptr, and the admin-node is read in; returns 1.
678 * If the RCS file does not exist and MUSTREAD,
679 * print an error unless QUIET and return 0.
680 * Otherwise, initialize the admin node and return -1.
681 *
682 * 0 is returned on all errors, e.g. files that are not regular files.
683 */
684 {
685 static struct buf tempbuf;
686
687 register char *p, *arg, *RCS1;
688 char const *base, *RCSbase, *x;
689 int paired;
690 size_t arglen, dlen, baselen, xlen;
691
692 fdlock = -1;
693
694 if (!(arg = *argv)) return 0; /* already paired pathname */
695 if (*arg == '-') {
696 error("%s option is ignored after pathnames", arg);
697 return 0;
698 }
699
700 base = basefilename(arg);
701 paired = false;
702
703 /* first check suffix to see whether it is an RCS file or not */
704 if ((x = rcssuffix(arg)))
705 {
706 /* RCS pathname given */
707 RCS1 = arg;
708 RCSbase = base;
709 baselen = x - base;
710 if (
711 1 < argc &&
712 !rcssuffix(workname = p = argv[1]) &&
713 baselen <= (arglen = strlen(p)) &&
714 ((p+=arglen-baselen) == workname || isSLASH(p[-1])) &&
715 memcmp(base, p, baselen) == 0
716 ) {
717 argv[1] = 0;
718 paired = true;
719 } else {
720 bufscpy(&tempbuf, base);
721 workname = p = tempbuf.string;
722 p[baselen] = 0;
723 }
724 } else {
725 /* working file given; now try to find RCS file */
726 workname = arg;
727 baselen = strlen(base);
728 /* Derive RCS pathname. */
729 if (
730 1 < argc &&
731 (x = rcssuffix(RCS1 = argv[1])) &&
732 baselen <= x - RCS1 &&
733 ((RCSbase=x-baselen)==RCS1 || isSLASH(RCSbase[-1])) &&
734 memcmp(base, RCSbase, baselen) == 0
735 ) {
736 argv[1] = 0;
737 paired = true;
738 } else
739 RCSbase = RCS1 = 0;
740 }
741 /* Now we have a (tentative) RCS pathname in RCS1 and workname. */
742 /* Second, try to find the right RCS file */
743 if (RCSbase!=RCS1) {
744 /* a path for RCSfile is given; single RCS file to look for */
745 bufscpy(&RCSbuf, RCS1);
746 finptr = (*rcsopen)(&RCSbuf, &RCSstat, mustread);
747 RCSerrno = errno;
748 } else {
749 bufscpy(&RCSbuf, "");
750 if (RCS1)
751 /* RCS filename was given without path. */
752 VOID fin2open(arg, (size_t)0, RCSbase, baselen,
753 x, strlen(x), rcsopen, mustread
754 );
755 else {
756 /* No RCS pathname was given. */
757 /* Try each suffix in turn. */
758 dlen = base-arg;
759 x = suffixes;
760 while (! fin2open(arg, dlen, base, baselen,
761 x, xlen=suffixlen(x), rcsopen, mustread
762 )) {
763 x += xlen;
764 if (!*x++)
765 break;
766 }
767 }
768 }
769 RCSname = p = RCSbuf.string;
770 if (finptr) {
771 if (!S_ISREG(RCSstat.st_mode)) {
772 error("%s isn't a regular file -- ignored", p);
773 return 0;
774 }
775 Lexinit(); getadmin();
776 } else {
777 if (RCSerrno!=ENOENT || mustread || fdlock<0) {
778 if (RCSerrno == EEXIST)
779 error("RCS file %s is in use", p);
780 else if (!quiet || RCSerrno!=ENOENT)
781 enerror(RCSerrno, p);
782 return 0;
783 }
784 InitAdmin();
785 };
786
787 if (paired && workstdout)
788 workwarn("Working file ignored due to -p option");
789
790 prevkeys = false;
791 return finptr ? 1 : -1;
792 }
793
794
795 char const *
getfullRCSname()796 getfullRCSname()
797 /*
798 * Return a pointer to the full pathname of the RCS file.
799 * Remove leading `./'.
800 */
801 {
802 if (ROOTPATH(RCSname)) {
803 return RCSname;
804 } else {
805 static struct buf rcsbuf;
806 # if needs_getabsname
807 bufalloc(&rcsbuf, SIZEABLE_PATH + 1);
808 while (getabsname(RCSname, rcsbuf.string, rcsbuf.size) != 0)
809 if (errno == ERANGE)
810 bufalloc(&rcsbuf, rcsbuf.size<<1);
811 else
812 efaterror("getabsname");
813 # else
814 static char const *wdptr;
815 static struct buf wdbuf;
816 static size_t wdlen;
817
818 register char const *r;
819 register size_t dlen;
820 register char *d;
821 register char const *wd;
822
823 if (!(wd = wdptr)) {
824 /* Get working directory for the first time. */
825 char *PWD = cgetenv("PWD");
826 struct stat PWDstat, dotstat;
827 if (! (
828 (d = PWD) &&
829 ROOTPATH(PWD) &&
830 stat(PWD, &PWDstat) == 0 &&
831 stat(".", &dotstat) == 0 &&
832 same_file(PWDstat, dotstat, 1)
833 )) {
834 bufalloc(&wdbuf, SIZEABLE_PATH + 1);
835 # if has_getcwd || !has_getwd
836 while (!(d = getcwd(wdbuf.string, wdbuf.size)))
837 if (errno == ERANGE)
838 bufalloc(&wdbuf, wdbuf.size<<1);
839 else if ((d = PWD))
840 break;
841 else
842 efaterror("getcwd");
843 # else
844 d = getwd(wdbuf.string);
845 if (!d && !(d = PWD))
846 efaterror("getwd");
847 # endif
848 }
849 wdlen = dir_useful_len(d);
850 d[wdlen] = 0;
851 wdptr = wd = d;
852 }
853 /*
854 * Remove leading `./'s from RCSname.
855 * Do not try to handle `../', since removing it may yield
856 * the wrong answer in the presence of symbolic links.
857 */
858 for (r = RCSname; r[0]=='.' && isSLASH(r[1]); r += 2)
859 /* `.////' is equivalent to `./'. */
860 while (isSLASH(r[2]))
861 r++;
862 /* Build full pathname. */
863 dlen = wdlen;
864 bufalloc(&rcsbuf, dlen + strlen(r) + 2);
865 d = rcsbuf.string;
866 VOID memcpy(d, wd, dlen);
867 d += dlen;
868 *d++ = SLASH;
869 VOID strcpy(d, r);
870 # endif
871 return rcsbuf.string;
872 }
873 }
874
875 /* Derived from code from the XFree86 project */
876 char const *
getfullCVSname()877 getfullCVSname()
878 /* Function: returns a pointer to the path name of the RCS file with the
879 * CVSROOT part stripped off, and with 'Attic/' stripped off (if present).
880 */
881 {
882
883 #define ATTICDIR "/Attic"
884
885 char const *namebuf = getfullRCSname();
886 char *cvsroot = cgetenv("CVSROOT");
887 int cvsrootlen;
888 char *c = NULL;
889 int alen = strlen(ATTICDIR);
890
891 if ((c = strrchr(namebuf, '/')) != NULL) {
892 if (namebuf - c >= alen) {
893 if (!strncmp(c - alen, ATTICDIR, alen)) {
894 while(*c != '\0') {
895 *(c - alen) = *c;
896 c++;
897 }
898 *(c - alen) = '\0';
899 }
900 }
901 }
902
903 if (!cvsroot)
904 return(namebuf);
905 else
906 {
907 cvsrootlen = strlen(cvsroot);
908 if (!strncmp(namebuf, cvsroot, cvsrootlen) &&
909 namebuf[cvsrootlen] == '/')
910 return(namebuf + cvsrootlen + 1);
911 else
912 return(namebuf);
913 }
914 }
915
916 static size_t
dir_useful_len(d)917 dir_useful_len(d)
918 char const *d;
919 /*
920 * D names a directory; yield the number of characters of D's useful part.
921 * To create a file in D, append a SLASH and a file name to D's useful part.
922 * Ignore trailing slashes if possible; not only are they ugly,
923 * but some non-Posix systems misbehave unless the slashes are omitted.
924 */
925 {
926 # ifndef SLASHSLASH_is_SLASH
927 # define SLASHSLASH_is_SLASH 0
928 # endif
929 size_t dlen = strlen(d);
930 if (!SLASHSLASH_is_SLASH && dlen==2 && isSLASH(d[0]) && isSLASH(d[1]))
931 --dlen;
932 else
933 while (dlen && isSLASH(d[dlen-1]))
934 --dlen;
935 return dlen;
936 }
937
938 #ifndef isSLASH
939 int
isSLASH(c)940 isSLASH(c)
941 int c;
942 {
943 switch (c) {
944 case SLASHes:
945 return true;
946 default:
947 return false;
948 }
949 }
950 #endif
951
952
953 #if !has_getcwd && !has_getwd
954
955 char *
getcwd(path,size)956 getcwd(path, size)
957 char *path;
958 size_t size;
959 {
960 static char const usrbinpwd[] = "/usr/bin/pwd";
961 # define binpwd (usrbinpwd+4)
962
963 register FILE *fp;
964 register int c;
965 register char *p, *lim;
966 int closeerrno, closeerror, e, fd[2], readerror, toolong, wstatus;
967 pid_t child;
968
969 if (!size) {
970 errno = EINVAL;
971 return 0;
972 }
973 if (pipe(fd) != 0)
974 return 0;
975 # if bad_wait_if_SIGCHLD_ignored
976 # ifndef SIGCHLD
977 # define SIGCHLD SIGCLD
978 # endif
979 VOID signal(SIGCHLD, SIG_DFL);
980 # endif
981 if (!(child = vfork())) {
982 if (
983 close(fd[0]) == 0 &&
984 (fd[1] == STDOUT_FILENO ||
985 # ifdef F_DUPFD
986 (VOID close(STDOUT_FILENO),
987 fcntl(fd[1], F_DUPFD, STDOUT_FILENO))
988 # else
989 dup2(fd[1], STDOUT_FILENO)
990 # endif
991 == STDOUT_FILENO &&
992 close(fd[1]) == 0
993 )
994 ) {
995 VOID close(STDERR_FILENO);
996 VOID execl(binpwd, binpwd, (char *)0);
997 VOID execl(usrbinpwd, usrbinpwd, (char *)0);
998 }
999 _exit(EXIT_FAILURE);
1000 }
1001 e = errno;
1002 closeerror = close(fd[1]);
1003 closeerrno = errno;
1004 fp = 0;
1005 readerror = toolong = wstatus = 0;
1006 p = path;
1007 if (0 <= child) {
1008 fp = fdopen(fd[0], "r");
1009 e = errno;
1010 if (fp) {
1011 lim = p + size;
1012 for (p = path; ; *p++ = c) {
1013 if ((c=getc(fp)) < 0) {
1014 if (feof(fp))
1015 break;
1016 if (ferror(fp)) {
1017 readerror = 1;
1018 e = errno;
1019 break;
1020 }
1021 }
1022 if (p == lim) {
1023 toolong = 1;
1024 break;
1025 }
1026 }
1027 }
1028 # if has_waitpid
1029 if (waitpid(child, &wstatus, 0) < 0)
1030 wstatus = 1;
1031 # else
1032 {
1033 pid_t w;
1034 do {
1035 if ((w = wait(&wstatus)) < 0) {
1036 wstatus = 1;
1037 break;
1038 }
1039 } while (w != child);
1040 }
1041 # endif
1042 }
1043 if (!fp) {
1044 VOID close(fd[0]);
1045 errno = e;
1046 return 0;
1047 }
1048 if (fclose(fp) != 0)
1049 return 0;
1050 if (readerror) {
1051 errno = e;
1052 return 0;
1053 }
1054 if (closeerror) {
1055 errno = closeerrno;
1056 return 0;
1057 }
1058 if (toolong) {
1059 errno = ERANGE;
1060 return 0;
1061 }
1062 if (wstatus || p == path || *--p != '\n') {
1063 errno = EACCES;
1064 return 0;
1065 }
1066 *p = '\0';
1067 return path;
1068 }
1069 #endif
1070
1071
1072 #ifdef PAIRTEST
1073 /* test program for pairnames() and getfullRCSname() */
1074
1075 char const cmdid[] = "pair";
1076
main(argc,argv)1077 main(argc, argv)
1078 int argc; char *argv[];
1079 {
1080 int result;
1081 int initflag;
1082 quietflag = initflag = false;
1083
1084 while(--argc, ++argv, argc>=1 && ((*argv)[0] == '-')) {
1085 switch ((*argv)[1]) {
1086
1087 case 'p': workstdout = stdout;
1088 break;
1089 case 'i': initflag=true;
1090 break;
1091 case 'q': quietflag=true;
1092 break;
1093 default: error("unknown option: %s", *argv);
1094 break;
1095 }
1096 }
1097
1098 do {
1099 RCSname = workname = 0;
1100 result = pairnames(argc,argv,rcsreadopen,!initflag,quietflag);
1101 if (result!=0) {
1102 diagnose("RCS pathname: %s; working pathname: %s\nFull RCS pathname: %s\n",
1103 RCSname, workname, getfullRCSname()
1104 );
1105 }
1106 switch (result) {
1107 case 0: continue; /* already paired file */
1108
1109 case 1: if (initflag) {
1110 rcserror("already exists");
1111 } else {
1112 diagnose("RCS file %s exists\n", RCSname);
1113 }
1114 Ifclose(finptr);
1115 break;
1116
1117 case -1:diagnose("RCS file doesn't exist\n");
1118 break;
1119 }
1120
1121 } while (++argv, --argc>=1);
1122
1123 }
1124
1125 void
exiterr()1126 exiterr()
1127 {
1128 dirtempunlink();
1129 tempunlink();
1130 _exit(EXIT_FAILURE);
1131 }
1132 #endif
1133