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