BSD 4_3_Tahoe development
authorCSRG <csrg@ucbvax.Berkeley.EDU>
Thu, 18 Feb 1988 08:16:57 +0000 (00:16 -0800)
committerCSRG <csrg@ucbvax.Berkeley.EDU>
Thu, 18 Feb 1988 08:16:57 +0000 (00:16 -0800)
Work on file usr/src/new/rcs/src/rcsedit.c
Work on file usr/src/new/rcs/src/rcsfnms.c
Work on file usr/src/new/rcs/src/rcsdiff.c

Synthesized-from: CSRG/cd2/4.3tahoe

usr/src/new/rcs/src/rcsdiff.c [new file with mode: 0644]
usr/src/new/rcs/src/rcsedit.c [new file with mode: 0644]
usr/src/new/rcs/src/rcsfnms.c [new file with mode: 0644]

diff --git a/usr/src/new/rcs/src/rcsdiff.c b/usr/src/new/rcs/src/rcsdiff.c
new file mode 100644 (file)
index 0000000..63350be
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ *                     RCS rcsdiff operation
+ */
+#ifndef lint
+static char rcsid[]=
+"$Header: /usr/src/new/rcs/src/RCS/rcsdiff.c,v 3.9 88/02/18 11:55:57 bostic Exp $ Purdue CS";
+#endif
+/*****************************************************************************
+ *                       generate difference between RCS revisions
+ *****************************************************************************
+ *
+ * Copyright (C) 1982 by Walter F. Tichy
+ *                       Purdue University
+ *                       Computer Science Department
+ *                       West Lafayette, IN 47907
+ *
+ * All rights reserved. No part of this software may be sold or distributed
+ * in any form or by any means without the prior written permission of the
+ * author.
+ * Report problems and direct all inquiries to Tichy@purdue (ARPA net).
+ */
+
+
+/* $Log:       rcsdiff.c,v $
+ * Revision 3.9  88/02/18  11:55:57  bostic
+ * replaced with version 4
+ * 
+ * Revision 4.4  87/12/18  11:37:46  narten
+ * changes Jay Lepreau made in the 4.3 BSD version, to add support for
+ * "-i", "-w", and "-t" flags and to permit flags to be bundled together, 
+ * merged in.
+ * 
+ * Revision 4.3  87/10/18  10:31:42  narten
+ * Updating version numbers. Changes relative to 1.1 actually
+ * relative to 4.1
+ * 
+ * Revision 1.3  87/09/24  13:59:21  narten
+ * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
+ * warnings)
+ * 
+ * Revision 1.2  87/03/27  14:22:15  jenkins
+ * Port to suns
+ * 
+ * Revision 1.1  84/01/23  14:50:18  kcs
+ * Initial revision
+ * 
+ * Revision 4.1  83/05/03  22:13:19  wft
+ * Added default branch, option -q, exit status like diff.
+ * Added fterror() to replace faterror().
+ * 
+ * Revision 3.6  83/01/15  17:52:40  wft
+ * Expanded mainprogram to handle multiple RCS files.
+ *
+ * Revision 3.5  83/01/06  09:33:45  wft
+ * Fixed passing of -c (context) option to diff.
+ *
+ * Revision 3.4  82/12/24  15:28:38  wft
+ * Added call to catchsig().
+ *
+ * Revision 3.3  82/12/10  16:08:17  wft
+ * Corrected checking of return code from diff; improved error msgs.
+ *
+ * Revision 3.2  82/12/04  13:20:09  wft
+ * replaced getdelta() with gettree(). Changed diagnostics.
+ *
+ * Revision 3.1  82/11/28  19:25:04  wft
+ * Initial revision.
+ *
+ */
+#include <ctype.h>
+#include "rcsbase.h"
+#define ERRCODE 2                   /*error code for exit status            */
+#ifndef lint
+static char rcsbaseid[] = RCSBASE;
+#endif
+
+extern int    cleanup();            /* cleanup after signals                */
+extern char * mktempfile();         /*temporary file name generator         */
+extern int    fterror();            /*forward for special fatal error func. */
+extern struct hshentry * genrevs(); /*generate delta numbers                */
+extern int    nerror;               /*counter for errors                    */
+extern int    quietflag;            /*suppresses diagnostics                */
+extern FILE * finptr;               /* RCS input file                       */
+
+char *RCSfilename;
+char *workfilename;
+char * temp1file, * temp2file;
+
+char bops[10] = "-";
+char otherops[10] = "-";
+
+main (argc, argv)
+int argc; char **argv;
+{
+        char * cmdusage;
+        char command[NCPPN+revlength+40];
+        int  revnums;                 /* counter for revision numbers given */
+        char * rev1, * rev2;          /* revision numbers from command line */
+        char numericrev[revlength];   /* holds expanded revision number     */
+        char * xrev1, * xrev2;        /* expanded revision numbers          */
+        struct hshentry * gendeltas[hshsize];/*stores deltas to be generated*/
+        struct hshentry * target;
+       char * boption, * otheroption;
+        int  exit_stats;
+        int  filecounter;
+       char *argp;
+       register c;
+
+        catchints();
+        otheroption = otherops + 1;
+       boption = bops + 1;
+        cmdid = "rcsdiff";
+       cmdusage = "command format:\n    rcsdiff [-biwt] [-q] [-cefhn] [-rrev1] [-rrev2] file";
+        filecounter=revnums=0;
+        while (--argc,++argv, argc>=1 && ((*argv)[0] == '-')) {
+           argp = &((*argv)[1]);
+           while (c = *argp++) switch (c) {
+                case 'r':
+                       if (*argp!='\0') {
+                            if (revnums==0) {
+                                    rev1= argp; revnums=1;
+                            } elif (revnums==1) {
+                                    rev2= argp; revnums=2;
+                            } else {
+                                   fterror("too many revision numbers");
+                            }
+                        } /* do nothing for empty -r */
+                       argp += strlen(argp);
+                        break;
+                case 'b':
+                case 'i':
+                case 'w':
+                case 't':
+                       *boption++ = c;
+                       break;
+               case 'q':
+                       quietflag=true;
+                       break;
+                case 'c':
+                case 'e':
+                case 'f':
+                case 'h':
+                case 'n':
+                        if (otheroption == otherops + 1) {
+                               *otheroption++ = c;
+                               if (c == 'c' && isdigit(*argp)) {
+                                       while (isdigit(*argp))
+                                               *otheroption++ = *argp++;
+                                       if (*argp)
+                                               faterror("-c: bad count");
+                                       argp = "";
+                               }
+                        } else {
+                               fterror("Options c,e,f,h,n are mutually exclusive");
+                        }
+                       break;
+                default:
+                       fterror("unknown option: %s\n%s", *argv,cmdusage);
+                };
+        } /* end of option processing */
+
+       if (boption != bops + 1) {
+           *boption = ' ';
+           boption = bops;
+       }
+       if (otheroption != otherops + 1) {
+           *otheroption = ' ';
+           otheroption = otherops;
+       }
+       if (argc<1) fterror("No input file\n%s",cmdusage);
+
+        /* now handle all filenames */
+        do {
+                finptr=NULL;
+
+                if (pairfilenames(argc,argv,true,false)!=1) continue;
+                if (++filecounter>1)
+                        diagnose("===================================================================");
+                diagnose("RCS file: %s",RCSfilename);
+                if (revnums<2 && !(access(workfilename,4)==0)) {
+                        error("Can't open %s",workfilename);
+                        continue;
+                }
+                if (!trysema(RCSfilename,false)) continue; /* give up */
+
+
+                gettree(); /* reads in the delta tree */
+
+                if (Head==nil) {
+                        error("no revisions present");
+                        continue;
+                }
+                if (revnums==0)
+                        rev1=Dbranch!=nil?Dbranch->num:Head->num; /* default rev1 */
+
+                if (!expandsym(rev1,numericrev)) continue;
+                if (!(target=genrevs(numericrev,(char *)nil,(char *)nil,(char *)nil,gendeltas))) continue;
+                xrev1=target->num;
+
+                if (revnums==2) {
+                        if (!expandsym(rev2,numericrev)) continue;
+                        if (!(target=genrevs(numericrev,(char *)nil,(char *)nil,(char *)nil,gendeltas))) continue;
+                        xrev2=target->num;
+                }
+
+
+                temp1file=mktempfile("/tmp/",TMPFILE1);
+                diagnose("retrieving revision %s",xrev1);
+                VOID sprintf(command,"%s/co -q -p%s %s > %s\n",
+                        TARGETDIR,xrev1,RCSfilename,temp1file);
+                if (system(command)){
+                        error("co failed");
+                        continue;
+                }
+                if (revnums<=1) {
+                        temp2file=workfilename;
+                       diagnose("diff %s%s-r%s %s",boption,otheroption,xrev1,workfilename);
+                } else {
+                        temp2file=mktempfile("/tmp/",TMPFILE2);
+                        diagnose("retrieving revision %s",xrev2);
+                        VOID sprintf(command,"%s/co -q -p%s %s > %s\n",
+                                TARGETDIR,xrev2,RCSfilename,temp2file);
+                        if (system(command)){
+                                error("co failed");
+                                continue;
+                        }
+                        diagnose("diff %s%s-r%s -r%s",boption,otheroption,xrev1,xrev2);
+                }
+                VOID sprintf(command,"%s %s%s%s %s\n",DIFF,boption,
+                        otheroption, temp1file, temp2file);
+                exit_stats = system (command);
+                if (exit_stats != 0 && exit_stats != (1 << BYTESIZ)) {
+                        error ("diff failed");
+                        continue;
+                }
+        } while (cleanup(),
+                 ++argv, --argc >=1);
+
+
+       if (nerror>0) {
+               exit(ERRCODE);
+       } else {
+               exit(exit_stats>>BYTESIZ);
+               /* return exit status from diff */
+       }
+
+}
+
+
+/*VARARGS3*/
+fterror(e, e1, e2)
+char * e, * e1, * e2;
+/* prints error message and terminates program with ERRCODE */
+{       nerror++;
+        VOID fprintf(stderr,"%s error: ",cmdid);
+       VOID fprintf(stderr,e, e1, e2);
+        VOID fprintf(stderr,"\n%s aborted\n",cmdid);
+        VOID cleanup();
+       exit(ERRCODE);
+}
+
diff --git a/usr/src/new/rcs/src/rcsedit.c b/usr/src/new/rcs/src/rcsedit.c
new file mode 100644 (file)
index 0000000..6029c4c
--- /dev/null
@@ -0,0 +1,483 @@
+/*
+ *                     RCS stream editor
+ */
+#ifndef lint
+static char rcsid[]= "$Id: rcsedit.c,v 3.9 88/02/18 11:56:51 bostic Exp $ Purdue CS";
+#endif
+/**********************************************************************************
+ *                       edits the input file according to a
+ *                       script from stdin, generated by diff -n
+ *                       performs keyword expansion
+ **********************************************************************************
+ *
+ * Copyright (C) 1982 by Walter F. Tichy
+ *                       Purdue University
+ *                       Computer Science Department
+ *                       West Lafayette, IN 47907
+ *
+ * All rights reserved. No part of this software may be sold or distributed
+ * in any form or by any means without the prior written permission of the
+ * author.
+ * Report problems and direct all inquiries to Tichy@purdue (ARPA net).
+ */
+
+
+/* $Log:       rcsedit.c,v $
+ * Revision 3.9  88/02/18  11:56:51  bostic
+ * replaced with version 4
+ * 
+ * Revision 4.5  87/12/18  11:38:46  narten
+ * Changes from the 43. version. Don't know the significance of the
+ * first change involving "rewind". Also, additional "lint" cleanup.
+ * (Guy Harris)
+ * 
+ * Revision 4.4  87/10/18  10:32:21  narten
+ * Updating version numbers. Changes relative to version 1.1 actually
+ * relative to 4.1
+ * 
+ * Revision 1.4  87/09/24  13:59:29  narten
+ * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
+ * warnings)
+ * 
+ * Revision 1.3  87/09/15  16:39:39  shepler
+ * added an initializatin of the variables editline and linecorr
+ * this will be done each time a file is processed.
+ * (there was an obscure bug where if co was used to retrieve multiple files
+ *  it would dump)
+ * fix attributed to  Roy Morris @FileNet Corp ...!felix!roy
+ * 
+ * Revision 1.2  87/03/27  14:22:17  jenkins
+ * Port to suns
+ * 
+ * Revision 1.1  84/01/23  14:50:20  kcs
+ * Initial revision
+ * 
+ * Revision 4.1  83/05/12  13:10:30  wft
+ * Added new markers Id and RCSfile; added locker to Header and Id.
+ * Overhauled expandline completely() (problem with $01234567890123456789@).
+ * Moved trymatch() and marker table to rcskeys.c.
+ * 
+ * Revision 3.7  83/05/12  13:04:39  wft
+ * Added retry to expandline to resume after failed match which ended in $.
+ * Fixed truncation problem for $19chars followed by@@.
+ * Log no longer expands full path of RCS file.
+ * 
+ * Revision 3.6  83/05/11  16:06:30  wft
+ * added retry to expandline to resume after failed match which ended in $.
+ * Fixed truncation problem for $19chars followed by@@.
+ * 
+ * Revision 3.5  82/12/04  13:20:56  wft
+ * Added expansion of keyword Locker.
+ *
+ * Revision 3.4  82/12/03  12:26:54  wft
+ * Added line number correction in case editing does not start at the
+ * beginning of the file.
+ * Changed keyword expansion to always print a space before closing KDELIM;
+ * Expansion for Header shortened.
+ *
+ * Revision 3.3  82/11/14  14:49:30  wft
+ * removed Suffix from keyword expansion. Replaced fclose with ffclose.
+ * keyreplace() gets log message from delta, not from curlogmsg.
+ * fixed expression overflow in while(c=putc(GETC....
+ * checked nil printing.
+ *
+ * Revision 3.2  82/10/18  21:13:39  wft
+ * I added checks for write errors during the co process, and renamed
+ * expandstring() to xpandstring().
+ *
+ * Revision 3.1  82/10/13  15:52:55  wft
+ * changed type of result of getc() from char to int.
+ * made keyword expansion loop in expandline() portable to machines
+ * without sign-extension.
+ */
+
+
+#include "rcsbase.h"
+
+
+extern FILE * fopen();
+extern char * mktempfile();
+extern char * bindex();
+extern FILE * finptr, * frewrite;
+extern int rewriteflag;
+extern int nextc;
+extern char * RCSfilename, * workfilename;
+extern char * bindex();
+extern char * getfullRCSname();
+extern enum markers trymatch();
+
+
+FILE  * fcopy,  * fedit; /* result and edit file descriptors                */
+char  *resultfile = nil; /* result file name                                */
+char  * editfile  = nil; /* edit   file name                                */
+int editline;  /*line counter in fedit; starts with 1, is always #lines+1   */
+int linecorr;  /*contains #adds - #deletes in each edit run.                */
+               /*used to correct editline in case file is not rewound after */
+               /* applying one delta                                        */
+
+initeditfiles(dir)
+char * dir;
+/* Function: Initializes resultfile and editfile with temporary filenames
+ * in directory dir. Opens resultfile for reading and writing, with fcopy
+ * as file descriptor. fedit is set to nil.
+ */
+{
+       editline = linecorr = 0;    /* make sure we start from the beginning*/
+        resultfile=mktempfile(dir,TMPFILE1);
+        editfile  =mktempfile(dir,TMPFILE2);
+        fedit=nil;
+        if ((fcopy=fopen(resultfile,"w+"))==NULL) {
+                faterror("Can't open working file %s",resultfile);
+        }
+}
+
+
+swapeditfiles(tostdout)
+/* Function: swaps resultfile and editfile, assigns fedit=fcopy,
+ * rewinds fedit for reading, and opens resultfile for reading and
+ * writing, using fcopy. If tostdout, fcopy is set to stdout.
+ */
+{       char * tmpptr;
+        if(ferror(fcopy))
+                faterror("write failed on %s -- file system full?",resultfile);
+        fedit=fcopy;
+        rewind(fedit);
+        editline = 1; linecorr=0;
+        tmpptr=editfile; editfile=resultfile; resultfile=tmpptr;
+        if (tostdout)
+                fcopy=stdout;
+        elsif ((fcopy=fopen(resultfile,"w+"))==NULL) {
+                faterror("Can't open working file %s",resultfile);
+        }
+}
+
+
+finishedit(delta)
+struct hshentry * delta;
+/* copy the rest of the edit file and close it (if it exists).
+ * if delta!=nil, perform keyword substitution at the same time.
+ */
+{
+        register int c;
+        if (fedit!=nil) {
+                if (delta!=nil) {
+                        while (expandline(fedit,fcopy,delta,false,false)) editline++;
+                } else {
+                        while((c=getc(fedit))!=EOF) {
+                                VOID putc(c,fcopy);
+                                if (c=='\n') editline++;
+                        }
+                }
+                ffclose(fedit);
+        }
+}
+
+
+copylines(line,delta)
+register int line; struct hshentry * delta;
+/* Function: copies input lines editline..line-1 from fedit to fcopy.
+ * If delta != nil, keyword expansion is done simultaneously.
+ * editline is updated. Rewinds a file only if necessary.
+ */
+{
+
+        if (editline>line) {
+                /* swap files */
+                finishedit((struct hshentry *)nil); swapeditfiles(false);
+                /* assumes edit only during last pass, from the beginning*/
+        }
+        while (editline<line) {
+                /*copy another line*/
+                if (delta)
+                        VOID expandline(fedit,fcopy,delta,false,false);
+                else
+                        while (putc(getc(fedit),fcopy)!='\n');
+                editline++;
+        }
+}
+
+
+
+xpandstring(delta)
+struct hshentry * delta;
+/* Function: Reads a string terminated by SDELIM from finptr and writes it
+ * to fcopy. Double SDELIM is replaced with single SDELIM.
+ * Keyword expansion is performed with data from delta.
+ * If rewriteflag==true, the string is also copied unchanged to frewrite.
+ * editline is updated.
+ */
+{
+        editline=1;
+        while (expandline(finptr,fcopy,delta,true,rewriteflag)) editline++;
+        nextc='\n';
+}
+
+
+copystring()
+/* Function: copies a string terminated with a single SDELIM from finptr to
+ * fcopy, replacing all double SDELIM with a single SDELIM.
+ * If rewriteflag==true, the string also copied unchanged to frewrite.
+ * editline is set to (number of lines copied)+1.
+ * Assumption: next character read is first string character.
+ */
+{       register c, write;
+        write=rewriteflag;
+        editline=1;
+        while ((c=GETC(finptr,frewrite,write)) != EOF) {
+                if ((c==SDELIM)&&((c=GETC(finptr,frewrite,write)) != SDELIM)){
+                        /* end of string */
+                        nextc = c;
+                        return;
+                }
+                VOID putc(c,fcopy);
+                if (c=='\n') editline++;
+        }
+        nextc = c;
+        serror("Unterminated string");
+        return;
+}
+
+
+
+
+editstring(delta)
+struct hshentry * delta;
+/* Function: reads an edit script from finptr and applies it to
+ * file fedit; the result is written to fcopy.
+ * If delta!=nil, keyword expansion is performed simultaneously.
+ * If frewrite==true, the edit script is also copied verbatim to frewrite.
+ * Assumes that all these files are open.
+ * If running out of lines in fedit, fedit and fcopy are swapped.
+ * resultfile and editfile are the names of the files that go with fcopy
+ * and fedit, respectively.
+ * Assumes the next input character from finptr is the first character of
+ * the edit script. Resets nextc on exit.
+ */
+{
+        int ed; /* editor command */
+        register int c;
+        register int write, i;
+        int line, length;
+
+        editline += linecorr; linecorr=0; /*correct line number*/
+        write=rewriteflag;
+        for (;;) {
+                /* read next command and decode */
+                /* assume next non-white character is command name*/
+                while((ed=GETC(finptr,frewrite,write))=='\n'||
+                        ed==' ' || ed=='\t');
+                if (ed==SDELIM) break;
+                /* now attempt to read numbers. */
+                /* fscanf causes trouble because of the required echoing */
+                while ((c=GETC(finptr,frewrite,write))==' ');  /*skip spaces*/
+                if (!('0'<=c && c<='9')) {
+                        faterror("missing line number in edit script");
+                        break;
+                }
+                line= c -'0';
+                while ('0'<=(c=GETC(finptr,frewrite,write)) && c<='9') {
+                        line = line*10 + c-'0';
+                }
+                while (c==' ') c=GETC(finptr,frewrite,write);
+                if (!('0'<=c && c<='9')) {
+                        faterror("incorrect range in edit script");
+                        break;
+                }
+                length= c -'0';
+                while ('0'<=(c=GETC(finptr,frewrite,write)) && c<='9') {
+                        length = length*10 + c-'0';
+                }
+                while(c!='\n'&&c!=EOF) c=GETC(finptr,frewrite,write); /* skip to end of line */
+
+                switch (ed) {
+                case 'd':
+                        copylines(line,delta);
+                        /* skip over unwanted lines */
+                        for (i=length;i>0;i--) {
+                                /*skip next line*/
+                                while ((c=getc(fedit))!='\n');
+                                       if (c==EOF)
+                                               faterror("EOF during edit");
+                                editline++;
+                        }
+                        linecorr -= length;
+                        break;
+                case 'a':
+                        copylines(line+1,delta); /*copy only; no delete*/
+                        for (i=length;i>0;i--) {
+                                /*copy next line from script*/
+                                if (delta!=nil)
+                                       VOID expandline(finptr,fcopy,delta,true,write);
+                                else {
+                                       c = GETC(finptr,frewrite,write);
+                                       while (putc(c,fcopy)!='\n'){
+                                               if ((c==SDELIM)&&((c=GETC(finptr,frewrite,write))!=SDELIM)){
+                                                       serror("Missing string delimiter in edit script");
+                                                       VOID putc(c,fcopy);
+                                               }
+                                               c = GETC(finptr,frewrite,write);
+                                       }
+                                }
+                        }
+                        linecorr += length;
+                        break;
+                default:
+                        faterror("unknown command in edit script: %c", ed);
+                        break;
+                }
+        }
+        nextc=GETC(finptr,frewrite,write);
+}
+
+
+
+/* The rest if for keyword expansion */
+
+
+
+expandline(in, out, delta,delimstuffed,write)
+FILE * in, * out; struct hshentry * delta;
+register int delimstuffed, write;
+/* Function: Reads a line from in and writes it to out.
+ * If delimstuffed==true, double SDELIM is replaced with single SDELIM.
+ * Keyword expansion is performed with data from delta.
+ * If write==true, the string is also copied unchanged to frewrite.
+ * Returns false if end-of-string or end-of-line is detected, true otherwise.
+ */
+{
+       register c;
+       register char * tp;
+       char keystring[keylength+2];
+       char keyval[keyvallength+2];
+        enum markers matchresult;
+
+       c=GETC(in,frewrite,write);
+        for (;;) {
+                if (c==EOF) {
+                        if(delimstuffed) {
+                                error("unterminated string");
+                                nextc=c;
+                        }
+                        return(false);
+                }
+
+                if (c==SDELIM && delimstuffed) {
+                        if ((c=GETC(in,frewrite,write))!=SDELIM) {
+                                /* end of string */
+                                nextc=c;
+                                return false;
+                        }
+                }
+                VOID putc(c,out);
+
+                if (c=='\n') return true; /* end of line */
+
+               if (c==KDELIM) {
+                        /* check for keyword */
+                        /* first, copy a long enough string into keystring */
+                       tp=keystring;
+                       while (((c=GETC(in,frewrite,write))!=EOF) && (tp<keystring+keylength) && (c!='\n')
+                              && (c!=SDELIM) && (c!=KDELIM) && (c!=VDELIM)) {
+                              VOID putc(c,out);
+                             *tp++ = c;
+                        }
+                       *tp++ = c; *tp = '\0';
+                       matchresult=trymatch(keystring,false);
+                       if (matchresult==Nomatch) continue;
+                       /* last c will be dealt with properly by continue*/
+
+                       /* Now we have a keyword terminated with a K/VDELIM */
+                       if (c==VDELIM) {
+                             /* try to find closing KDELIM, and replace value */
+                             tp=keyval;
+                             while (((c=GETC(in,frewrite,write)) != EOF)
+                                    && (c!='\n') && (c!=KDELIM) && (tp<keyval+keyvallength)) {
+                                     *tp++ =c;
+                                     if (c==SDELIM && delimstuffed) { /*skip next SDELIM */
+                                               c=GETC(in,frewrite,write);
+                                               /* Can't be at end of string.
+                                               /* always a \n before closing SDELIM */
+                                     }
+                             }
+                             if (c!=KDELIM) {
+                                   /* couldn't find closing KDELIM -- give up */
+                                   VOID putc(VDELIM,out); *tp='\0';
+                                   VOID fputs(keyval,out);
+                                   continue;   /* last c handled properly */
+                             }
+                       }
+                       /* now put out the new keyword value */
+                       keyreplace(matchresult,delta,out);
+                }
+                c=GETC(in,frewrite,write);
+        } /* end for */
+}
+
+
+
+keyreplace(marker,delta,out)
+enum markers marker; struct hshentry * delta; FILE * out;
+/* function: ouputs the keyword value(s) corresponding to marker.
+ * Attributes are derived from delta.
+ */
+{
+        char * date;
+        register char * sp;
+
+        date= delta->date;
+
+        switch (marker) {
+        case Author:
+                VOID fprintf(out,"%c %s %c",VDELIM,delta->author,KDELIM);
+                break;
+        case Date:
+                VOID putc(VDELIM,out);VOID putc(' ',out);
+                VOID PRINTDATE(out,date);VOID putc(' ',out);
+                VOID PRINTTIME(out,date);VOID putc(' ',out);VOID putc(KDELIM,out);
+                break;
+        case Id:
+       case Header:
+               VOID putc(VDELIM,out); VOID putc(' ',out);
+               if (marker==Id)
+                        VOID fputs(bindex(RCSfilename,'/'),out);
+               else     VOID fputs(getfullRCSname(),out);
+               VOID fprintf(out," %s ", delta->num);
+                VOID PRINTDATE(out,date);VOID putc(' ',out);VOID PRINTTIME(out,date);
+               VOID fprintf(out, " %s %s ",delta->author,delta->state);
+               if (delta->lockedby!=nil)
+                        VOID fprintf(out,"Locker: %s ",delta->lockedby);
+               VOID putc(KDELIM,out);
+                break;
+        case Locker:
+                VOID fprintf(out,"%c %s %c", VDELIM,
+                        delta->lockedby==nil?"":delta->lockedby,KDELIM);
+                break;
+        case Log:
+                VOID fprintf(out, "%c\t%s %c\n%sRevision %s  ",
+                        VDELIM, bindex(RCSfilename,'/'), KDELIM, Comment, delta->num);
+                VOID PRINTDATE(out,date);VOID fputs("  ",out);VOID PRINTTIME(out,date);
+                VOID fprintf(out, "  %s\n%s",delta->author,Comment);
+                /* do not include state here because it may change and is not updated*/
+                sp = delta->log;
+                while (*sp) if (putc(*sp++,out)=='\n') VOID fputs(Comment,out);
+                /* Comment is the comment leader */
+                break;
+        case RCSfile:
+                VOID fprintf(out,"%c %s %c",VDELIM,bindex(RCSfilename,'/'),KDELIM);
+                break;
+        case Revision:
+                VOID fprintf(out,"%c %s %c",VDELIM,delta->num,KDELIM);
+                break;
+        case Source:
+                VOID fprintf(out,"%c %s %c",VDELIM,getfullRCSname(),KDELIM);
+                break;
+        case State:
+                VOID fprintf(out,"%c %s %c",VDELIM,delta->state,KDELIM);
+                break;
+        case Nomatch:
+                VOID putc(KDELIM,out);
+                break;
+        }
+}
+
+
diff --git a/usr/src/new/rcs/src/rcsfnms.c b/usr/src/new/rcs/src/rcsfnms.c
new file mode 100644 (file)
index 0000000..19b6e13
--- /dev/null
@@ -0,0 +1,762 @@
+/*
+ *                     RCS file name handling
+ */
+#ifndef lint
+ static char
+ rcsid[]= "$Id: rcsfnms.c,v 3.10 88/02/18 11:57:27 bostic Exp $ Purdue CS";
+#endif
+/****************************************************************************
+ *                     creation and deletion of semaphorefile,
+ *                     creation of temporary filenames and cleanup()
+ *                     pairing of RCS file names and working file names.
+ *                     Testprogram: define PAIRTEST
+ ****************************************************************************
+ *
+ * Copyright (C) 1982 by Walter F. Tichy
+ *                       Purdue University
+ *                       Computer Science Department
+ *                       West Lafayette, IN 47907
+ *
+ * All rights reserved. No part of this software may be sold or distributed
+ * in any form or by any means without the prior written permission of the
+ * author.
+ * Report problems and direct all inquiries to Tichy@purdue (ARPA net).
+ */
+
+
+/* $Log:       rcsfnms.c,v $
+ * Revision 3.10  88/02/18  11:57:27  bostic
+ * replaced with version 4
+ * 
+ * Revision 4.6  87/12/18  11:40:23  narten
+ * additional file types added from 4.3 BSD version, and SPARC assembler
+ * comment character added. Also, more lint cleanups. (Guy Harris)
+ * 
+ * Revision 4.5  87/10/18  10:34:16  narten
+ * Updating version numbers. Changes relative to 1.1 actually relative
+ * to verion 4.3
+ * 
+ * Revision 1.3  87/03/27  14:22:21  jenkins
+ * Port to suns
+ * 
+ * Revision 1.2  85/06/26  07:34:28  svb
+ * Comment leader '% ' for '*.tex' files added.
+ * 
+ * Revision 1.1  84/01/23  14:50:24  kcs
+ * Initial revision
+ * 
+ * Revision 4.3  83/12/15  12:26:48  wft
+ * Added check for KDELIM in file names to pairfilenames().
+ * 
+ * Revision 4.2  83/12/02  22:47:45  wft
+ * Added csh, red, and sl file name suffixes.
+ * 
+ * Revision 4.1  83/05/11  16:23:39  wft
+ * Added initialization of Dbranch to InitAdmin(). Canged pairfilenames():
+ * 1. added copying of path from workfile to RCS file, if RCS file is omitted;
+ * 2. added getting the file status of RCS and working files;
+ * 3. added ignoring of directories.
+ * 
+ * Revision 3.7  83/05/11  15:01:58  wft
+ * Added comtable[] which pairs file name suffixes with comment leaders;
+ * updated InitAdmin() accordingly.
+ * 
+ * Revision 3.6  83/04/05  14:47:36  wft
+ * fixed Suffix in InitAdmin().
+ * 
+ * Revision 3.5  83/01/17  18:01:04  wft
+ * Added getwd() and rename(); these can be removed by defining
+ * V4_2BSD, since they are not needed in 4.2 bsd.
+ * Changed sys/param.h to sys/types.h.
+ *
+ * Revision 3.4  82/12/08  21:55:20  wft
+ * removed unused variable.
+ *
+ * Revision 3.3  82/11/28  20:31:37  wft
+ * Changed mktempfile() to store the generated file names.
+ * Changed getfullRCSname() to store the file and pathname, and to
+ * delete leading "../" and "./".
+ *
+ * Revision 3.2  82/11/12  14:29:40  wft
+ * changed pairfilenames() to handle file.sfx,v; also deleted checkpathnosfx(),
+ * checksuffix(), checkfullpath(). Semaphore name generation updated.
+ * mktempfile() now checks for nil path; lastfilename initialized properly.
+ * Added Suffix .h to InitAdmin. Added testprogram PAIRTEST.
+ * Moved rmsema, trysema, trydiraccess, getfullRCSname from rcsutil.c to here.
+ *
+ * Revision 3.1  82/10/18  14:51:28  wft
+ * InitAdmin() now initializes StrictLocks=STRICT_LOCKING (def. in rcsbase.h).
+ * renamed checkpath() to checkfullpath().
+ */
+
+
+#include "rcsbase.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/dir.h>
+
+extern char * rindex();
+extern char * mktemp();
+extern char * malloc();
+extern FILE * fopen();
+extern char * getwd();         /* get working directory; forward decl       */
+extern int    stat(), fstat();
+
+extern FILE * finptr;          /* RCS input file descriptor                 */
+extern FILE * frewrite;        /* New RCS file descriptor                   */
+extern char * RCSfilename, * workfilename; /* filenames                     */
+struct stat RCSstat, workstat; /* file status for RCS file and working file */
+int    haveRCSstat,  haveworkstat; /* indicators if status availalble       */
+
+
+char tempfilename [NCPFN+10];  /* used for derived file names               */
+char sub1filename [NCPPN];     /* used for files path/file.sfx,v            */
+char sub2filename [NCPPN];     /* used for files path/RCS/file.sfx,v        */
+char semafilename [NCPPN];     /* name of semaphore file                    */
+int  madesema;                 /* indicates whether a semaphore file has been set */
+char * tfnames[10] =           /* temp. file names to be unlinked when finished   */
+       {nil,nil,nil,nil,nil,nil,nil,nil,nil,nil};
+int  lastfilename         = -1;/* index of last file name in tfnames[]      */
+
+
+struct compair {
+        char * suffix, * comlead;
+};
+
+struct compair comtable[] = {
+/* comtable pairs each filename suffix with a comment leader. The comment   */
+/* leader is placed before each line generated by the $Log keyword. This    */
+/* table is used to guess the proper comment leader from the working file's */
+/* suffix during initial ci (see InitAdmin()). Comment leaders are needed   */
+/* for languages without multiline comments; for others they are optional.  */
+        "c",   " * ",   /* C           */
+       "csh", "# ",    /* shell       */
+        "e",   "# ",    /* efl         */
+        "f",   "c ",    /* fortran     */
+        "h",   " * ",   /* C-header    */
+        "l",   " * ",   /* lex         NOTE: conflict between lex and franzlisp*/
+        "mac", "; ",    /* macro       vms or dec-20 or pdp-11 macro */
+        "me",  "\\\" ", /* me-macros   t/nroff*/
+       "mm",  "\\\" ", /* mm-macros   t/nroff*/
+        "ms",  "\\\" ", /* ms-macros   t/nroff*/
+        "p",   " * ",   /* pascal      */
+        "r",   "# ",    /* ratfor      */
+        "red", "% ",    /* psl/rlisp   */
+
+#ifdef sparc
+        "s",   "! ",    /* assembler   */
+#endif
+#ifdef mc68000
+        "s",   "| ",    /* assembler   */
+#endif
+#ifdef pdp11
+        "s",   "/ ",    /* assembler   */
+#endif
+#ifdef vax
+        "s",   "# ",    /* assembler   */
+#endif
+
+        "sh",  "# ",    /* shell       */
+        "sl",  "% ",    /* psl         */
+        "red", "% ",    /* psl/rlisp   */
+        "cl",  ";;; ",  /* common lisp   */
+        "ml",  "; ",    /* mocklisp    */
+        "el",  "; ",    /* gnulisp     */
+       "tex", "% ",    /* tex         */
+        "y",   " * ",   /* yacc        */
+        "ye",  " * ",   /* yacc-efl    */
+        "yr",  " * ",   /* yacc-ratfor */
+        "",    "# ",    /* default for empty suffix */
+        nil,   ""       /* default for unknown suffix; must always be last */
+};
+
+
+ffclose(fptr)
+FILE * fptr;
+/* Function: checks ferror(fptr) and aborts the program if there were
+ * errors; otherwise closes fptr.
+ */
+{       if (ferror(fptr) || fclose(fptr)==EOF)
+                faterror("File read or write error; file system full?");
+}
+
+
+
+int trysema(RCSfilename,makesema)
+char * RCSfilename; int makesema;
+/* Function: Checks whether a semaphore file exists for RCSfilename. If yes,
+ * returns false. If not, creates one if makesema==true and returns true
+ * if successful. If a semaphore file was created, madesema is set to true.
+ * The name of the semaphore file is put into variable semafilename.
+ */
+{
+        register char * tp, *sp, *lp;
+        int fdesc;
+
+        sp=RCSfilename;
+        lp = rindex(sp,'/');
+        if (lp==0) {
+                semafilename[0]='.'; semafilename[1]='/';
+                tp= &semafilename[2];
+        } else {
+                /* copy path */
+                tp=semafilename;
+                do *tp++ = *sp++; while (sp<=lp);
+        }
+        /*now insert `,' and append file name */
+        *tp++ = ',';
+        lp = rindex(sp, RCSSEP);
+        while (sp<lp) *tp++ = *sp++;
+        *tp++ = ','; *tp++ = '\0'; /* will be the same length as RCSfilename*/
+
+        madesema = false;
+        if (access(semafilename, 0) == 0) {
+                error("RCS file %s is in use",RCSfilename);
+                return false;
+        }
+        if (makesema) {
+                if ((fdesc=creat(semafilename, 000)) == -1) {
+                     error("Can't create semaphore file for RCS file %s",RCSfilename);
+                     return false;
+                } else
+                     VOID close(fdesc);
+                     madesema=true;
+        }
+        return true;
+}
+
+
+int rmsema()
+/* Function: delete the semaphore file if madeseam==true;
+ * sets madesema to false.
+ */
+{
+        if (madesema) {
+                madesema=false;
+                if (unlink(semafilename) == -1) {
+                        error("Can't find semaphore file %s",semafilename);
+                        return false;
+                }
+        }
+        return true;
+}
+
+
+
+InitCleanup()
+{       lastfilename =  -1;  /* initialize pointer */
+}
+
+
+cleanup()
+/* Function: closes input file and rewrite file.
+ * Unlinks files in tfnames[], deletes semaphore file.
+ */
+{
+        register int i;
+
+        if (finptr!=NULL)   VOID fclose(finptr);
+        if (frewrite!=NULL) VOID fclose(frewrite);
+        for (i=0; i<=lastfilename; i++) {
+            if (tfnames[i][0]!='\0')  VOID unlink(tfnames[i]);
+        }
+        InitCleanup();
+        return (rmsema());
+}
+
+
+char * mktempfile(fullpath,filename)
+register char * fullpath, * filename;
+/* Function: Creates a unique filename using the process id and stores it
+ * into a free slot in tfnames. The filename consists of the path contained
+ * in fullpath concatenated with filename. filename should end in "XXXXXX".
+ * Because of storage in tfnames, cleanup() can unlink the file later.
+ * lastfilename indicates the highest occupied slot in tfnames.
+ * Returns a pointer to the filename created.
+ * Example use: mktempfile("/tmp/", somefilename)
+ */
+{
+        register char * lastslash, *tp;
+        lastfilename++;
+        if ((tp=tfnames[lastfilename])==nil)
+              tp=tfnames[lastfilename] = malloc(NCPPN);
+        if (fullpath!=nil && (lastslash=rindex(fullpath,'/'))!=0) {
+                /* copy path */
+                while (fullpath<=lastslash) *tp++ = *fullpath++;
+        }
+        while (*tp++ = *filename++);
+        return (mktemp(tfnames[lastfilename]));
+}
+
+
+
+
+char * bindex(sp,c)
+register char * sp, c;
+/* Function: Finds the last occurrence of character c in string sp
+ * and returns a pointer to the character just beyond it. If the
+ * character doesn't occur in the string, sp is returned.
+ */
+{       register char * r;
+        r = sp;
+        while (*sp) {
+                if (*sp++ == c) r=sp;
+        }
+        return r;
+}
+
+
+
+
+
+InitAdmin()
+/* function: initializes an admin node */
+{       register char * Suffix;
+        register int i;
+
+        Head=Dbranch=nil; AccessList=nil; Symbols=nil; Locks=nil;
+        StrictLocks=STRICT_LOCKING;
+
+        /* guess the comment leader from the suffix*/
+        Suffix=bindex(workfilename, '.');
+        if (Suffix==workfilename) Suffix= ""; /* empty suffix; will get default*/
+        for (i=0;;i++) {
+                if (comtable[i].suffix==nil) {
+                        Comment=comtable[i].comlead; /*default*/
+                        break;
+                } elsif (strcmp(Suffix,comtable[i].suffix)==0) {
+                        Comment=comtable[i].comlead; /*default*/
+                        break;
+                }
+        }
+        Lexinit(); /* Note: if finptr==NULL, reads nothing; only initializes*/
+}
+
+
+
+char * findpairfile(argc, argv, fname)
+int argc; char * argv[], *fname;
+/* Function: Given a filename fname, findpairfile scans argv for a pathname
+ * ending in fname. If found, returns a pointer to the pathname, and sets
+ * the corresponding pointer in argv to nil. Otherwise returns fname.
+ * argc indicates the number of entries in argv. Some of them may be nil.
+ */
+{
+        register char * * next, * match;
+        register int count;
+
+        for (next = argv, count = argc; count>0; next++,count--) {
+                if ((*next != nil) && strcmp(bindex(*next,'/'),fname)==0) {
+                        /* bindex finds the beginning of the file name stem */
+                        match= *next;
+                        *next=nil;
+                        return match;
+                }
+        }
+        return fname;
+}
+
+
+int pairfilenames(argc, argv, mustread, tostdout)
+int argc; char ** argv; int mustread, tostdout;
+/* Function: Pairs the filenames pointed to by argv; argc indicates
+ * how many there are.
+ * Places a pointer to the RCS filename into RCSfilename,
+ * and a pointer to the name of the working file into workfilename.
+ * If both the workfilename and the RCS filename are given, and tostdout
+ * is true, a warning is printed.
+ *
+ * If the working file exists, places its status into workstat and
+ * sets haveworkstat to 0; otherwise, haveworkstat is set to -1;
+ * Similarly for the RCS file and the variables RCSstat and haveRCSstat.
+ *
+ * If the RCS file exists, it is opened for reading, the file pointer
+ * is placed into finptr, and the admin-node is read in; returns 1.
+ * If the RCS file does not exist and mustread==true, an error is printed
+ * and 0 returned.
+ * If the RCS file does not exist and mustread==false, the admin node
+ * is initialized to empty (Head, AccessList, Locks, Symbols, StrictLocks, Dbranch)
+ * and -1 returned.
+ *
+ * 0 is returned on all errors. Files that are directories are errors.
+ * Also calls InitCleanup();
+ */
+{
+        register char * sp, * tp;
+        char * lastsep, * purefname, * pureRCSname;
+        int opened, returncode;
+        char * RCS1;
+       char prefdir[NCPPN];
+
+        if (*argv == nil) return 0; /* already paired filename */
+       if (rindex(*argv,KDELIM)!=0) {
+               /* KDELIM causes havoc in keyword expansion    */
+               error("RCS file name may not contain %c",KDELIM);
+               return 0;
+       }
+        InitCleanup();
+
+        /* first check suffix to see whether it is an RCS file or not */
+        purefname=bindex(*argv, '/'); /* skip path */
+        lastsep=rindex(purefname, RCSSEP);
+        if (lastsep!= 0 && *(lastsep+1)==RCSSUF && *(lastsep+2)=='\0') {
+                /* RCS file name given*/
+                RCS1=(*argv); pureRCSname=purefname;
+                /* derive workfilename*/
+                sp = purefname; tp=tempfilename;
+                while (sp<lastsep) *tp++ = *sp++; *tp='\0';
+                /* try to find workfile name among arguments */
+                workfilename=findpairfile(argc-1,argv+1,tempfilename);
+                if (strlen(pureRCSname)>NCPFN) {
+                        error("RCS file name %s too long",RCS1);
+                        return 0;
+                }
+        } else {
+                /* working file given; now try to find RCS file */
+                workfilename= *argv;
+                /* derive RCS file name*/
+                sp=purefname; tp=tempfilename;
+                while (*tp++ = *sp++);
+                *(tp-1)=RCSSEP; *tp++=RCSSUF; *tp++='\0';
+                /* Try to find RCS file name among arguments*/
+                RCS1=findpairfile(argc-1,argv+1,tempfilename);
+                pureRCSname=bindex(RCS1, '/');
+                if (strlen(pureRCSname)>NCPFN) {
+                        error("working file name %s too long",workfilename);
+                        return 0;
+                }
+        }
+        /* now we have a (tentative) RCS filename in RCS1 and workfilename  */
+        /* First, get status of workfilename */
+        haveworkstat=stat(workfilename, &workstat);
+        if ((haveworkstat==0) && ((workstat.st_mode & S_IFDIR) == S_IFDIR)) {
+                diagnose("Directory %s ignored",workfilename);
+                return 0;
+        }
+        /* Second, try to find the right RCS file */
+        if (pureRCSname!=RCS1) {
+                /* a path for RCSfile is given; single RCS file to look for */
+                finptr=fopen(RCSfilename=RCS1, "r");
+                if (finptr!=NULL) {
+                    returncode=1;
+                } else { /* could not open */
+                    if (access(RCSfilename,0)==0) {
+                        error("Can't open existing %s", RCSfilename);
+                        return 0;
+                    }
+                    if (mustread) {
+                        error("Can't find %s", RCSfilename);
+                        return 0;
+                    } else {
+                        /* initialize if not mustread */
+                        returncode = -1;
+                    }
+                }
+        } else {
+               /* no path for RCS file name. Prefix it with path of work */
+               /* file if RCS file omitted. Make a second name including */
+               /* RCSDIR and try to open that one first.                 */
+               sub1filename[0]=sub2filename[0]= '\0';
+               if (RCS1==tempfilename) {
+                       /* RCS file name not given; prepend work path */
+                       sp= *argv; tp= sub1filename;
+                       while (sp<purefname) *tp++ = *sp ++;
+                       *tp='\0';
+                       VOID strcpy(sub2filename,sub1filename); /* second one */
+               }
+               VOID strcat(sub1filename,RCSDIR);
+               VOID strcpy(prefdir,sub1filename); /* preferred directory for RCS file*/
+               VOID strcat(sub1filename,RCS1); VOID strcat(sub2filename,RCS1);
+
+
+                opened=(
+               ((finptr=fopen(RCSfilename=sub1filename, "r"))!=NULL) ||
+               ((finptr=fopen(RCSfilename=sub2filename,"r"))!=NULL) );
+
+                if (opened) {
+                        /* open succeeded */
+                        returncode=1;
+                } else {
+                        /* open failed; may be read protected */
+                       if ((access(RCSfilename=sub1filename,0)==0) ||
+                           (access(RCSfilename=sub2filename,0)==0)) {
+                                error("Can't open existing %s",RCSfilename);
+                                return 0;
+                        }
+                        if (mustread) {
+                               error("Can't find %s nor %s",sub1filename,sub2filename);
+                                return 0;
+                        } else {
+                                /* initialize new file. Put into ./RCS if possible, strip off suffix*/
+                               RCSfilename= (access(prefdir,0)==0)?sub1filename:sub2filename;
+                                returncode= -1;
+                        }
+                }
+        }
+
+        if (returncode == 1) { /* RCS file open */
+                haveRCSstat=fstat(fileno(finptr),&RCSstat);
+                if ((haveRCSstat== 0) && ((RCSstat.st_mode & S_IFDIR) == S_IFDIR)) {
+                        diagnose("Directory %s ignored",RCSfilename);
+                        return 0;
+                }
+                Lexinit(); getadmin();
+        } else {  /* returncode == -1; RCS file nonexisting */
+                haveRCSstat = -1;
+                InitAdmin();
+        };
+
+        if (tostdout&&
+            !(RCS1==tempfilename||workfilename==tempfilename))
+                /*The last term determines whether a pair of        */
+                /* file names was given in the argument list        */
+                warn("Option -p is set; ignoring output file %s",workfilename);
+
+        return returncode;
+}
+
+
+char * getfullRCSname()
+/* Function: returns a pointer to the full path name of the RCS file.
+ * Calls getwd(), but only once.
+ * removes leading "../" and "./".
+ */
+{       static char pathbuf[NCPPN];
+        static char namebuf[NCPPN];
+        static int  pathlength =0;
+
+        register char * realname, * lastpathchar;
+        register int  dotdotcounter, realpathlength;
+
+        if (*RCSfilename=='/') {
+                return(RCSfilename);
+        } else {
+                if (pathlength==0) { /*call curdir for the first time*/
+                    if (getwd(pathbuf)==NULL)
+                        faterror("Can't build current directory path");
+                    pathlength=strlen(pathbuf);
+                    if (!((pathlength==1) && (pathbuf[0]=='/'))) {
+                        pathbuf[pathlength++]='/';
+                        /* Check needed because some getwd implementations */
+                        /* generate "/" for the root.                      */
+                    }
+                }
+                /*the following must be redone since RCSfilename may change*/
+                /* find how many ../ to remvove from RCSfilename */
+                dotdotcounter =0;
+                realname = RCSfilename;
+                while( realname[0]=='.' &&
+                      (realname[1]=='/'||(realname[1]=='.'&&realname[2]=='/'))){
+                        if (realname[1]=='/') {
+                            /* drop leading ./ */
+                            realname += 2;
+                        } else {
+                            /* drop leading ../ and remember */
+                            dotdotcounter++;
+                            realname += 3;
+                        }
+                }
+                /* now remove dotdotcounter trailing directories from pathbuf*/
+                lastpathchar=pathbuf + pathlength-1;
+                while (dotdotcounter>0 && lastpathchar>pathbuf) {
+                    /* move pointer backwards over trailing directory */
+                    lastpathchar--;
+                    if (*lastpathchar=='/') {
+                        dotdotcounter--;
+                    }
+                }
+                if (dotdotcounter>0) {
+                    error("Can't generate full path name for RCS file");
+                    return RCSfilename;
+                } else {
+                    /* build full path name */
+                    realpathlength=lastpathchar-pathbuf+1;
+                    VOID strncpy(namebuf,pathbuf,realpathlength);
+                    VOID strcpy(&namebuf[realpathlength],realname);
+                    return(namebuf);
+                }
+        }
+}
+
+
+
+int trydiraccess(filename)
+char * filename;
+/* checks write permission in directory of filename and returns
+ * true if writable, false otherwise
+ */
+{
+        char pathname[NCPPN];
+        register char * tp, *sp, *lp;
+        lp = rindex(filename,'/');
+        if (lp==0) {
+                /* check current directory */
+                if (access(".",2)==0)
+                        return true;
+                else {
+                        error("Current directory not writable");
+                        return false;
+                }
+        }
+        /* copy path */
+        sp=filename;
+        tp=pathname;
+        do *tp++ = *sp++; while (sp<=lp);
+        *tp='\0';
+        if (access(pathname,2)==0)
+                return true;
+        else {
+                error("Directory %s not writable", pathname);
+                return false;
+        }
+}
+
+
+
+#ifndef V4_2BSD
+/* rename() and getwd() will be provided in bsd 4.2 */
+
+
+int rename(from, to)
+char * from, *to;
+/* Function: renames a file with the name given by from to the name given by to.
+ * unlinks the to-file if it already exists. returns -1 on error, 0 otherwise.
+ */
+{       VOID unlink(to);      /* no need to check return code; will be caught by link*/
+                         /* no harm done if file "to" does not exist            */
+        if (link(from,to)<0) return -1;
+        return(unlink(from));
+}
+
+
+
+#define dot     "."
+#define dotdot  ".."
+
+
+
+char * getwd(name)
+char * name;
+/* Function: places full pathname of current working directory into name and
+ * returns name on success, NULL on failure.
+ * getwd is an adaptation of pwd. May not return to the current directory on
+ * failure.
+ */
+{
+        FILE    *file;
+        struct  stat    d, dd;
+        char buf[2];    /* to NUL-terminate dir.d_name */
+        struct  direct  dir;
+
+        int rdev, rino;
+        int off;
+        register i,j;
+
+        name[off= 0] = '/';
+        name[1] = '\0';
+        buf[0] = '\0';
+        stat("/", &d);
+        rdev = d.st_dev;
+        rino = d.st_ino;
+        for (;;) {
+                if (stat(dot, &d)<0) return NULL;
+                if (d.st_ino==rino && d.st_dev==rdev) {
+                        if (name[off] == '/') name[off] = '\0';
+                        chdir(name); /*change back to current directory*/
+                        return name;
+                }
+                if ((file = fopen(dotdot,"r")) == NULL) return NULL;
+                if (fstat(fileno(file), &dd)<0) goto fail;
+                chdir(dotdot);
+                if(d.st_dev == dd.st_dev) {
+                        if(d.st_ino == dd.st_ino) {
+                            if (name[off] == '/') name[off] = '\0';
+                            chdir(name); /*change back to current directory*/
+                            VOID fclose(file);
+                            return name;
+                        }
+                        do {
+                            if (fread((char *)&dir, sizeof(dir), 1, file) !=1)
+                                goto fail;
+                        } while (dir.d_ino != d.st_ino);
+                }
+                else do {
+                        if(fread((char *)&dir, sizeof(dir), 1, file) != 1) {
+                            goto fail;
+                        }
+                        stat(dir.d_name, &dd);
+                } while(dd.st_ino != d.st_ino || dd.st_dev != d.st_dev);
+                VOID fclose(file);
+
+                /* concatenate file name */
+                i = -1;
+                while (dir.d_name[++i] != 0);
+                for(j=off+1; j>0; --j)
+                        name[j+i+1] = name[j];
+                off=i+off+1;
+                name[i+1] = '/';
+                for(--i; i>=0; --i)
+                        name[i+1] = dir.d_name[i];
+        } /* end for */
+
+fail:   VOID fclose(file);
+        return NULL;
+}
+
+
+#endif
+
+
+#ifdef PAIRTEST
+/* test program for pairfilenames() and getfullRCSname() */
+char * workfilename, *RCSfilename;
+extern int quietflag;
+
+main(argc, argv)
+int argc; char *argv[];
+{
+        int result;
+        int initflag,tostdout;
+        quietflag=tostdout=initflag=false;
+        cmdid="pair";
+
+        while(--argc, ++argv, argc>=1 && ((*argv)[0] == '-')) {
+                switch ((*argv)[1]) {
+
+                case 'p':       tostdout=true;
+                                break;
+                case 'i':       initflag=true;
+                                break;
+                case 'q':       quietflag=true;
+                                break;
+                default:        error("unknown option: %s", *argv);
+                                break;
+                }
+        }
+
+        do {
+                RCSfilename=workfilename=nil;
+                result=pairfilenames(argc,argv,!initflag,tostdout);
+                if (result!=0) {
+                     diagnose("RCSfile: %s; working file: %s",RCSfilename,workfilename);
+                     diagnose("Full RCS file name: %s", getfullRCSname());
+                }
+                switch (result) {
+                        case 0: continue; /* already paired file */
+
+                        case 1: if (initflag) {
+                                    error("RCS file %s exists already",RCSfilename);
+                                } else {
+                                    diagnose("RCS file %s exists",RCSfilename);
+                                }
+                                VOID fclose(finptr);
+                                break;
+
+                        case -1:diagnose("RCS file does not exist");
+                                break;
+                }
+
+        } while (++argv, --argc>=1);
+
+}
+#endif