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