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