1: /*
2: * RCS stream editor
3: */
4: static char rcsid[]=
5: "$Header: rcsedit.c,v 3.8 86/05/15 02:15:43 lepreau Exp $ Purdue CS";
6: /**********************************************************************************
7: * edits the input file according to a
8: * script from stdin, generated by diff -n
9: * performs keyword expansion
10: **********************************************************************************
11: *
12: * Copyright (C) 1982 by Walter F. Tichy
13: * Purdue University
14: * Computer Science Department
15: * West Lafayette, IN 47907
16: *
17: * All rights reserved. No part of this software may be sold or distributed
18: * in any form or by any means without the prior written permission of the
19: * author.
20: * Report problems and direct all inquiries to Tichy@purdue (ARPA net).
21: */
22:
23:
24: /* $Log: rcsedit.c,v $
25: * Revision 3.8 86/05/15 02:15:43 lepreau
26: * Use "Locked" instead of state in $Head expansion if locked.
27: *
28: * Revision 3.7 83/05/12 13:04:39 wft
29: * *** empty log message ***
30: *
31: * Revision 3.7 83/05/12 13:04:39 wft
32: * Added retry to expandline to resume after failed match which ended in $.
33: * Fixed truncation problem for $19chars followed by@@.
34: * Log no longer expands full path of RCS file.
35: *
36: * Revision 3.5 82/12/04 13:20:56 wft
37: * Added expansion of keyword Locker.
38: *
39: * Revision 3.4 82/12/03 12:26:54 wft
40: * Added line number correction in case editing does not start at the
41: * beginning of the file.
42: * Changed keyword expansion to always print a space before closing KDELIM;
43: * Expansion for Header shortened.
44: *
45: * Revision 3.3 82/11/14 14:49:30 wft
46: * removed Suffix from keyword expansion. Replaced fclose with ffclose.
47: * keyreplace() gets log message from delta, not from curlogmsg.
48: * fixed expression overflow in while(c=putc(GETC....
49: * checked nil printing.
50: *
51: * Revision 3.2 82/10/18 21:13:39 wft
52: * I added checks for write errors during the co process, and renamed
53: * expandstring() to xpandstring().
54: *
55: * Revision 3.1 82/10/13 15:52:55 wft
56: * changed type of result of getc() from char to int.
57: * made keyword expansion loop in expandline() portable to machines
58: * without sign-extension.
59: */
60:
61:
62: #include "rcsbase.h"
63:
64:
65: extern FILE * fopen();
66: extern char * mktempfile();
67: extern FILE * finptr, * frewrite;
68: extern int rewriteflag;
69: extern int nextc;
70: extern char * getfullRCSname();
71: extern char * RCSfilename;
72:
73:
74: FILE * fcopy, * fedit; /* result and edit file descriptors */
75: char *resultfile = nil; /* result file name */
76: char * editfile = nil; /* edit file name */
77: int editline; /*line counter in fedit; starts with 1, is always #lines+1 */
78: int linecorr; /*contains #adds - #deletes in each edit run. */
79: /*used to correct editline in case file is not rewound after */
80: /* applying one delta */
81:
82: initeditfiles(dir)
83: char * dir;
84: /* Function: Initializes resultfile and editfile with temporary filenames
85: * in directory dir. Opens resultfile for reading and writing, with fcopy
86: * as file descriptor. fedit is set to nil.
87: */
88: {
89: resultfile=mktempfile(dir,TMPFILE1);
90: editfile =mktempfile(dir,TMPFILE2);
91: fedit=nil;
92: if ((fcopy=fopen(resultfile,"w+"))==NULL) {
93: faterror("Can't open working file %s",resultfile);
94: }
95: }
96:
97:
98: swapeditfiles(tostdout)
99: /* Function: swaps resultfile and editfile, assigns fedit=fcopy,
100: * rewinds fedit for reading, and opens resultfile for reading and
101: * writing, using fcopy. If tostdout, fcopy is set to stdout.
102: */
103: { char * tmpptr;
104: if(ferror(fcopy))
105: faterror("write failed on %s -- file system full?",resultfile);
106: fedit=fcopy;
107: rewind(fedit);
108: editline = 1; linecorr=0;
109: tmpptr=editfile; editfile=resultfile; resultfile=tmpptr;
110: if (tostdout)
111: fcopy=stdout;
112: elsif ((fcopy=fopen(resultfile,"w+"))==NULL) {
113: faterror("Can't open working file %s",resultfile);
114: }
115: }
116:
117:
118: finishedit(delta)
119: struct hshentry * delta;
120: /* copy the rest of the edit file and close it (if it exists).
121: * if delta!=nil, perform keyword substitution at the same time.
122: */
123: {
124: register int c;
125: if (fedit!=nil) {
126: if (delta!=nil) {
127: while (expandline(fedit,fcopy,delta,false,false)) editline++;
128: } else {
129: while((c=getc(fedit))!=EOF) {
130: putc(c,fcopy);
131: if (c=='\n') editline++;
132: }
133: }
134: ffclose(fedit);
135: }
136: }
137:
138:
139: copylines(line,delta)
140: register int line; struct hshentry * delta;
141: /* Function: copies input lines editline..line-1 from fedit to fcopy.
142: * If delta != nil, keyword expansion is done simultaneously.
143: * editline is updated. Rewinds a file only if necessary.
144: */
145: {
146:
147: if (editline>line) {
148: /* swap files */
149: finishedit(nil); swapeditfiles(false);
150: /* assumes edit only during last pass, from the beginning*/
151: }
152: while (editline<line) {
153: /*copy another line*/
154: if (delta)
155: expandline(fedit,fcopy,delta,false,false);
156: else
157: while (putc(getc(fedit),fcopy)!='\n');
158: editline++;
159: }
160: }
161:
162:
163:
164: xpandstring(delta)
165: struct hshentry * delta;
166: /* Function: Reads a string terminated by SDELIM from finptr and writes it
167: * to fcopy. Double SDELIM is replaced with single SDELIM.
168: * Keyword expansion is performed with data from delta.
169: * If rewriteflag==true, the string is also copied unchanged to frewrite.
170: * editline is updated.
171: */
172: {
173: editline=1;
174: while (expandline(finptr,fcopy,delta,true,rewriteflag)) editline++;
175: nextc='\n';
176: }
177:
178:
179: copystring()
180: /* Function: copies a string terminated with a single SDELIM from finptr to
181: * fcopy, replacing all double SDELIM with a single SDELIM.
182: * If rewriteflag==true, the string also copied unchanged to frewrite.
183: * editline is set to (number of lines copied)+1.
184: * Assumption: next character read is first string character.
185: */
186: { register c, write;
187: write=rewriteflag;
188: editline=1;
189: while ((c=GETC(finptr,frewrite,write)) != EOF) {
190: if ((c==SDELIM)&&((c=GETC(finptr,frewrite,write)) != SDELIM)){
191: /* end of string */
192: nextc = c;
193: return;
194: }
195: putc(c,fcopy);
196: if (c=='\n') editline++;
197: }
198: nextc = c;
199: serror("Unterminated string");
200: return;
201: }
202:
203:
204:
205:
206: editstring(delta)
207: struct hshentry * delta;
208: /* Function: reads an edit script from finptr and applies it to
209: * file fedit; the result is written to fcopy.
210: * If delta!=nil, keyword expansion is performed simultaneously.
211: * If frewrite==true, the edit script is also copied verbatim to frewrite.
212: * Assumes that all these files are open.
213: * If running out of lines in fedit, fedit and fcopy are swapped.
214: * resultfile and editfile are the names of the files that go with fcopy
215: * and fedit, respectively.
216: * Assumes the next input character from finptr is the first character of
217: * the edit script. Resets nextc on exit.
218: */
219: {
220: int ed; /* editor command */
221: register int c;
222: register int write, i;
223: int line, length;
224:
225: editline += linecorr; linecorr=0; /*correct line number*/
226: write=rewriteflag;
227: for (;;) {
228: /* read next command and decode */
229: /* assume next non-white character is command name*/
230: while((ed=GETC(finptr,frewrite,write))=='\n'||
231: ed==' ' || ed=='\t');
232: if (ed==SDELIM) break;
233: /* now attempt to read numbers. */
234: /* fscanf causes trouble because of the required echoing */
235: while ((c=GETC(finptr,frewrite,write))==' '); /*skip spaces*/
236: if (!('0'<=c && c<='9')) {
237: faterror("missing line number in edit script");
238: break;
239: }
240: line= c -'0';
241: while ('0'<=(c=GETC(finptr,frewrite,write)) && c<='9') {
242: line = line*10 + c-'0';
243: }
244: while (c==' ') c=GETC(finptr,frewrite,write);
245: if (!('0'<=c && c<='9')) {
246: faterror("incorrect range in edit script");
247: break;
248: }
249: length= c -'0';
250: while ('0'<=(c=GETC(finptr,frewrite,write)) && c<='9') {
251: length = length*10 + c-'0';
252: }
253: while(c!='\n'&&c!=EOF) c=GETC(finptr,frewrite,write); /* skip to end of line */
254:
255: switch (ed) {
256: case 'd':
257: copylines(line,delta);
258: /* skip over unwanted lines */
259: for (i=length;i>0;i--) {
260: /*skip next line*/
261: while ((c=getc(fedit))!='\n')
262: if (c==EOF)
263: faterror("EOF during edit");
264: editline++;
265: }
266: linecorr -= length;
267: break;
268: case 'a':
269: copylines(line+1,delta); /*copy only; no delete*/
270: for (i=length;i>0;i--) {
271: /*copy next line from script*/
272: if (delta!=nil)
273: expandline(finptr,fcopy,delta,true,write);
274: else {
275: c = GETC(finptr,frewrite,write);
276: while (putc(c,fcopy)!='\n'){
277: if ((c==SDELIM)&&((c=GETC(finptr,frewrite,write))!=SDELIM)){
278: serror("Missing string delimiter in edit script");
279: putc(c,fcopy);
280: }
281: c = GETC(finptr,frewrite,write);
282: }
283: }
284: }
285: linecorr += length;
286: break;
287: default:
288: faterror("unknown command in edit script: %c", ed);
289: break;
290: }
291: }
292: nextc=GETC(finptr,frewrite,write);
293: }
294:
295:
296:
297: /* The rest if for keyword expansion */
298:
299: struct { char * keyword; enum markers marker;} markertable[] =
300: {{AUTHOR, Author },
301: {DATE, Date },
302: {HEADER, Header },
303: {LOCKER, Locker },
304: {LOG, Log },
305: {REVISION, Revision},
306: {SOURCE, Source },
307: {STATE, State },
308: {nil, Nomatch }};
309:
310: enum markers trymatch(string)
311: char * string;
312: /* function: Checks whether string is a keyword.
313: * If so, returns the appropriate marker, otherwise Nomatch.
314: */
315: {
316: register int j;
317: for (j=0; markertable[j].keyword!=nil; j++ ) {
318: if (strcmp(string, markertable[j].keyword) ==0)
319: return(markertable[j].marker);
320: }
321: return(Nomatch);
322: }
323:
324:
325:
326: expandline(in, out, delta,delimstuffed,write)
327: FILE * in, * out; struct hshentry * delta;
328: register int delimstuffed, write;
329: /* Function: Reads a line from in and writes it to out.
330: * If delimstuffed==true, double SDELIM is replaced with single SDELIM.
331: * Keyword expansion is performed with data from delta.
332: * If write==true, the string is also copied unchanged to frewrite.
333: * Returns false if end-of-string or end-of-line is detected, true otherwise.
334: */
335: {
336: register c, j;
337: char keystring[keylength];
338: char keyval[keyvallength];
339: enum markers matchresult;
340:
341: for (;;) {
342: c=GETC(in,frewrite,write);
343: if (c==EOF) {
344: if(delimstuffed) {
345: error("unterminated string");
346: nextc=c;
347: }
348: return(false);
349: }
350:
351: if (c==SDELIM && delimstuffed) {
352: if ((c=GETC(in,frewrite,write))!=SDELIM) {
353: /* end of string */
354: nextc=c;
355: return false;
356: }
357: }
358: putc(c,out);
359:
360: if (c=='\n') return true; /* end of line */
361:
362: retry: if (c==KDELIM) {
363: /* check for keyword */
364: /* first, copy a long enough string into keystring */
365: j=0;
366: while (((c=GETC(in,frewrite,write))!=EOF) && (j<keylength-1) && (c!='\n')
367: && (c!=KDELIM) && (c!=VDELIM)) {
368: putc(c,out);
369: keystring[j++] = c;
370: if (c==SDELIM && delimstuffed) { /*skip next SDELIM */
371: c=GETC(in,frewrite,write);
372: /* Can't be at end of string -- always a '\n' before*/
373: /* closing SDELIM */
374: }
375: }
376: if (!((c==KDELIM) || (c==VDELIM))) {
377: /* no match */
378: /* can get SDELIM here if have $...keylength-1 chars...@@ */
379: if (c==SDELIM && delimstuffed) {
380: c=GETC(in,frewrite,write);
381: }
382: putc(c,out);
383: if (c=='\n') return true; /* end of line */
384: } else {
385: /* no we have something that looks like a */
386: /* keyword, and is terminated with K/VDELIM*/
387: keystring[j]= '\0';
388: if ((matchresult=trymatch(keystring))==Nomatch) {
389: /* no match */
390: putc(c,out);
391: if (c==KDELIM) goto retry;
392: } elsif (c==VDELIM) {
393: /* try to find closing KDELIM, and replace value */
394: j=0;
395: while (((c=GETC(in,frewrite,write)) != EOF)
396: && (c!='\n') && (c!=KDELIM) && (j<keyvallength-2)) {
397: keyval[j++] =c;
398: if (c==SDELIM && delimstuffed) { /*skip next SDELIM */
399: c=GETC(in,frewrite,write);
400: /* Can't be at end of string -- always a '\n' before*/
401: /* closing SDELIM */
402: }
403: }
404: keyval[j++] =c;
405: if (c!=KDELIM) {
406: /* couldn't find closing KDELIM -- give up */
407: putc(VDELIM,out); keyval[j]='\0';fputs(keyval,out);
408: if (c=='\n') return true; /* end of line */
409: } else {
410: /* found complete pattern -- replace */
411: keyreplace(matchresult,delta,out);
412: }
413: } else {
414: /* string of the form $keyword$ */
415: keyreplace(matchresult,delta,out);
416: }
417: }
418: }
419: } /* end for */
420: }
421:
422:
423:
424: keyreplace(marker,delta,out)
425: enum markers marker; struct hshentry * delta; FILE * out;
426: /* function: ouputs the keyword value(s) corresponding to marker.
427: * Attributes are derived from delta.
428: */
429: {
430: char * date;
431: register char * sp;
432:
433: date= delta->date;
434:
435: switch (marker) {
436: case Author:
437: fprintf(out,"%c %s %c",VDELIM,delta->author,KDELIM);
438: break;
439: case Date:
440: putc(VDELIM,out);putc(' ',out);
441: PRINTDATE(out,date);putc(' ',out);
442: PRINTTIME(out,date);putc(' ',out);putc(KDELIM,out);
443: break;
444: case :
445: fprintf(out,"%c %s %s ",VDELIM,bindex(RCSfilename,'/'),
446: delta->num);
447: PRINTDATE(out,date);putc(' ',out);PRINTTIME(out,date);
448: if (delta->lockedby == nil)
449: fprintf(out, " %s %s ", delta->author, delta->state);
450: else
451: fprintf(out," %s Locked ", delta->lockedby);
452: putc(KDELIM, out);
453: break;
454: case Locker:
455: fprintf(out,"%c %s %c", VDELIM,
456: delta->lockedby==nil?"":delta->lockedby,KDELIM);
457: break;
458: case Log:
459: fprintf(out, "%c\t%s %c\n%sRevision %s ",
460: VDELIM, bindex(RCSfilename,'/'), KDELIM, Comment, delta->num);
461: PRINTDATE(out,date);fputs(" ",out);PRINTTIME(out,date);
462: fprintf(out, " %s\n%s",delta->author,Comment);
463: /* do not include state here because it may change and is not updated*/
464: sp = delta->log;
465: while (*sp) if (putc(*sp++,out)=='\n') fputs(Comment,out);
466: /* Comment is the comment leader */
467: break;
468: case Revision:
469: fprintf(out,"%c %s %c",VDELIM,delta->num,KDELIM);
470: break;
471: case Source:
472: fprintf(out,"%c %s %c",VDELIM,getfullRCSname(),KDELIM);
473: break;
474: case State:
475: fprintf(out,"%c %s %c",VDELIM,delta->state,KDELIM);
476: break;
477: case Nomatch:
478: putc(KDELIM,out);
479: break;
480: }
481: }