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