home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / RCS_SRC.ZIP / RCSFNMS.C < prev    next >
C/C++ Source or Header  |  1991-01-15  |  29KB  |  785 lines

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