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