+/*
+ * RCS stream editor
+ */
+ static char rcsid[]=
+ "$Header: /usr/wft/RCS/SRC/RCS/rcsedit.c,v 3.7 83/05/12 13:04:39 wft Exp $ Purdue CS";
+/**********************************************************************************
+ * 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.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.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 FILE * finptr, * frewrite;
+extern int rewriteflag;
+extern int nextc;
+extern char * getfullRCSname();
+extern char * RCSfilename;
+
+
+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.
+ */
+{
+ 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;
+ if (editline!=1) rewind(fedit);
+ /* no need to rewind at beginning of file */
+ 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) {
+ 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(nil); swapeditfiles(false);
+ /* assumes edit only during last pass, from the beginning*/
+ }
+ while (editline<line) {
+ /*copy another line*/
+ if (delta)
+ 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;
+ }
+ 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 (getc(fedit)!='\n');
+ 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)
+ 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");
+ 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 */
+
+struct { char * keyword; enum markers marker;} markertable[] =
+ {{AUTHOR, Author },
+ {DATE, Date },
+ {HEADER, Header },
+ {LOCKER, Locker },
+ {LOG, Log },
+ {REVISION, Revision},
+ {SOURCE, Source },
+ {STATE, State },
+ {nil, Nomatch }};
+
+enum markers trymatch(string)
+char * string;
+/* function: Checks whether string is a keyword.
+ * If so, returns the appropriate marker, otherwise Nomatch.
+ */
+{
+ register int j;
+ for (j=0; markertable[j].keyword!=nil; j++ ) {
+ if (strcmp(string, markertable[j].keyword) ==0)
+ return(markertable[j].marker);
+ }
+ return(Nomatch);
+}
+
+
+
+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, j;
+ char keystring[keylength];
+ char keyval[keyvallength];
+ enum markers matchresult;
+
+ for (;;) {
+ c=GETC(in,frewrite,write);
+ 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;
+ }
+ }
+ putc(c,out);
+
+ if (c=='\n') return true; /* end of line */
+
+retry: if (c==KDELIM) {
+ /* check for keyword */
+ /* first, copy a long enough string into keystring */
+ j=0;
+ while (((c=GETC(in,frewrite,write))!=EOF) && (j<keylength-1) && (c!='\n')
+ && (c!=KDELIM) && (c!=VDELIM)) {
+ putc(c,out);
+ keystring[j++] = 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) || (c==VDELIM))) {
+ /* no match */
+ /* can get SDELIM here if have $...keylength-1 chars...@@ */
+ if (c==SDELIM && delimstuffed) {
+ c=GETC(in,frewrite,write);
+ }
+ putc(c,out);
+ if (c=='\n') return true; /* end of line */
+ } else {
+ /* no we have something that looks like a */
+ /* keyword, and is terminated with K/VDELIM*/
+ keystring[j]= '\0';
+ if ((matchresult=trymatch(keystring))==Nomatch) {
+ /* no match */
+ putc(c,out);
+ if (c==KDELIM) goto retry;
+ } elsif (c==VDELIM) {
+ /* try to find closing KDELIM, and replace value */
+ j=0;
+ while (((c=GETC(in,frewrite,write)) != EOF)
+ && (c!='\n') && (c!=KDELIM) && (j<keyvallength-2)) {
+ keyval[j++] =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 */
+ }
+ }
+ keyval[j++] =c;
+ if (c!=KDELIM) {
+ /* couldn't find closing KDELIM -- give up */
+ putc(VDELIM,out); keyval[j]='\0';fputs(keyval,out);
+ if (c=='\n') return true; /* end of line */
+ } else {
+ /* found complete pattern -- replace */
+ keyreplace(matchresult,delta,out);
+ }
+ } else {
+ /* string of the form $keyword$ */
+ keyreplace(matchresult,delta,out);
+ }
+ }
+ }
+ } /* 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:
+ fprintf(out,"%c %s %c",VDELIM,delta->author,KDELIM);
+ break;
+ case Date:
+ putc(VDELIM,out);putc(' ',out);
+ PRINTDATE(out,date);putc(' ',out);
+ PRINTTIME(out,date);putc(' ',out);putc(KDELIM,out);
+ break;
+ case Header:
+ fprintf(out,"%c %s %s ",VDELIM,bindex(RCSfilename,'/'),
+ delta->num);
+ PRINTDATE(out,date);putc(' ',out);PRINTTIME(out,date);
+ fprintf(out, " %s %s %c",delta->author,delta->state,KDELIM);
+ break;
+ case Locker:
+ fprintf(out,"%c %s %c", VDELIM,
+ delta->lockedby==nil?"":delta->lockedby,KDELIM);
+ break;
+ case Log:
+ fprintf(out, "%c\t%s %c\n%sRevision %s ",
+ VDELIM, bindex(RCSfilename,'/'), KDELIM, Comment, delta->num);
+ PRINTDATE(out,date);fputs(" ",out);PRINTTIME(out,date);
+ 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') fputs(Comment,out);
+ /* Comment is the comment leader */
+ break;
+ case Revision:
+ fprintf(out,"%c %s %c",VDELIM,delta->num,KDELIM);
+ break;
+ case Source:
+ fprintf(out,"%c %s %c",VDELIM,getfullRCSname(),KDELIM);
+ break;
+ case State:
+ fprintf(out,"%c %s %c",VDELIM,delta->state,KDELIM);
+ break;
+ case Nomatch:
+ putc(KDELIM,out);
+ break;
+ }
+}
+
+