|
|
1.1 root 1: /*
2: * RCS file name handling
3: */
4: #ifndef lint
5: static char
6: rcsid[]= "$Id: rcsfnms.c,v 3.10 88/02/18 11:57:27 bostic Exp $ Purdue CS";
7: #endif
8: /****************************************************************************
9: * creation and deletion of semaphorefile,
10: * creation of temporary filenames and cleanup()
11: * pairing of RCS file names and working file names.
12: * Testprogram: define PAIRTEST
13: ****************************************************************************
14: *
15: * Copyright (C) 1982 by Walter F. Tichy
16: * Purdue University
17: * Computer Science Department
18: * West Lafayette, IN 47907
19: *
20: * All rights reserved. No part of this software may be sold or distributed
21: * in any form or by any means without the prior written permission of the
22: * author.
23: * Report problems and direct all inquiries to Tichy@purdue (ARPA net).
24: */
25:
26:
27: /* $Log: rcsfnms.c,v $
28: * Revision 3.10 88/02/18 11:57:27 bostic
29: * replaced with version 4
30: *
31: * Revision 4.6 87/12/18 11:40:23 narten
32: * additional file types added from 4.3 BSD version, and SPARC assembler
33: * comment character added. Also, more lint cleanups. (Guy Harris)
34: *
35: * Revision 4.5 87/10/18 10:34:16 narten
36: * Updating version numbers. Changes relative to 1.1 actually relative
37: * to verion 4.3
38: *
39: * Revision 1.3 87/03/27 14:22:21 jenkins
40: * Port to suns
41: *
42: * Revision 1.2 85/06/26 07:34:28 svb
43: * Comment leader '% ' for '*.tex' files added.
44: *
45: * Revision 1.1 84/01/23 14:50:24 kcs
46: * Initial revision
47: *
48: * Revision 4.3 83/12/15 12:26:48 wft
49: * Added check for KDELIM in file names to pairfilenames().
50: *
51: * Revision 4.2 83/12/02 22:47:45 wft
52: * Added csh, red, and sl file name suffixes.
53: *
54: * Revision 4.1 83/05/11 16:23:39 wft
55: * Added initialization of Dbranch to InitAdmin(). Canged pairfilenames():
56: * 1. added copying of path from workfile to RCS file, if RCS file is omitted;
57: * 2. added getting the file status of RCS and working files;
58: * 3. added ignoring of directories.
59: *
60: * Revision 3.7 83/05/11 15:01:58 wft
61: * Added comtable[] which pairs file name suffixes with comment leaders;
62: * updated InitAdmin() accordingly.
63: *
64: * Revision 3.6 83/04/05 14:47:36 wft
65: * fixed Suffix in InitAdmin().
66: *
67: * Revision 3.5 83/01/17 18:01:04 wft
68: * Added getwd() and rename(); these can be removed by defining
69: * V4_2BSD, since they are not needed in 4.2 bsd.
70: * Changed sys/param.h to sys/types.h.
71: *
72: * Revision 3.4 82/12/08 21:55:20 wft
73: * removed unused variable.
74: *
75: * Revision 3.3 82/11/28 20:31:37 wft
76: * Changed mktempfile() to store the generated file names.
77: * Changed getfullRCSname() to store the file and pathname, and to
78: * delete leading "../" and "./".
79: *
80: * Revision 3.2 82/11/12 14:29:40 wft
81: * changed pairfilenames() to handle file.sfx,v; also deleted checkpathnosfx(),
82: * checksuffix(), checkfullpath(). Semaphore name generation updated.
83: * mktempfile() now checks for nil path; lastfilename initialized properly.
84: * Added Suffix .h to InitAdmin. Added testprogram PAIRTEST.
85: * Moved rmsema, trysema, trydiraccess, getfullRCSname from rcsutil.c to here.
86: *
87: * Revision 3.1 82/10/18 14:51:28 wft
88: * InitAdmin() now initializes StrictLocks=STRICT_LOCKING (def. in rcsbase.h).
89: * renamed checkpath() to checkfullpath().
90: */
91:
92:
93: #include "rcsbase.h"
94: #include <sys/types.h>
95: #include <sys/stat.h>
96: #include <sys/dir.h>
97:
98: extern char * rindex();
99: extern char * mktemp();
100: extern char * malloc();
101: extern FILE * fopen();
102: extern char * getwd(); /* get working directory; forward decl */
103: extern int stat(), fstat();
104:
105: extern FILE * finptr; /* RCS input file descriptor */
106: extern FILE * frewrite; /* New RCS file descriptor */
107: extern char * RCSfilename, * workfilename; /* filenames */
108: struct stat RCSstat, workstat; /* file status for RCS file and working file */
109: int haveRCSstat, haveworkstat; /* indicators if status availalble */
110:
111:
112: char tempfilename [NCPFN+10]; /* used for derived file names */
113: char sub1filename [NCPPN]; /* used for files path/file.sfx,v */
114: char sub2filename [NCPPN]; /* used for files path/RCS/file.sfx,v */
115: char semafilename [NCPPN]; /* name of semaphore file */
116: int madesema; /* indicates whether a semaphore file has been set */
117: char * tfnames[10] = /* temp. file names to be unlinked when finished */
118: {nil,nil,nil,nil,nil,nil,nil,nil,nil,nil};
119: int lastfilename = -1;/* index of last file name in tfnames[] */
120:
121:
122: struct compair {
123: char * suffix, * comlead;
124: };
125:
126: struct compair comtable[] = {
127: /* comtable pairs each filename suffix with a comment leader. The comment */
128: /* leader is placed before each line generated by the $Log keyword. This */
129: /* table is used to guess the proper comment leader from the working file's */
130: /* suffix during initial ci (see InitAdmin()). Comment leaders are needed */
131: /* for languages without multiline comments; for others they are optional. */
132: "c", " * ", /* C */
133: "csh", "# ", /* shell */
134: "e", "# ", /* efl */
135: "f", "c ", /* fortran */
136: "h", " * ", /* C-header */
137: "l", " * ", /* lex NOTE: conflict between lex and franzlisp*/
138: "mac", "; ", /* macro vms or dec-20 or pdp-11 macro */
139: "me", "\\\" ", /* me-macros t/nroff*/
140: "mm", "\\\" ", /* mm-macros t/nroff*/
141: "ms", "\\\" ", /* ms-macros t/nroff*/
142: "p", " * ", /* pascal */
143: "r", "# ", /* ratfor */
144: "red", "% ", /* psl/rlisp */
145:
146: #ifdef sparc
147: "s", "! ", /* assembler */
148: #endif
149: #ifdef mc68000
150: "s", "| ", /* assembler */
151: #endif
152: #ifdef pdp11
153: "s", "/ ", /* assembler */
154: #endif
155: #ifdef vax
156: "s", "# ", /* assembler */
157: #endif
158:
159: "sh", "# ", /* shell */
160: "sl", "% ", /* psl */
161: "red", "% ", /* psl/rlisp */
162: "cl", ";;; ", /* common lisp */
163: "ml", "; ", /* mocklisp */
164: "el", "; ", /* gnulisp */
165: "tex", "% ", /* tex */
166: "y", " * ", /* yacc */
167: "ye", " * ", /* yacc-efl */
168: "yr", " * ", /* yacc-ratfor */
169: "", "# ", /* default for empty suffix */
170: nil, "" /* default for unknown suffix; must always be last */
171: };
172:
173:
174: ffclose(fptr)
175: FILE * fptr;
176: /* Function: checks ferror(fptr) and aborts the program if there were
177: * errors; otherwise closes fptr.
178: */
179: { if (ferror(fptr) || fclose(fptr)==EOF)
180: faterror("File read or write error; file system full?");
181: }
182:
183:
184:
185: int trysema(RCSfilename,makesema)
186: char * RCSfilename; int makesema;
187: /* Function: Checks whether a semaphore file exists for RCSfilename. If yes,
188: * returns false. If not, creates one if makesema==true and returns true
189: * if successful. If a semaphore file was created, madesema is set to true.
190: * The name of the semaphore file is put into variable semafilename.
191: */
192: {
193: register char * tp, *sp, *lp;
194: int fdesc;
195:
196: sp=RCSfilename;
197: lp = rindex(sp,'/');
198: if (lp==0) {
199: semafilename[0]='.'; semafilename[1]='/';
200: tp= &semafilename[2];
201: } else {
202: /* copy path */
203: tp=semafilename;
204: do *tp++ = *sp++; while (sp<=lp);
205: }
206: /*now insert `,' and append file name */
207: *tp++ = ',';
208: lp = rindex(sp, RCSSEP);
209: while (sp<lp) *tp++ = *sp++;
210: *tp++ = ','; *tp++ = '\0'; /* will be the same length as RCSfilename*/
211:
212: madesema = false;
213: if (access(semafilename, 0) == 0) {
214: error("RCS file %s is in use",RCSfilename);
215: return false;
216: }
217: if (makesema) {
218: if ((fdesc=creat(semafilename, 000)) == -1) {
219: error("Can't create semaphore file for RCS file %s",RCSfilename);
220: return false;
221: } else
222: VOID close(fdesc);
223: madesema=true;
224: }
225: return true;
226: }
227:
228:
229: int rmsema()
230: /* Function: delete the semaphore file if madeseam==true;
231: * sets madesema to false.
232: */
233: {
234: if (madesema) {
235: madesema=false;
236: if (unlink(semafilename) == -1) {
237: error("Can't find semaphore file %s",semafilename);
238: return false;
239: }
240: }
241: return true;
242: }
243:
244:
245:
246: InitCleanup()
247: { lastfilename = -1; /* initialize pointer */
248: }
249:
250:
251: cleanup()
252: /* Function: closes input file and rewrite file.
253: * Unlinks files in tfnames[], deletes semaphore file.
254: */
255: {
256: register int i;
257:
258: if (finptr!=NULL) VOID fclose(finptr);
259: if (frewrite!=NULL) VOID fclose(frewrite);
260: for (i=0; i<=lastfilename; i++) {
261: if (tfnames[i][0]!='\0') VOID unlink(tfnames[i]);
262: }
263: InitCleanup();
264: return (rmsema());
265: }
266:
267:
268: char * mktempfile(fullpath,filename)
269: register char * fullpath, * filename;
270: /* Function: Creates a unique filename using the process id and stores it
271: * into a free slot in tfnames. The filename consists of the path contained
272: * in fullpath concatenated with filename. filename should end in "XXXXXX".
273: * Because of storage in tfnames, cleanup() can unlink the file later.
274: * lastfilename indicates the highest occupied slot in tfnames.
275: * Returns a pointer to the filename created.
276: * Example use: mktempfile("/tmp/", somefilename)
277: */
278: {
279: register char * lastslash, *tp;
280: lastfilename++;
281: if ((tp=tfnames[lastfilename])==nil)
282: tp=tfnames[lastfilename] = malloc(NCPPN);
283: if (fullpath!=nil && (lastslash=rindex(fullpath,'/'))!=0) {
284: /* copy path */
285: while (fullpath<=lastslash) *tp++ = *fullpath++;
286: }
287: while (*tp++ = *filename++);
288: return (mktemp(tfnames[lastfilename]));
289: }
290:
291:
292:
293:
294: char * bindex(sp,c)
295: register char * sp, c;
296: /* Function: Finds the last occurrence of character c in string sp
297: * and returns a pointer to the character just beyond it. If the
298: * character doesn't occur in the string, sp is returned.
299: */
300: { register char * r;
301: r = sp;
302: while (*sp) {
303: if (*sp++ == c) r=sp;
304: }
305: return r;
306: }
307:
308:
309:
310:
311:
312: InitAdmin()
313: /* function: initializes an admin node */
314: { register char * Suffix;
315: register int i;
316:
317: Head=Dbranch=nil; AccessList=nil; Symbols=nil; Locks=nil;
318: StrictLocks=STRICT_LOCKING;
319:
320: /* guess the comment leader from the suffix*/
321: Suffix=bindex(workfilename, '.');
322: if (Suffix==workfilename) Suffix= ""; /* empty suffix; will get default*/
323: for (i=0;;i++) {
324: if (comtable[i].suffix==nil) {
325: Comment=comtable[i].comlead; /*default*/
326: break;
327: } elsif (strcmp(Suffix,comtable[i].suffix)==0) {
328: Comment=comtable[i].comlead; /*default*/
329: break;
330: }
331: }
332: Lexinit(); /* Note: if finptr==NULL, reads nothing; only initializes*/
333: }
334:
335:
336:
337: char * findpairfile(argc, argv, fname)
338: int argc; char * argv[], *fname;
339: /* Function: Given a filename fname, findpairfile scans argv for a pathname
340: * ending in fname. If found, returns a pointer to the pathname, and sets
341: * the corresponding pointer in argv to nil. Otherwise returns fname.
342: * argc indicates the number of entries in argv. Some of them may be nil.
343: */
344: {
345: register char * * next, * match;
346: register int count;
347:
348: for (next = argv, count = argc; count>0; next++,count--) {
349: if ((*next != nil) && strcmp(bindex(*next,'/'),fname)==0) {
350: /* bindex finds the beginning of the file name stem */
351: match= *next;
352: *next=nil;
353: return match;
354: }
355: }
356: return fname;
357: }
358:
359:
360: int pairfilenames(argc, argv, mustread, tostdout)
361: int argc; char ** argv; int mustread, tostdout;
362: /* Function: Pairs the filenames pointed to by argv; argc indicates
363: * how many there are.
364: * Places a pointer to the RCS filename into RCSfilename,
365: * and a pointer to the name of the working file into workfilename.
366: * If both the workfilename and the RCS filename are given, and tostdout
367: * is true, a warning is printed.
368: *
369: * If the working file exists, places its status into workstat and
370: * sets haveworkstat to 0; otherwise, haveworkstat is set to -1;
371: * Similarly for the RCS file and the variables RCSstat and haveRCSstat.
372: *
373: * If the RCS file exists, it is opened for reading, the file pointer
374: * is placed into finptr, and the admin-node is read in; returns 1.
375: * If the RCS file does not exist and mustread==true, an error is printed
376: * and 0 returned.
377: * If the RCS file does not exist and mustread==false, the admin node
378: * is initialized to empty (Head, AccessList, Locks, Symbols, StrictLocks, Dbranch)
379: * and -1 returned.
380: *
381: * 0 is returned on all errors. Files that are directories are errors.
382: * Also calls InitCleanup();
383: */
384: {
385: register char * sp, * tp;
386: char * lastsep, * purefname, * pureRCSname;
387: int opened, returncode;
388: char * RCS1;
389: char prefdir[NCPPN];
390:
391: if (*argv == nil) return 0; /* already paired filename */
392: if (rindex(*argv,KDELIM)!=0) {
393: /* KDELIM causes havoc in keyword expansion */
394: error("RCS file name may not contain %c",KDELIM);
395: return 0;
396: }
397: InitCleanup();
398:
399: /* first check suffix to see whether it is an RCS file or not */
400: purefname=bindex(*argv, '/'); /* skip path */
401: lastsep=rindex(purefname, RCSSEP);
402: if (lastsep!= 0 && *(lastsep+1)==RCSSUF && *(lastsep+2)=='\0') {
403: /* RCS file name given*/
404: RCS1=(*argv); pureRCSname=purefname;
405: /* derive workfilename*/
406: sp = purefname; tp=tempfilename;
407: while (sp<lastsep) *tp++ = *sp++; *tp='\0';
408: /* try to find workfile name among arguments */
409: workfilename=findpairfile(argc-1,argv+1,tempfilename);
410: if (strlen(pureRCSname)>NCPFN) {
411: error("RCS file name %s too long",RCS1);
412: return 0;
413: }
414: } else {
415: /* working file given; now try to find RCS file */
416: workfilename= *argv;
417: /* derive RCS file name*/
418: sp=purefname; tp=tempfilename;
419: while (*tp++ = *sp++);
420: *(tp-1)=RCSSEP; *tp++=RCSSUF; *tp++='\0';
421: /* Try to find RCS file name among arguments*/
422: RCS1=findpairfile(argc-1,argv+1,tempfilename);
423: pureRCSname=bindex(RCS1, '/');
424: if (strlen(pureRCSname)>NCPFN) {
425: error("working file name %s too long",workfilename);
426: return 0;
427: }
428: }
429: /* now we have a (tentative) RCS filename in RCS1 and workfilename */
430: /* First, get status of workfilename */
431: haveworkstat=stat(workfilename, &workstat);
432: if ((haveworkstat==0) && ((workstat.st_mode & S_IFDIR) == S_IFDIR)) {
433: diagnose("Directory %s ignored",workfilename);
434: return 0;
435: }
436: /* Second, try to find the right RCS file */
437: if (pureRCSname!=RCS1) {
438: /* a path for RCSfile is given; single RCS file to look for */
439: finptr=fopen(RCSfilename=RCS1, "r");
440: if (finptr!=NULL) {
441: returncode=1;
442: } else { /* could not open */
443: if (access(RCSfilename,0)==0) {
444: error("Can't open existing %s", RCSfilename);
445: return 0;
446: }
447: if (mustread) {
448: error("Can't find %s", RCSfilename);
449: return 0;
450: } else {
451: /* initialize if not mustread */
452: returncode = -1;
453: }
454: }
455: } else {
456: /* no path for RCS file name. Prefix it with path of work */
457: /* file if RCS file omitted. Make a second name including */
458: /* RCSDIR and try to open that one first. */
459: sub1filename[0]=sub2filename[0]= '\0';
460: if (RCS1==tempfilename) {
461: /* RCS file name not given; prepend work path */
462: sp= *argv; tp= sub1filename;
463: while (sp<purefname) *tp++ = *sp ++;
464: *tp='\0';
465: VOID strcpy(sub2filename,sub1filename); /* second one */
466: }
467: VOID strcat(sub1filename,RCSDIR);
468: VOID strcpy(prefdir,sub1filename); /* preferred directory for RCS file*/
469: VOID strcat(sub1filename,RCS1); VOID strcat(sub2filename,RCS1);
470:
471:
472: opened=(
473: ((finptr=fopen(RCSfilename=sub1filename, "r"))!=NULL) ||
474: ((finptr=fopen(RCSfilename=sub2filename,"r"))!=NULL) );
475:
476: if (opened) {
477: /* open succeeded */
478: returncode=1;
479: } else {
480: /* open failed; may be read protected */
481: if ((access(RCSfilename=sub1filename,0)==0) ||
482: (access(RCSfilename=sub2filename,0)==0)) {
483: error("Can't open existing %s",RCSfilename);
484: return 0;
485: }
486: if (mustread) {
487: error("Can't find %s nor %s",sub1filename,sub2filename);
488: return 0;
489: } else {
490: /* initialize new file. Put into ./RCS if possible, strip off suffix*/
491: RCSfilename= (access(prefdir,0)==0)?sub1filename:sub2filename;
492: returncode= -1;
493: }
494: }
495: }
496:
497: if (returncode == 1) { /* RCS file open */
498: haveRCSstat=fstat(fileno(finptr),&RCSstat);
499: if ((haveRCSstat== 0) && ((RCSstat.st_mode & S_IFDIR) == S_IFDIR)) {
500: diagnose("Directory %s ignored",RCSfilename);
501: return 0;
502: }
503: Lexinit(); getadmin();
504: } else { /* returncode == -1; RCS file nonexisting */
505: haveRCSstat = -1;
506: InitAdmin();
507: };
508:
509: if (tostdout&&
510: !(RCS1==tempfilename||workfilename==tempfilename))
511: /*The last term determines whether a pair of */
512: /* file names was given in the argument list */
513: warn("Option -p is set; ignoring output file %s",workfilename);
514:
515: return returncode;
516: }
517:
518:
519: char * getfullRCSname()
520: /* Function: returns a pointer to the full path name of the RCS file.
521: * Calls getwd(), but only once.
522: * removes leading "../" and "./".
523: */
524: { static char pathbuf[NCPPN];
525: static char namebuf[NCPPN];
526: static int pathlength =0;
527:
528: register char * realname, * lastpathchar;
529: register int dotdotcounter, realpathlength;
530:
531: if (*RCSfilename=='/') {
532: return(RCSfilename);
533: } else {
534: if (pathlength==0) { /*call curdir for the first time*/
535: if (getwd(pathbuf)==NULL)
536: faterror("Can't build current directory path");
537: pathlength=strlen(pathbuf);
538: if (!((pathlength==1) && (pathbuf[0]=='/'))) {
539: pathbuf[pathlength++]='/';
540: /* Check needed because some getwd implementations */
541: /* generate "/" for the root. */
542: }
543: }
544: /*the following must be redone since RCSfilename may change*/
545: /* find how many ../ to remvove from RCSfilename */
546: dotdotcounter =0;
547: realname = RCSfilename;
548: while( realname[0]=='.' &&
549: (realname[1]=='/'||(realname[1]=='.'&&realname[2]=='/'))){
550: if (realname[1]=='/') {
551: /* drop leading ./ */
552: realname += 2;
553: } else {
554: /* drop leading ../ and remember */
555: dotdotcounter++;
556: realname += 3;
557: }
558: }
559: /* now remove dotdotcounter trailing directories from pathbuf*/
560: lastpathchar=pathbuf + pathlength-1;
561: while (dotdotcounter>0 && lastpathchar>pathbuf) {
562: /* move pointer backwards over trailing directory */
563: lastpathchar--;
564: if (*lastpathchar=='/') {
565: dotdotcounter--;
566: }
567: }
568: if (dotdotcounter>0) {
569: error("Can't generate full path name for RCS file");
570: return RCSfilename;
571: } else {
572: /* build full path name */
573: realpathlength=lastpathchar-pathbuf+1;
574: VOID strncpy(namebuf,pathbuf,realpathlength);
575: VOID strcpy(&namebuf[realpathlength],realname);
576: return(namebuf);
577: }
578: }
579: }
580:
581:
582:
583: int trydiraccess(filename)
584: char * filename;
585: /* checks write permission in directory of filename and returns
586: * true if writable, false otherwise
587: */
588: {
589: char pathname[NCPPN];
590: register char * tp, *sp, *lp;
591: lp = rindex(filename,'/');
592: if (lp==0) {
593: /* check current directory */
594: if (access(".",2)==0)
595: return true;
596: else {
597: error("Current directory not writable");
598: return false;
599: }
600: }
601: /* copy path */
602: sp=filename;
603: tp=pathname;
604: do *tp++ = *sp++; while (sp<=lp);
605: *tp='\0';
606: if (access(pathname,2)==0)
607: return true;
608: else {
609: error("Directory %s not writable", pathname);
610: return false;
611: }
612: }
613:
614:
615:
616: #ifndef V4_2BSD
617: /* rename() and getwd() will be provided in bsd 4.2 */
618:
619:
620: int rename(from, to)
621: char * from, *to;
622: /* Function: renames a file with the name given by from to the name given by to.
623: * unlinks the to-file if it already exists. returns -1 on error, 0 otherwise.
624: */
625: { VOID unlink(to); /* no need to check return code; will be caught by link*/
626: /* no harm done if file "to" does not exist */
627: if (link(from,to)<0) return -1;
628: return(unlink(from));
629: }
630:
631:
632:
633: #define dot "."
634: #define dotdot ".."
635:
636:
637:
638: char * getwd(name)
639: char * name;
640: /* Function: places full pathname of current working directory into name and
641: * returns name on success, NULL on failure.
642: * getwd is an adaptation of pwd. May not return to the current directory on
643: * failure.
644: */
645: {
646: FILE *file;
647: struct stat d, dd;
648: char buf[2]; /* to NUL-terminate dir.d_name */
649: struct direct dir;
650:
651: int rdev, rino;
652: int off;
653: register i,j;
654:
655: name[off= 0] = '/';
656: name[1] = '\0';
657: buf[0] = '\0';
658: stat("/", &d);
659: rdev = d.st_dev;
660: rino = d.st_ino;
661: for (;;) {
662: if (stat(dot, &d)<0) return NULL;
663: if (d.st_ino==rino && d.st_dev==rdev) {
664: if (name[off] == '/') name[off] = '\0';
665: chdir(name); /*change back to current directory*/
666: return name;
667: }
668: if ((file = fopen(dotdot,"r")) == NULL) return NULL;
669: if (fstat(fileno(file), &dd)<0) goto fail;
670: chdir(dotdot);
671: if(d.st_dev == dd.st_dev) {
672: if(d.st_ino == dd.st_ino) {
673: if (name[off] == '/') name[off] = '\0';
674: chdir(name); /*change back to current directory*/
675: VOID fclose(file);
676: return name;
677: }
678: do {
679: if (fread((char *)&dir, sizeof(dir), 1, file) !=1)
680: goto fail;
681: } while (dir.d_ino != d.st_ino);
682: }
683: else do {
684: if(fread((char *)&dir, sizeof(dir), 1, file) != 1) {
685: goto fail;
686: }
687: stat(dir.d_name, &dd);
688: } while(dd.st_ino != d.st_ino || dd.st_dev != d.st_dev);
689: VOID fclose(file);
690:
691: /* concatenate file name */
692: i = -1;
693: while (dir.d_name[++i] != 0);
694: for(j=off+1; j>0; --j)
695: name[j+i+1] = name[j];
696: off=i+off+1;
697: name[i+1] = '/';
698: for(--i; i>=0; --i)
699: name[i+1] = dir.d_name[i];
700: } /* end for */
701:
702: fail: VOID fclose(file);
703: return NULL;
704: }
705:
706:
707: #endif
708:
709:
710: #ifdef PAIRTEST
711: /* test program for pairfilenames() and getfullRCSname() */
712: char * workfilename, *RCSfilename;
713: extern int quietflag;
714:
715: main(argc, argv)
716: int argc; char *argv[];
717: {
718: int result;
719: int initflag,tostdout;
720: quietflag=tostdout=initflag=false;
721: cmdid="pair";
722:
723: while(--argc, ++argv, argc>=1 && ((*argv)[0] == '-')) {
724: switch ((*argv)[1]) {
725:
726: case 'p': tostdout=true;
727: break;
728: case 'i': initflag=true;
729: break;
730: case 'q': quietflag=true;
731: break;
732: default: error("unknown option: %s", *argv);
733: break;
734: }
735: }
736:
737: do {
738: RCSfilename=workfilename=nil;
739: result=pairfilenames(argc,argv,!initflag,tostdout);
740: if (result!=0) {
741: diagnose("RCSfile: %s; working file: %s",RCSfilename,workfilename);
742: diagnose("Full RCS file name: %s", getfullRCSname());
743: }
744: switch (result) {
745: case 0: continue; /* already paired file */
746:
747: case 1: if (initflag) {
748: error("RCS file %s exists already",RCSfilename);
749: } else {
750: diagnose("RCS file %s exists",RCSfilename);
751: }
752: VOID fclose(finptr);
753: break;
754:
755: case -1:diagnose("RCS file does not exist");
756: break;
757: }
758:
759: } while (++argv, --argc>=1);
760:
761: }
762: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.