/* * RCS file name handling */ static char rcsid[]= "$Header: rcsfnms.c,v 3.9 86/05/15 02:24:55 lepreau Exp $ Purdue CS"; /**************************************************************************** * creation and deletion of semaphorefile, * creation of temporary filenames and cleanup() * pairing of RCS file names and working file names. * Testprogram: define PAIRTEST **************************************************************************** * * Copyright (C) 1982 by Walter F. Tichy * Purdue University * Computer Science Department * West Lafayette, IN 47907 * * All rights reserved. No part of this software may be sold or distributed * in any form or by any means without the prior written permission of the * author. * Report problems and direct all inquiries to Tichy@purdue (ARPA net). */ /* $Log: rcsfnms.c,v $ * Revision 3.9 86/05/15 02:24:55 lepreau * add suffix .el for gnulisp * * Revision 3.8 86/01/12 01:20:29 lepreau * add suffixes csh,cl,sl,red for csh, common lisp, * psl, and rlisp respectively. * * Revision 3.7 83/05/11 15:01:58 wft * *** empty log message *** * * Revision 3.7 83/05/11 15:01:58 wft * Added comtable[] which pairs file name suffixes with comment leaders; * updated InitAdmin() accordingly. * * Revision 3.6 83/04/05 14:47:36 wft * fixed Suffix in InitAdmin(). * * Revision 3.5 83/01/17 18:01:04 wft * Added getwd() and rename(); these can be removed by defining * V4_2BSD, since they are not needed in 4.2 bsd. * Changed sys/param.h to sys/types.h. * * Revision 3.4 82/12/08 21:55:20 wft * removed unused variable. * * Revision 3.3 82/11/28 20:31:37 wft * Changed mktempfile() to store the generated file names. * Changed getfullRCSname() to store the file and pathname, and to * delete leading "../" and "./". * * Revision 3.2 82/11/12 14:29:40 wft * changed pairfilenames() to handle file.sfx,v; also deleted checkpathnosfx(), * checksuffix(), checkfullpath(). Semaphore name generation updated. * mktempfile() now checks for nil path; lastfilename initialized properly. * Added Suffix .h to InitAdmin. Added testprogram PAIRTEST. * Moved rmsema, trysema, trydiraccess, getfullRCSname from rcsutil.c to here. * * Revision 3.1 82/10/18 14:51:28 wft * InitAdmin() now initializes StrictLocks=STRICT_LOCKING (def. in rcsbase.h). * renamed checkpath() to checkfullpath(). */ #include "rcsbase.h" extern char * rindex(); extern char * mktemp(); extern char * malloc(); extern FILE * fopen(); extern char * getwd(); /* get working directory; forward decl */ extern FILE * finptr; /* RCS input file descriptor */ extern FILE * frewrite; /* New RCS file descriptor */ extern char * RCSfilename, * workfilename; /* filenames */ char tempfilename [NCPFN+10]; /* used for derived file names */ char subfilename [NCPFN+14]; /* used for files RCS/file.sfx,v */ char semafilename [NCPPN]; /* name of semaphore file */ int madesema; /* indicates whether a semaphore file has been set */ char * tfnames[10] = /* temp. file names to be unlinked when finished */ {nil,nil,nil,nil,nil,nil,nil,nil,nil,nil}; int lastfilename = -1;/* index of last file name in tfnames[] */ struct compair { char * suffix, * comlead; }; struct compair comtable[] = { /* comtable pairs each filename suffix with a comment leader. The comment */ /* leader is placed before each line generated by the $Log keyword. This */ /* table is used to guess the proper comment leader from the working file's */ /* suffix during initial ci (see InitAdmin()). Comment leaders are needed */ /* for languages without multiline comments; for others they are optional. */ "c", " * ", /* C */ "h", " * ", /* C-header */ "p", " * ", /* pascal */ "sh", "# ", /* shell */ "csh", "# ", /* shell */ "s", "# ", /* assembler */ "sl", "% ", /* psl */ "red", "% ", /* psl/rlisp */ "cl", ";;; ", /* common lisp */ "r", "# ", /* ratfor */ "e", "# ", /* efl */ "l", " * ", /* lex NOTE: conflict between lex and franzlisp*/ "y", " * ", /* yacc */ "yr", " * ", /* yacc-ratfor */ "ye", " * ", /* yacc-efl */ "ml", "; ", /* mocklisp */ "el", "; ", /* gnulisp */ "mac", "; ", /* macro vms or dec-20 or pdp-11 macro */ "f", "c ", /* fortran */ "ms", "\\\" ", /* ms-macros t/nroff*/ "me", "\\\" ", /* me-macros t/nroff*/ "", "# ", /* default for empty suffix */ nil, "" /* default for unknown suffix; must always be last */ }; ffclose(fptr) FILE * fptr; /* Function: checks ferror(fptr) and aborts the program if there were * errors; otherwise closes fptr. */ { if (ferror(fptr) || fclose(fptr)==EOF) faterror("File read or write error; file system full?"); } int trysema(RCSfilename,makesema) char * RCSfilename; int makesema; /* Function: Checks whether a semaphore file exists for RCSfilename. If yes, * returns false. If not, creates one if makesema==true and returns true * if successful. If a semaphore file was created, madesema is set to true. * The name of the semaphore file is put into variable semafilename. */ { register char * tp, *sp, *lp; int fdesc; sp=RCSfilename; lp = rindex(sp,'/'); if (lp==0) { semafilename[0]='.'; semafilename[1]='/'; tp= &semafilename[2]; } else { /* copy path */ tp=semafilename; do *tp++ = *sp++; while (sp<=lp); } /*now insert `,' and append file name */ *tp++ = ','; lp = rindex(sp, RCSSEP); while (sp0; next++,count--) { if ((*next != nil) && strcmp(bindex(*next,'/'),fname)==0) { /* bindex finds the beginning of the file name stem */ match= *next; *next=nil; return match; } } return fname; } int pairfilenames(argc, argv, mustread, tostdout) int argc; char ** argv; int mustread, tostdout; /* Function: Pairs the filenames pointed to by argv; argc indicates * how many there are. * Places a pointer to the RCS filename into RCSfilename, * and a pointer to the name of the working file into workfilename. * If both the workfilename and the RCS filename are given, and tostdout * is true, a warning is printed. * * If the RCS file exists, it is opened for reading, the file pointer * is placed into finptr, and the admin-node is read in; returns 1. * If the RCS file does not exist and mustread==true, an error is printed * and 0 returned. * If the RCS file does not exist and mustread==false, the admin node * is initialized to empty (Head, AccessList, Locks, Symbols, StrictLocks), * and -1 returned. * * 0 is returned on all errors. * Also calls InitCleanup(); */ { register char * sp, * tp; char * lastsep, * purefname, * pureRCSname; int opened, returncode; char * RCS1; if (*argv == nil) return 0; /* already paired filename */ InitCleanup(); /* first check suffix to see whether it is an RCS file or not */ purefname=bindex(*argv, '/'); /* skip path */ lastsep=rindex(purefname, RCSSEP); if (lastsep!= 0 && *(lastsep+1)==RCSSUF && *(lastsep+2)=='\0') { /* RCS file name given*/ RCS1=(*argv); pureRCSname=purefname; /* derive workfilename*/ sp = purefname; tp=tempfilename; while (spNCPFN) { error("RCS file name %s too long",RCS1); return 0; } } else { /* working file given; now try to find RCS file */ workfilename= *argv; /* derive RCS file name*/ sp=purefname; tp=tempfilename; while (*tp++ = *sp++); *(tp-1)=RCSSEP; *tp++=RCSSUF; *tp++='\0'; /* Try to find RCS file name among arguments*/ RCS1=findpairfile(argc-1,argv+1,tempfilename); pureRCSname=bindex(RCS1, '/'); if (strlen(pureRCSname)>NCPFN) { error("working file name %s too long",workfilename); return 0; } } /* now we have a (tentative) RCS filename in RCS1 and workfilename */ if (pureRCSname!=RCS1) { /* a path for RCSfile is given; single RCS file to look for */ finptr=fopen(RCSfilename=RCS1, "r"); if (finptr!=NULL) { Lexinit(); getadmin(); returncode=1; } else { /* could not open */ if (access(RCSfilename,0)==0) { error("Can't open existing %s", RCSfilename); return 0; } if (mustread) { error("Can't find %s", RCSfilename); return 0; } else { /* initialize if not mustread */ InitAdmin(); returncode = -1; } } } else { /* build second RCS file name by prefixing it with RCSDIR*/ /* then try to open one of them */ strcpy(subfilename,RCSDIR); strcat(subfilename,RCS1); opened=( ((finptr=fopen(RCSfilename=subfilename, "r"))!=NULL) || ((finptr=fopen(RCSfilename=RCS1,"r"))!=NULL) ); if (opened) { /* open succeeded */ Lexinit(); getadmin(); returncode=1; } else { /* open failed; may be read protected */ if ((access(RCSfilename=subfilename,0)==0) || (access(RCSfilename=RCS1,0)==0)) { error("Can't open existing %s",RCSfilename); return 0; } if (mustread) { error("Can't find %s nor %s",subfilename,RCS1); return 0; } else { /* initialize new file. Put into ./RCS if possible, strip off suffix*/ RCSfilename= (access(RCSDIR,0)==0)?subfilename:RCS1; InitAdmin(); returncode= -1; } } } if (tostdout&& !(RCS1==tempfilename||workfilename==tempfilename)) /*The last term determines whether a pair of */ /* file names was given in the argument list */ warn("Option -p is set; ignoring output file %s",workfilename); return returncode; } char * getfullRCSname() /* Function: returns a pointer to the full path name of the RCS file. * Calls getwd(), but only once. * removes leading "../" and "./". */ { static char pathbuf[NCPPN]; static char namebuf[NCPPN]; static int pathlength =0; register char * realname, * lastpathchar; register int dotdotcounter, realpathlength; if (*RCSfilename=='/') { return(RCSfilename); } else { if (pathlength==0) { /*call curdir for the first time*/ if (getwd(pathbuf)==NULL) faterror("Can't build current directory path"); pathlength=strlen(pathbuf); if (!((pathlength==1) && (pathbuf[0]=='/'))) { pathbuf[pathlength++]='/'; /* Check needed because some getwd implementations */ /* generate "/" for the root. */ } } /*the following must be redone since RCSfilename may change*/ /* find how many ../ to remvove from RCSfilename */ dotdotcounter =0; realname = RCSfilename; while( realname[0]=='.' && (realname[1]=='/'||(realname[1]=='.'&&realname[2]=='/'))){ if (realname[1]=='/') { /* drop leading ./ */ realname += 2; } else { /* drop leading ../ and remember */ dotdotcounter++; realname += 3; } } /* now remove dotdotcounter trailing directories from pathbuf*/ lastpathchar=pathbuf + pathlength-1; while (dotdotcounter>0 && lastpathchar>pathbuf) { /* move pointer backwards over trailing directory */ lastpathchar--; if (*lastpathchar=='/') { dotdotcounter--; } } if (dotdotcounter>0) { error("Can't generate full path name for RCS file"); return RCSfilename; } else { /* build full path name */ realpathlength=lastpathchar-pathbuf+1; strncpy(namebuf,pathbuf,realpathlength); strcpy(&namebuf[realpathlength],realname); return(namebuf); } } } int trydiraccess(filename) char * filename; /* checks write permission in directory of filename and returns * true if writable, false otherwise */ { char pathname[NCPPN]; register char * tp, *sp, *lp; lp = rindex(filename,'/'); if (lp==0) { /* check current directory */ if (access(".",2)==0) return true; else { error("Current directory not writable"); return false; } } /* copy path */ sp=filename; tp=pathname; do *tp++ = *sp++; while (sp<=lp); *tp='\0'; if (access(pathname,2)==0) return true; else { error("Directory %s not writable", pathname); return false; } } #ifndef V4_2BSD /* rename() and getwd() will be provided in bsd 4.2 */ int rename(from, to) char * from, *to; /* Function: renames a file with the name given by from to the name given by to. * unlinks the to-file if it already exists. returns -1 on error, 0 otherwise. */ { unlink(to); /* no need to check return code; will be caught by link*/ /* no harm done if file "to" does not exist */ if (link(from,to)<0) return -1; return(unlink(from)); } #include #include #include #define dot "." #define dotdot ".." char * getwd(name) char * name; /* Function: places full pathname of current working directory into name and * returns name on success, NULL on failure. * getwd is an adaptation of pwd. May not return to the current directory on * failure. */ { FILE *file; struct stat d, dd; char buf[2]; /* to NUL-terminate dir.d_name */ struct direct dir; int rdev, rino; int off; register i,j; name[off= 0] = '/'; name[1] = '\0'; buf[0] = '\0'; stat("/", &d); rdev = d.st_dev; rino = d.st_ino; for (;;) { if (stat(dot, &d)<0) return NULL; if (d.st_ino==rino && d.st_dev==rdev) { if (name[off] == '/') name[off] = '\0'; chdir(name); /*change back to current directory*/ return name; } if ((file = fopen(dotdot,"r")) == NULL) return NULL; if (fstat(fileno(file), &dd)<0) goto fail; chdir(dotdot); if(d.st_dev == dd.st_dev) { if(d.st_ino == dd.st_ino) { if (name[off] == '/') name[off] = '\0'; chdir(name); /*change back to current directory*/ fclose(file); return name; } do { if (fread((char *)&dir, sizeof(dir), 1, file) !=1) goto fail; } while (dir.d_ino != d.st_ino); } else do { if(fread((char *)&dir, sizeof(dir), 1, file) != 1) { goto fail; } stat(dir.d_name, &dd); } while(dd.st_ino != d.st_ino || dd.st_dev != d.st_dev); fclose(file); /* concatenate file name */ i = -1; while (dir.d_name[++i] != 0); for(j=off+1; j>0; --j) name[j+i+1] = name[j]; off=i+off+1; name[i+1] = '/'; for(--i; i>=0; --i) name[i+1] = dir.d_name[i]; } /* end for */ fail: fclose(file); return NULL; } #endif #ifdef PAIRTEST /* test program for pairfilenames() and getfullRCSname() */ char * workfilename, *RCSfilename; main(argc, argv) int argc; char *argv[]; { int result; int initflag,tostdout; tostdout=initflag=false; cmdid="pair"; while(--argc, ++argv, argc>=1 && ((*argv)[0] == '-')) { switch ((*argv)[1]) { case 'p': tostdout=true; break; case 'i': initflag=true; break; } } do { RCSfilename=workfilename=nil; result=pairfilenames(argc,argv,!initflag,tostdout); if (result!=0) { diagnose("RCSfile: %s; working file: %s",RCSfilename,workfilename); diagnose("Full RCS file name: %s", getfullRCSname()); } switch (result) { case 0: continue; /* already paired file */ case 1: if (initflag) { error("RCS file exists already"); continue; } else { diagnose("RCS file exists"); } break; case -1:diagnose("RCS file does not exist"); break; } } while (++argv, --argc>=1); } #endif