home *** CD-ROM | disk | FTP | other *** search
/ Power Programmierung 2 / Power-Programmierung CD 2 (Tewi)(1994).iso / gnu / gnulib / rcs4 / source / rcsfnms.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-03-08  |  33.9 KB  |  1,006 lines

  1. /*
  2.  *                     RCS file name handling
  3.  */
  4. #ifndef lint
  5.  static char
  6.  rcsid[]= "$Id: rcsfnms.c 5.8 91/03/08 14:11:59 ROOT_DOS 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 $
  45.  * Revision 5.8  91/03/08  14:11:59  ROOT_DOS
  46.  * Was dereferencing NULL pointer if TMP env variable not set
  47.  * 
  48.  * Revision 5.7  91/02/10  15:58:37  ROOT_DOS
  49.  * Remove two more differences between DOS & Unix with calls to unixize
  50.  * 
  51.  * Revision 5.6  91/02/07  13:50:39  ROOT_DOS
  52.  * Make all DOS file names look like Unix ones
  53.  * 
  54.  * Revision 5.5  90/07/16  21:30:57  lfk
  55.  * checked in with -k by ROOT_DOS at 91.02.07.11.45.23.
  56.  * 
  57.  * Revision 5.5  90/07/16  21:30:57  lfk
  58.  * fixed a number of mistakes in the MKS/DOS filenaming stuff
  59.  * 
  60.  * Revision 5.4  90/07/15  23:42:35  lfk
  61.  * Fixed one last '\\' versus '/' problem
  62.  * 
  63.  * Revision 5.3  90/07/15  20:25:06  lfk
  64.  * Most major fixes added between rev 5.1 and rev 5.5:
  65.  *     signals fixed so they work on MS-DOS
  66.  *     Added MKS arguments code so argv can be large
  67.  *     added code to handle slashes a'la Unix
  68.  *     added more file extensions to system from MS-DOS
  69.  * 
  70.  * Revision 5.2  90/07/15  11:33:42  ROOT_DOS
  71.  * DOS version of RCS 4.0 checked in for MODS
  72.  * by lfk@athena.mit.edu
  73.  * Also update to MSC 6.0
  74.  * 
  75.  * revision 5.2 koya 90/01/24 10:22:39
  76.  * Change path-name handlars.
  77.  * Especially, separator of path names.
  78.  * 
  79.  * revision 5.1 koya 90/01/24 07:05:43
  80.  * Initial revision
  81.  * 
  82.  * Revision 4.8  89/05/01  15:09:41  narten
  83.  * changed getwd to not stat empty directories.
  84.  * 
  85.  * Revision 4.7  88/11/08  12:01:22  narten
  86.  * changes from  eggert@sm.unisys.com (Paul Eggert)
  87.  * 
  88.  * Revision 4.7  88/08/09  19:12:53  eggert
  89.  * Fix troff macro comment leader bug; add Prolog; allow cc -R; remove lint.
  90.  * 
  91.  * Revision 4.6  87/12/18  11:40:23  narten
  92.  * additional file types added from 4.3 BSD version, and SPARC assembler
  93.  * comment character added. Also, more lint cleanups. (Guy Harris)
  94.  * 
  95.  * Revision 4.5  87/10/18  10:34:16  narten
  96.  * Updating version numbers. Changes relative to 1.1 actually relative
  97.  * to verion 4.3
  98.  * 
  99.  * Revision 1.3  87/03/27  14:22:21  jenkins
  100.  * Port to suns
  101.  * 
  102.  * Revision 1.2  85/06/26  07:34:28  svb
  103.  * Comment leader '% ' for '*.tex' files added.
  104.  * 
  105.  * Revision 1.1  84/01/23  14:50:24  kcs
  106.  * Initial revision
  107.  * 
  108.  * Revision 4.3  83/12/15  12:26:48  wft
  109.  * Added check for KDELIM in file names to pairfilenames().
  110.  * 
  111.  * Revision 4.2  83/12/02  22:47:45  wft
  112.  * Added csh, red, and sl file name suffixes.
  113.  * 
  114.  * Revision 4.1  83/05/11  16:23:39  wft
  115.  * Added initialization of Dbranch to InitAdmin(). Canged pairfilenames():
  116.  * 1. added copying of path from workfile to RCS file, if RCS file is omitted;
  117.  * 2. added getting the file status of RCS and working files;
  118.  * 3. added ignoring of directories.
  119.  * 
  120.  * Revision 3.7  83/05/11  15:01:58  wft
  121.  * Added comtable[] which pairs file name suffixes with comment leaders;
  122.  * updated InitAdmin() accordingly.
  123.  * 
  124.  * Revision 3.6  83/04/05  14:47:36  wft
  125.  * fixed Suffix in InitAdmin().
  126.  * 
  127.  * Revision 3.5  83/01/17  18:01:04  wft
  128.  * Added getwd() and rename(); these can be removed by defining
  129.  * V4_2BSD, since they are not needed in 4.2 bsd.
  130.  * Changed sys/param.h to sys/types.h.
  131.  *
  132.  * Revision 3.4  82/12/08  21:55:20  wft
  133.  * removed unused variable.
  134.  *
  135.  * Revision 3.3  82/11/28  20:31:37  wft
  136.  * Changed mktempfile() to store the generated file names.
  137.  * Changed getfullRCSname() to store the file and pathname, and to
  138.  * delete leading "../" and "./".
  139.  *
  140.  * Revision 3.2  82/11/12  14:29:40  wft
  141.  * changed pairfilenames() to handle file.sfx,v; also deleted checkpathnosfx(),
  142.  * checksuffix(), checkfullpath(). Semaphore name generation updated.
  143.  * mktempfile() now checks for nil path; freefilename initialized properly.
  144.  * Added Suffix .h to InitAdmin. Added testprogram PAIRTEST.
  145.  * Moved rmsema, trysema, trydiraccess, getfullRCSname from rcsutil.c to here.
  146.  *
  147.  * Revision 3.1  82/10/18  14:51:28  wft
  148.  * InitAdmin() now initializes StrictLocks=STRICT_LOCKING (def. in rcsbase.h).
  149.  * renamed checkpath() to checkfullpath().
  150.  */
  151.  
  152.  
  153. #include "rcsbase.h"
  154. #include <sys/types.h>
  155. #include <sys/stat.h>
  156. #ifdef MSDOS
  157. #include <direct.h>
  158. #else
  159. #include <sys/dir.h>
  160. #endif /* MSDOS */
  161.  
  162. extern char * rindex();
  163. extern char * mktemp();
  164. extern FILE * fopen();
  165. extern char * getwd();         /* get working directory; forward decl       */
  166. extern int    stat(), fstat();
  167.  
  168. extern FILE * finptr;          /* RCS input file descriptor                 */
  169. extern FILE * frewrite;        /* New RCS file descriptor                   */
  170. extern char * RCSfilename, * workfilename; /* filenames                     */
  171. struct stat RCSstat, workstat; /* file status for RCS file and working file */
  172. int    haveRCSstat,  haveworkstat; /* indicators if status availalble       */
  173.  
  174.  
  175. char tempfilename [NCPFN+10];  /* used for derived file names               */
  176. char sub1filename [NCPPN];     /* used for files path/file.sfx,v            */
  177. char sub2filename [NCPPN];     /* used for files path/RCS/file.sfx,v        */
  178. char semafilename [NCPPN];     /* name of semaphore file                    */
  179. int  madesema;                 /* indicates whether a semaphore file has been set */
  180. char * tfnames[10];            /* temp. file names to be unlinked when finished   */
  181. int  freefilename;             /* index of next free file name in tfnames[]  */
  182.  
  183.  
  184. struct compair {
  185.         char * suffix, * comlead;
  186. };
  187.  
  188. struct compair comtable[] = {
  189. /* comtable pairs each filename suffix with a comment leader. The comment   */
  190. /* leader is placed before each line generated by the $Log keyword. This    */
  191. /* table is used to guess the proper comment leader from the working file's */
  192. /* suffix during initial ci (see InitAdmin()). Comment leaders are needed   */
  193. /* for languages without multiline comments; for others they are optional.  */
  194. /* According to your MSDOS-environment, you change these settlements. !!    */
  195. /* If you can work on UN*X, it is worthwile to keep these.                  */
  196. /* It however, may casue lack of memory on MSDOS                           */ 
  197. /* $Author: ROOT_DOS $                                                      */
  198.         "",        "# ",        /* default for empty suffix */
  199.         "awk",    "# ",        /* AWK */
  200.         "c",    " * ",        /* C           */
  201. #ifdef ALL
  202.         "c++"    "// "        /* C++ */
  203.         "cc"    "// "        /* C++ */
  204.         "CC"    "// "        /* C++ */
  205.         "C"        "// "        /* C++ */
  206.         "cl",    ";;; ",        /* common lisp */
  207.         "csh",    "# ",        /* shell       */
  208.         "e",    "# ",        /* efl         */
  209.         "el",    "; ",        /* gnulisp     */
  210. #endif
  211.         "f",    "c ",        /* fortran     */
  212.         "h",    " * ",        /* C-header    */
  213.         "ksh",    "# ",        /* korn shell  */
  214.         "l",    " * ",        /* lex NOTE: conflict between lex and franzlisp*/
  215.         "mac",    "; ",        /* macro vms or dec-20 or pdp-11 macro */
  216.         "me",    ".\\\" ",    /* me-macros   t/nroff*/
  217. #ifdef ALL
  218.         "ml",    "; ",        /* mocklisp    */
  219. #endif
  220.         "mm",    ".\\\" ",    /* mm-macros   t/nroff*/
  221.         "ms",    ".\\\" ",    /* ms-macros   t/nroff*/
  222. #ifdef ALL
  223.         "p",    " * ",        /* pascal      */
  224.         "perl",    "# ",        /* L. Wall's Perl */
  225.         "pl",    "% ",        /* prolog      */
  226.         "r",    "# ",        /* ratfor      */
  227.         "red",    "% ",        /* psl/rlisp   */
  228.         "sh",    "# ",        /* shell       */
  229.         "sl",    "% ",        /* psl         */
  230. #endif
  231.         "tex",    "% ",        /* tex           */
  232.         "y",    " * ",        /* yacc        */
  233. #ifdef ALL
  234.         "ye",    " * ",        /* yacc-efl    */
  235.         "yr",    " * ",        /* yacc-ratfor */
  236. #endif
  237. #ifdef sparc
  238.         "s",    "! ",    /* assembler   */
  239. #endif
  240. #ifdef mc68000
  241.         "s",    "| ",    /* assembler   */
  242. #endif
  243. #ifdef pdp11
  244.         "s",    "/ ",    /* assembler   */
  245. #endif
  246. #ifdef vax
  247.         "s",    "# ",    /* assembler   */
  248. #endif
  249. #ifdef MSDOS 
  250.         "asm",    "; ",    /* assembler   */
  251.         "bat",    "REM ",    /* MS-DOS command.com */
  252.         "cpp"    "// "    /* Zortech C++ */
  253.         "cxx",    "// ",    /* MS-DOS c++ */
  254.         "fin",    ".\\\" ",    /* for *.fin files on MSDOS */
  255.         "fma",    ".\\\" ",    /* for fma-macors fin on MSDOS */    
  256.         "for",    "c ",    /* MS Fortran */
  257.         "hpp"    "// "    /* Zortech C++ */
  258.         "pl",    "# ",    /* L. Wall's Perl for MS-DOS */
  259. #endif /* MSDOS */
  260.         nil,   ""       /* default for unknown suffix; must always be last */
  261. };
  262.  
  263.  
  264. ffclose(fptr)
  265. FILE * fptr;
  266. /* Function: checks ferror(fptr) and aborts the program if there were
  267.  * errors; otherwise closes fptr.
  268.  */
  269. {       if (ferror(fptr) || fclose(fptr)==EOF)
  270.                 faterror("File read or write error; file system full?");
  271. }
  272.  
  273.  
  274. int trysema(RCSname,makesema)
  275. char * RCSname; int makesema;
  276. /* Function: Checks whether a semaphore file exists for RCSname. If yes,
  277.  * returns false. If not, creates one if makesema==true and returns true
  278.  * if successful. If a semaphore file was created, madesema is set to true.
  279.  * The name of the semaphore file is put into variable semafilename.
  280.  */
  281. {
  282. #ifndef MSDOS
  283.         register char * tp, *sp, *lp;
  284.         int fdesc;
  285.  
  286.         sp=RCSname;
  287.         lp = rindex(sp,'/');
  288.         if (lp==0) {
  289.                 semafilename[0]='.'; semafilename[1]='/';
  290.                 tp= &semafilename[2];
  291.         } else {
  292.                 /* copy path */
  293.                 tp=semafilename;
  294.                 do *tp++ = *sp++; while (sp<=lp);
  295.         }
  296.         /*now insert `,' and append file name */
  297.         *tp++ = ',';
  298.         lp = rindex(sp, RCSSEP);
  299.         while (sp<lp) *tp++ = *sp++;
  300.         *tp++ = ','; *tp++ = '\0'; /* will be the same length as RCSname*/
  301.  
  302.         madesema = false;
  303.         if (access(semafilename, 0) == 0) {
  304.                 error("RCS file %s is in use",RCSname);
  305.                 return false;
  306.         }
  307.         if (makesema) {
  308.                 if ((fdesc=creat(semafilename, 000)) == -1) {
  309.                      error("Can't create semaphore file for RCS file %s",RCSname);
  310.                      return false;
  311.                 } else
  312.                      VOID close(fdesc);
  313.                      madesema=true;
  314.         }
  315.         return true;
  316. #else /* NOT MSDOS */
  317.     makesema = true;
  318.     return true;
  319. #endif /* NOT MSDOS */
  320. }
  321.  
  322.  
  323. rmsema()
  324. /* Function: delete the semaphore file if madeseam==true;
  325.  * sets madesema to false.
  326.  */
  327. {
  328. #ifdef MSDOS
  329.     madesema = false ;
  330. #else
  331.         if (madesema) {
  332.                 madesema=false;
  333.                 if (unlink(semafilename) == -1) {
  334.                         error("Can't find semaphore file %s",semafilename);
  335.                 }
  336.         }
  337. #endif /* MSDOS */
  338. }
  339.  
  340. InitCleanup()
  341. {       freefilename =  0;  /* initialize pointer */
  342. }
  343.  
  344.  
  345. cleanup()
  346. /* Function: closes input file and rewrite file.
  347.  * Unlinks files in tfnames[], deletes semaphore file.
  348.  */
  349. {
  350.         register int i;
  351.  
  352.         if (finptr!=NULL)   VOID fclose(finptr);
  353.         if (frewrite!=NULL) VOID fclose(frewrite);
  354.         for (i=0; i<freefilename; i++) {
  355.             if (tfnames[i][0]!='\0')  VOID unlink(tfnames[i]);
  356.         }
  357.         InitCleanup();
  358.         rmsema();
  359. }
  360.  
  361.  
  362. #ifdef MSDOS
  363. /*
  364.  * Convert DOS-type file name to Unix-type file name. N.B. All known
  365.  * DOS C compilers will accept unix-type file names in open, unlink,
  366.  * stat, rename etc. functions.
  367.  */
  368.  
  369. void unixize(char *name)
  370. {
  371.     while(*name)
  372.         {
  373.         if(*name == '\\')
  374.             *name = '/';
  375.         else
  376.             *name = tolower(*name);
  377.         name++;
  378.         }
  379. }
  380. #endif
  381.  
  382.  
  383.  
  384. char * mktempfile(fullpath,filename)
  385. register char * fullpath, * filename;
  386. /* Function: Creates a unique filename using the process id and stores it
  387.  * into a free slot in tfnames. The filename consists of the path contained
  388.  * in fullpath concatenated with filename. filename should end in "XXXXXX".
  389.  * Because of storage in tfnames, cleanup() can unlink the file later.
  390.  * freefilename indicates the lowest unoccupied slot in tfnames.
  391.  * Returns a pointer to the filename created.
  392.  * Example use: mktempfile("/tmp/", somefilename)
  393.  */
  394. {
  395.         register char * lastslash, *tp;
  396.         if ((tp=tfnames[freefilename])==nil)
  397.               tp=tfnames[freefilename] = talloc(NCPPN);
  398. #ifdef MSDOS
  399.     if(fullpath != nil)
  400.         unixize(fullpath);
  401. #endif /* MSDOS */
  402.         if (fullpath!=nil && (lastslash=rindex(fullpath,'/'))!=0) {
  403.                 /* copy path */
  404.                 while (fullpath<=lastslash) *tp++ = *fullpath++;
  405.         }
  406.         while (*tp++ = *filename++);
  407.         return (mktemp(tfnames[freefilename++]));
  408. }
  409.  
  410.  
  411.  
  412.  
  413. char * bindex(sp,c)
  414. register char * sp, c;
  415. /* Function: Finds the last occurrence of character c in string sp
  416.  * and returns a pointer to the character just beyond it. If the
  417.  * character doesn't occur in the string, sp is returned.
  418.  */
  419. {       register char * r;
  420.         r = sp;
  421.         while (*sp) {
  422.                 if (*sp++ == c) r=sp;
  423.         }
  424.         return r;
  425. }
  426.  
  427.  
  428.  
  429.  
  430.  
  431. InitAdmin()
  432. /* function: initializes an admin node */
  433. {       register char * Suffix;
  434.         register int i;
  435.  
  436.         Head=Dbranch=nil; AccessList=nil; Symbols=nil; Locks=nil;
  437.         StrictLocks=STRICT_LOCKING;
  438.  
  439.         /* guess the comment leader from the suffix*/
  440.         Suffix=bindex(workfilename, '.');
  441. #ifdef MSDOS
  442.     Suffix = strlwr( Suffix );
  443. #endif /* MSDOS */
  444.         if (Suffix==workfilename) Suffix= ""; /* empty suffix; will get default*/
  445.         for (i=0;;i++) {
  446.                 if (comtable[i].suffix==nil) {
  447.                         Comment=comtable[i].comlead; /*default*/
  448.                         break;
  449.                 } elsif (strcmp(Suffix,comtable[i].suffix)==0) {
  450.                         Comment=comtable[i].comlead; /*default*/
  451.                         break;
  452.                 }
  453.         }
  454.         Lexinit(); /* Note: if finptr==NULL, reads nothing; only initializes*/
  455. }
  456.  
  457.  
  458.  
  459. char * findpairfile(argc, argv, fname)
  460. int argc; char * argv[], *fname;
  461. /* Function: Given a filename fname, findpairfile scans argv for a pathname
  462.  * ending in fname. If found, returns a pointer to the pathname, and sets
  463.  * the corresponding pointer in argv to nil. Otherwise returns fname.
  464.  * argc indicates the number of entries in argv. Some of them may be nil.
  465.  */
  466. {
  467.         register char * * next, * match;
  468.         register int count;
  469.  
  470.         for (next = argv, count = argc; count>0; next++,count--) {
  471. #ifdef MSDOS
  472.         if(*next != nil)
  473.             unixize(*next);
  474. #endif /* MSDOS */
  475.                 if ((*next != nil) && strcmp(bindex(*next,'/'),fname)==0) {
  476.                         /* bindex finds the beginning of the file name stem */
  477.                         match= *next;
  478.                         *next=nil;
  479.                         return match;
  480.                 }
  481.         }
  482.         return fname;
  483. }
  484.  
  485. int pairfilenames(argc, argv, mustread, tostdout)
  486. int argc; char ** argv; int mustread, tostdout;
  487. /* Function: Pairs the filenames pointed to by argv; argc indicates
  488.  * how many there are.
  489.  * Places a pointer to the RCS filename into RCSfilename,
  490.  * and a pointer to the name of the working file into workfilename.
  491.  * If both the workfilename and the RCS filename are given, and tostdout
  492.  * is true, a warning is printed.
  493.  *
  494.  * If the working file exists, places its status into workstat and
  495.  * sets haveworkstat to 0; otherwise, haveworkstat is set to -1;
  496.  * Similarly for the RCS file and the variables RCSstat and haveRCSstat.
  497.  *
  498.  * If the RCS file exists, it is opened for reading, the file pointer
  499.  * is placed into finptr, and the admin-node is read in; returns 1.
  500.  * If the RCS file does not exist and mustread==true, an error is printed
  501.  * and 0 returned.
  502.  * If the RCS file does not exist and mustread==false, the admin node
  503.  * is initialized to empty (Head, AccessList, Locks, Symbols, StrictLocks, Dbranch)
  504.  * and -1 returned.
  505.  *
  506.  * 0 is returned on all errors. Files that are directories are errors.
  507.  * Also calls InitCleanup();
  508.  */
  509. {
  510.         register char * sp, * tp;
  511.         char * lastsep, * purefname, * pureRCSname;
  512.         int opened, returncode;
  513. #ifdef MSDOS
  514.     struct stat dirstat;
  515. #endif /* MSDOS */
  516.         char * RCS1;
  517.     char prefdir[NCPPN];
  518.  
  519.  
  520.         if (*argv == nil) return 0; /* already paired filename */
  521.     if (rindex(*argv,KDELIM)!=0) {
  522.         /* KDELIM causes havoc in keyword expansion    */
  523.         error("RCS file name may not contain %c",KDELIM);
  524.         return 0;
  525.     }
  526.         InitCleanup();
  527.  
  528. #ifdef MSDOS
  529.     unixize(*argv);
  530. #endif
  531.         /* first check suffix to see whether it is an RCS file or not */
  532.         purefname=bindex(*argv, '/'); /* skip path */
  533. #ifdef MSDOS
  534.     if( ( pureRCSname = strstr( *argv, "rcs/" ) ) != NULL){
  535.                 /* RCS file name given*/
  536.         RCS1 = (*argv);
  537.                 /* derive workfilename*/
  538.         sp = purefname; tp=tempfilename;
  539.         while( *sp != '\0' ) *tp++=*sp++; *tp= '\0';
  540.                 workfilename=findpairfile(argc-1,argv+1,tempfilename);
  541.         if( strlen(pureRCSname) > NCPFN + 4 ) {
  542.             /* To avoid, ambiguous file name. eg."rcs/rcs/foo.c" */
  543.             if( strlen(pureRCSname + 4) != 0 ){
  544.                 error("file name %s is ambiguous", RCS1);
  545.                 return 0;
  546.             }
  547.             error("RCS file name %s too long",RCS1);
  548.             return 0;
  549.         }
  550.     } else {
  551.                 /* working file given; now try to find RCS file */
  552.         workfilename=*argv;
  553.         /* derive RCS file name*/
  554.         strcpy( tempfilename, "rcs/" );
  555.         strcat( tempfilename, purefname );
  556.                 RCS1=findpairfile(argc-1,argv+1,tempfilename);
  557.         pureRCSname = strstr( RCS1, "rcs/" );
  558.                 if (strlen(pureRCSname)>NCPFN+4) {
  559.                         error("working file name %s too long",workfilename);
  560.                         return 0;
  561.                 }
  562.         }
  563.     if( strchr( workfilename, '/' ) != NULL ){
  564.         error("Sorry, file name %s confuses RCS", workfilename );
  565.         return 0;
  566.     }
  567. #else /* MSDOS */
  568.         lastsep=rindex(purefname, RCSSEP);
  569.         if (lastsep!= 0 && *(lastsep+1)==RCSSUF && *(lastsep+2)=='\0') {
  570.                 /* RCS file name given*/
  571.                 RCS1=(*argv); pureRCSname=purefname;
  572.                 /* derive workfilename*/
  573.                 sp = purefname; tp=tempfilename;
  574.                 while (sp<lastsep) *tp++ = *sp++; *tp='\0';
  575.                 /* try to find workfile name among arguments */
  576.                 workfilename=findpairfile(argc-1,argv+1,tempfilename);
  577.                 if (strlen(pureRCSname)>NCPFN) {
  578.                         error("RCS file name %s too long",RCS1);
  579.                         return 0;
  580.                 }
  581.         } else {
  582.                 /* working file given; now try to find RCS file */
  583.                 workfilename= *argv;
  584.                 /* derive RCS file name*/
  585.                 sp=purefname; tp=tempfilename;
  586.                 while (*tp++ = *sp++);
  587.                 *(tp-1)=RCSSEP; *tp++=RCSSUF; *tp++='\0';
  588.                 /* Try to find RCS file name among arguments*/
  589.                 RCS1=findpairfile(argc-1,argv+1,tempfilename);
  590.                 pureRCSname=bindex(RCS1, '/');
  591.                 if (strlen(pureRCSname)>NCPFN) {
  592.                         error("working file name %s too long",workfilename);
  593.                         return 0;
  594.                 }
  595.         }
  596. #endif /* MSDOS */
  597.         /* now we have a (tentative) RCS filename in RCS1 and workfilename  */
  598.         /* First, get status of workfilename */
  599.         haveworkstat=stat(workfilename, &workstat);
  600.         if ((haveworkstat==0) && ((workstat.st_mode & S_IFDIR) == S_IFDIR)) {
  601.                 diagnose("Directory %s ignored",workfilename);
  602.                 return 0;
  603.         }
  604.         /* Second, try to find the right RCS file */
  605. #ifdef MSDOS
  606.     RCSfilename=pureRCSname;
  607.     if (pureRCSname != RCS1){
  608.         error("Sorry, file name %s confuses RCS", RCS1);
  609.         return 0;
  610.     }else{
  611.         opened = ( ( finptr=fopen(pureRCSname, "r" ) ) != NULL );
  612.         if(opened){
  613.             returncode = 1;
  614.         }else{
  615.                         if (mustread) {
  616.                 error("Can't find %s",RCSfilename);
  617.                                 return 0;
  618.             }else{
  619.                 if( stat("RCS", &dirstat) == -1 ){
  620.                     error("Can't find RCS subdirectory");
  621.                     return 0;
  622.                 }else{
  623.                     if( !( dirstat.st_mode & S_IFDIR ) ){
  624.                         error("RCS must be a directory");
  625.                         return 0;
  626.                     }else returncode = -1;
  627.                 }
  628.             }
  629.         }
  630.     }
  631. #else /* MSDOS */    
  632.         if (pureRCSname!=RCS1) {
  633.                 /* a path for RCSfile is given; single RCS file to look for */
  634.                 finptr=fopen(RCSfilename=RCS1, "r");
  635.                 if (finptr!=NULL) {
  636.                     returncode=1;
  637.                 } else { /* could not open */
  638.                     if (access(RCSfilename,0)==0) {
  639.                         error("Can't open existing %s", RCSfilename);
  640.                         return 0;
  641.                     }
  642.                     if (mustread) {
  643.                         error("Can't find %s", RCSfilename);
  644.                         return 0;
  645.                     } else {
  646.                         /* initialize if not mustread */
  647.             returncode = -1;
  648.                     }
  649.                 }
  650.         } else {
  651.         /* no path for RCS file name. Prefix it with path of work */
  652.         /* file if RCS file omitted. Make a second name including */
  653.         /* RCSDIR and try to open that one first.                 */
  654.         sub1filename[0]=sub2filename[0]= '\0';
  655.         if (RCS1==tempfilename) {
  656.             /* RCS file name not given; prepend work path */
  657.             sp= *argv; tp= sub1filename;
  658.             while (sp<purefname) *tp++ = *sp ++;
  659.             *tp='\0';
  660.             VOID strcpy(sub2filename,sub1filename); /* second one */
  661.         }
  662.         VOID strcat(sub1filename,RCSDIR);
  663.         VOID strcpy(prefdir,sub1filename); /* preferred directory for RCS file*/
  664.         VOID strcat(sub1filename,RCS1); VOID strcat(sub2filename,RCS1);
  665.  
  666.                 opened=(
  667.         ((finptr=fopen(RCSfilename=sub1filename, "r"))!=NULL) ||
  668.         ((finptr=fopen(RCSfilename=sub2filename,"r"))!=NULL) );
  669.  
  670.                 if (opened) {
  671.                         /* open succeeded */
  672.                         returncode=1;
  673.                 } else {
  674.                         /* open failed; may be read protected */
  675.             if ((access(RCSfilename=sub1filename,0)==0) ||
  676.                 (access(RCSfilename=sub2filename,0)==0)) {
  677.                                 error("Can't open existing %s",RCSfilename);
  678.                                 return 0;
  679.                         }
  680.                         if (mustread) {
  681.                 error("Can't find %s nor %s",sub1filename,sub2filename);
  682.                                 return 0;
  683.                         } else {
  684.                                 /* initialize new file. Put into ./RCS if possible, strip off suffix*/
  685.                 RCSfilename= (access(prefdir,0)==0)?sub1filename:sub2filename;
  686.                                 returncode= -1;
  687.                         }
  688.                 }
  689.         }
  690. #endif /* MSDOS */
  691.         if (returncode == 1) { /* RCS file open */
  692.                 haveRCSstat=fstat(fileno(finptr),&RCSstat);
  693.                 if ((haveRCSstat== 0) && ((RCSstat.st_mode & S_IFDIR) == S_IFDIR)) {
  694.                         diagnose("Directory %s ignored",RCSfilename);
  695.                         return 0;
  696.                 }
  697.                 Lexinit(); getadmin();
  698.         } else {  /* returncode == -1; RCS file nonexisting */
  699.                 haveRCSstat = -1;
  700.                 InitAdmin();
  701.         };
  702.  
  703.         if (tostdout&&
  704.             !(RCS1==tempfilename||workfilename==tempfilename))
  705.                 /*The last term determines whether a pair of        */
  706.                 /* file names was given in the argument list        */
  707.                 warn("Option -p is set; ignoring output file %s",workfilename);
  708.  
  709.         return returncode;
  710. }
  711.  
  712.  
  713. char * getfullRCSname()
  714. /* Function: returns a pointer to the full path name of the RCS file.
  715.  * Calls getwd(), but only once.
  716.  * removes leading "../" and "./".
  717.  */
  718. {       static char pathbuf[NCPPN];
  719.         static char namebuf[NCPPN];
  720.         static int  pathlength;
  721.  
  722.         register char * realname, * lastpathchar;
  723.         register int  dotdotcounter, realpathlength;
  724.  
  725. #ifdef MSDOS
  726.     unixize(RCSfilename);
  727.     /* Treat a full path name containing the drive name */
  728.     /* Added by lfk */
  729.     if (RCSfilename[1] == ':' && RCSfilename[2] == '/')
  730.                 return(RCSfilename);
  731. #endif /* MSDOS */
  732.         if (*RCSfilename=='/') {
  733.                 return(RCSfilename);
  734.         } else {
  735.                 if (pathlength==0) { /*call curdir for the first time*/
  736.                     if (getwd(pathbuf)==NULL)
  737.                         faterror("Can't build current directory path");
  738.                     pathlength=strlen(pathbuf);
  739. #ifdef MSDOS    
  740.         /* Generally, a MSDOS path name seems to be like "A:\" */
  741.         /* Added by lfk */
  742.         if (!((pathlength==3) && (pathbuf[2]=='/'))) {
  743.                         pathbuf[pathlength++]='/';
  744.             /* Check needed because some getwd implementations */
  745.                         /* generate "/" for the root.                      */
  746.                     }
  747. #else
  748.                     if (!((pathlength==1) && (pathbuf[0]=='/'))) {
  749.                         pathbuf[pathlength++]='/';
  750.                         /* Check needed because some getwd implementations */
  751.                         /* generate "/" for the root.                      */
  752.                     }
  753. #endif /* MSDOS */
  754.                 }
  755.                 /*the following must be redone since RCSfilename may change*/
  756.                 /* find how many ../ to remvove from RCSfilename */
  757.                 dotdotcounter =0;
  758.                 realname = RCSfilename;
  759.                 while( realname[0]=='.' &&
  760.                       (realname[1]=='/'||(realname[1]=='.'&&realname[2]=='/'))){
  761.                         if (realname[1]=='/') {
  762.                             /* drop leading ./ */
  763.                             realname += 2;
  764.                         } else {
  765.                             /* drop leading ../ and remember */
  766.                             dotdotcounter++;
  767.                             realname += 3;
  768.                         }
  769.                 }
  770.                 /* now remove dotdotcounter trailing directories from pathbuf*/
  771.                 lastpathchar=pathbuf + pathlength-1;
  772.                 while (dotdotcounter>0 && lastpathchar>pathbuf) {
  773.                     /* move pointer backwards over trailing directory */
  774.                     lastpathchar--;
  775.                     if (*lastpathchar=='/') {
  776.                         dotdotcounter--;
  777.                     }
  778.                 }
  779.                 if (dotdotcounter>0) {
  780.                     error("Can't generate full path name for RCS file");
  781.                     return RCSfilename;
  782.                 } else {
  783.                     /* build full path name */
  784.                     realpathlength=lastpathchar-pathbuf+1;
  785.                     VOID strncpy(namebuf,pathbuf,realpathlength);
  786.                     VOID strcpy(&namebuf[realpathlength],realname);
  787.                     return(namebuf);
  788.                 }
  789.         }
  790. }
  791.  
  792. /* In MSDOS and (perhaps) OS/2, any directries are writable.     */
  793. /* So, the below check is NON-SENSE !! : lfk         */
  794. int trydiraccess(filename)
  795. char * filename;
  796. /* checks write permission in directory of filename and returns
  797.  * true if writable, false otherwise
  798.  */
  799. {
  800.         char pathname[NCPPN];
  801.         register char * tp, *sp, *lp;
  802.  
  803. #ifdef MSDOS
  804.     return true;
  805. #else
  806.         lp = rindex(filename,'/');
  807.         if (lp==0) {
  808.                 /* check current directory */
  809.                 if (access(".",2)==0)
  810.                         return true;
  811.                 else {
  812.                         error("Current directory not writable");
  813.                         return false;
  814.                 }
  815.         }
  816.         /* copy path */
  817.         sp=filename;
  818.         tp=pathname;
  819.         do *tp++ = *sp++; while (sp<=lp);
  820.         *tp='\0';
  821.         if (access(pathname,2)==0)
  822.                 return true;
  823.         else {
  824.                 error("Directory %s not writable", pathname);
  825.                 return false;
  826.         }
  827. #endif /* MSDOS */
  828. }
  829.  
  830.  
  831.  
  832. #ifndef V4_2BSD
  833. /* rename() and getwd() will be provided in bsd 4.2 */
  834.  
  835. #ifndef MSDOS
  836. int rename(from, to)
  837. char * from, *to;
  838. /* Function: renames a file with the name given by from to the name given by to.
  839.  * unlinks the to-file if it already exists. returns -1 on error, 0 otherwise.
  840.  */
  841. {       VOID unlink(to);      /* no need to check return code; will be caught by link*/
  842.                          /* no harm done if file "to" does not exist            */
  843.         if (link(from,to)<0) return -1;
  844.         return(unlink(from));
  845. }
  846.  
  847. #define dot     "."
  848. #define dotdot  ".."
  849.  
  850.  
  851. char * getwd(name)
  852. char * name;
  853. /* Function: places full pathname of current working directory into name and
  854.  * returns name on success, NULL on failure.
  855.  * getwd is an adaptation of pwd. May not return to the current directory on
  856.  * failure.
  857.  */
  858. {
  859.         FILE    *file;
  860.         struct  stat    d, dd;
  861.         char buf[2];    /* to NUL-terminate dir.d_name */
  862.         struct  direct  dir;
  863.  
  864.         int rdev, rino;
  865.         int off;
  866.         register i,j;
  867.  
  868.         name[off= 0] = '/';
  869.         name[1] = '\0';
  870.         buf[0] = '\0';
  871.         if (stat("/", &d)<0) return NULL;
  872.         rdev = d.st_dev;
  873.         rino = d.st_ino;
  874.         for (;;) {
  875.                 if (stat(dot, &d)<0) return NULL;
  876.                 if (d.st_ino==rino && d.st_dev==rdev) {
  877.                         if (name[off] == '/') name[off] = '\0';
  878.                         chdir(name); /*change back to current directory*/
  879.                         return name;
  880.                 }
  881.                 if ((file = fopen(dotdot,"r")) == NULL) return NULL;
  882.                 if (fstat(fileno(file), &dd)<0) goto fail;
  883.                 chdir(dotdot);
  884.                 if(d.st_dev == dd.st_dev) {
  885.                         if(d.st_ino == dd.st_ino) {
  886.                             if (name[off] == '/') name[off] = '\0';
  887.                             chdir(name); /*change back to current directory*/
  888.                             VOID fclose(file);
  889.                             return name;
  890.                         }
  891.                         do {
  892.                             if (fread((char *)&dir, sizeof(dir), 1, file) !=1)
  893.                                 goto fail;
  894.                         } while (dir.d_ino != d.st_ino);
  895.                 }
  896.                 else do {
  897.                         if(fread((char *)&dir, sizeof(dir), 1, file) != 1) {
  898.                             goto fail;
  899.                         }
  900.                         if (dir.d_ino == 0)
  901.                 dd.st_ino = d.st_ino + 1;
  902.                         else if (stat(dir.d_name, &dd) < 0)
  903.                 goto fail;
  904.                 } while(dd.st_ino != d.st_ino || dd.st_dev != d.st_dev);
  905.                 VOID fclose(file);
  906.  
  907.                 /* concatenate file name */
  908.                 i = -1;
  909.                 while (dir.d_name[++i] != 0);
  910.                 for(j=off+1; j>0; --j)
  911.                         name[j+i+1] = name[j];
  912.                 off=i+off+1;
  913.                 name[i+1] = '/';
  914.                 for(--i; i>=0; --i)
  915.                         name[i+1] = dir.d_name[i];
  916.         } /* end for */
  917.  
  918. fail:   VOID fclose(file);
  919.         return NULL;
  920. }
  921. #else /* MSDOS */
  922.  
  923. char *getwd(name)
  924. char    *name;
  925. {
  926.     int a,i;
  927.     (void)getcwd(name, NCPPN);
  928.     unixize(name);
  929.     return name;
  930. }
  931. #endif /* MSODS */
  932. #endif
  933. #ifdef MSDOS
  934. char *gettmpdir()
  935. {
  936.     char tp[NCPPN], *getenv(), *t;
  937.  
  938.     t = getenv ("TMP");
  939.     if(!t || !*t)
  940.         t = getenv ("TEMP");
  941.     if(!t || !*t)
  942.         t = ".";
  943.     strcpy (tp, t);
  944.     unixize(tp);
  945.     if (tp[strlen (tp) - 1] != '/' )
  946.         strcat( tp, "/" );
  947.     return tp;
  948. }
  949.  
  950. #endif /* MSDOS */
  951.  
  952. #ifdef PAIRTEST
  953. /* test program for pairfilenames() and getfullRCSname() */
  954. char * workfilename, *RCSfilename;
  955. extern int quietflag;
  956.  
  957. main(argc, argv)
  958. int argc; char *argv[];
  959. {
  960.         int result;
  961.         int initflag,tostdout;
  962.         quietflag=tostdout=initflag=false;
  963.         cmdid="pair";
  964.  
  965.         while(--argc, ++argv, argc>=1 && ((*argv)[0] == '-')) {
  966.                 switch ((*argv)[1]) {
  967.  
  968.                 case 'p':       tostdout=true;
  969.                                 break;
  970.                 case 'i':       initflag=true;
  971.                                 break;
  972.                 case 'q':       quietflag=true;
  973.                                 break;
  974.                 default:        error("unknown option: %s", *argv);
  975.                                 break;
  976.                 }
  977.         }
  978.  
  979.         do {
  980.                 RCSfilename=workfilename=nil;
  981.                 result=pairfilenames(argc,argv,!initflag,tostdout);
  982.                 if (result!=0) {
  983.                      diagnose("RCSfile: %s; working file: %s",RCSfilename,workfilename);
  984.                      diagnose("Full RCS file name: %s", getfullRCSname());
  985.                 }
  986.                 switch (result) {
  987.                         case 0: continue; /* already paired file */
  988.  
  989.                         case 1: if (initflag) {
  990.                                     error("RCS file %s exists already",RCSfilename);
  991.                                 } else {
  992.                                     diagnose("RCS file %s exists",RCSfilename);
  993.                                 }
  994.                                 VOID fclose(finptr);
  995.                                 break;
  996.  
  997.                         case -1:diagnose("RCS file does not exist");
  998.                                 break;
  999.                 }
  1000.  
  1001.         } while (++argv, --argc>=1);
  1002.  
  1003. }
  1004. #endif
  1005.  
  1006.