home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / OSK / CMDS / rcs4.lha / rcsfnms.c < prev    next >
Text File  |  1993-03-03  |  28KB  |  782 lines

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