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