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