Sync usage with man page.
[netbsd-mini2440.git] / usr.bin / rcs / src / rcsedit.c
blobe12f020a20d9a93f93dfa0afa1571446e5b32a68
1 /*
2 * RCS stream editor
3 */
4 #ifndef lint
5 static char rcsid[]= "$Id: rcsedit.c,v 1.1 1993/03/21 09:58:07 cgd 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 **********************************************************************************
14 /* Copyright (C) 1982, 1988, 1989 Walter Tichy
15 * All rights reserved.
17 * Redistribution and use in source and binary forms are permitted
18 * provided that the above copyright notice and this paragraph are
19 * duplicated in all such forms and that any documentation,
20 * advertising materials, and other materials related to such
21 * distribution and use acknowledge that the software was developed
22 * by Walter Tichy.
23 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
24 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
25 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27 * Report all problems and direct all questions to:
28 * rcs-bugs@cs.purdue.edu
40 /* $Log: rcsedit.c,v $
41 * Revision 3.11 89/08/15 21:37:31 bostic
42 * Version 4 from Tom Narten at Purdue
44 * Revision 4.8 89/05/01 15:12:35 narten
45 * changed copyright header to reflect current distribution rules
47 * Revision 4.7 88/11/08 13:54:14 narten
48 * misplaced semicolon caused infinite loop
50 * Revision 4.6 88/11/08 12:01:41 narten
51 * changes from eggert@sm.unisys.com (Paul Eggert)
53 * Revision 4.6 88/08/09 19:12:45 eggert
54 * Shrink stdio code size; allow cc -R.
56 * Revision 4.5 87/12/18 11:38:46 narten
57 * Changes from the 43. version. Don't know the significance of the
58 * first change involving "rewind". Also, additional "lint" cleanup.
59 * (Guy Harris)
61 * Revision 4.4 87/10/18 10:32:21 narten
62 * Updating version numbers. Changes relative to version 1.1 actually
63 * relative to 4.1
65 * Revision 1.4 87/09/24 13:59:29 narten
66 * Sources now pass through lint (if you ignore printf/sprintf/fprintf
67 * warnings)
69 * Revision 1.3 87/09/15 16:39:39 shepler
70 * added an initializatin of the variables editline and linecorr
71 * this will be done each time a file is processed.
72 * (there was an obscure bug where if co was used to retrieve multiple files
73 * it would dump)
74 * fix attributed to Roy Morris @FileNet Corp ...!felix!roy
76 * Revision 1.2 87/03/27 14:22:17 jenkins
77 * Port to suns
79 * Revision 1.1 84/01/23 14:50:20 kcs
80 * Initial revision
82 * Revision 4.1 83/05/12 13:10:30 wft
83 * Added new markers Id and RCSfile; added locker to Header and Id.
84 * Overhauled expandline completely() (problem with $01234567890123456789@).
85 * Moved trymatch() and marker table to rcskeys.c.
87 * Revision 3.7 83/05/12 13:04:39 wft
88 * Added retry to expandline to resume after failed match which ended in $.
89 * Fixed truncation problem for $19chars followed by@@.
90 * Log no longer expands full path of RCS file.
92 * Revision 3.6 83/05/11 16:06:30 wft
93 * added retry to expandline to resume after failed match which ended in $.
94 * Fixed truncation problem for $19chars followed by@@.
96 * Revision 3.5 82/12/04 13:20:56 wft
97 * Added expansion of keyword Locker.
99 * Revision 3.4 82/12/03 12:26:54 wft
100 * Added line number correction in case editing does not start at the
101 * beginning of the file.
102 * Changed keyword expansion to always print a space before closing KDELIM;
103 * Expansion for Header shortened.
105 * Revision 3.3 82/11/14 14:49:30 wft
106 * removed Suffix from keyword expansion. Replaced fclose with ffclose.
107 * keyreplace() gets log message from delta, not from curlogmsg.
108 * fixed expression overflow in while(c=putc(GETC....
109 * checked nil printing.
111 * Revision 3.2 82/10/18 21:13:39 wft
112 * I added checks for write errors during the co process, and renamed
113 * expandstring() to xpandstring().
115 * Revision 3.1 82/10/13 15:52:55 wft
116 * changed type of result of getc() from char to int.
117 * made keyword expansion loop in expandline() portable to machines
118 * without sign-extension.
122 #include "rcsbase.h"
125 extern FILE * fopen();
126 extern char * mktempfile();
127 extern char * bindex();
128 extern FILE * finptr, * frewrite;
129 extern int rewriteflag;
130 extern int nextc;
131 extern char * RCSfilename, * workfilename;
132 extern char * bindex();
133 extern char * getfullRCSname();
134 extern enum markers trymatch();
137 FILE * fcopy, * fedit; /* result and edit file descriptors */
138 char *resultfile; /* result file name */
139 char * editfile; /* edit file name */
140 int editline; /*line counter in fedit; starts with 1, is always #lines+1 */
141 int linecorr; /*contains #adds - #deletes in each edit run. */
142 /*used to correct editline in case file is not rewound after */
143 /* applying one delta */
145 initeditfiles(dir)
146 char * dir;
147 /* Function: Initializes resultfile and editfile with temporary filenames
148 * in directory dir. Opens resultfile for reading and writing, with fcopy
149 * as file descriptor. fedit is set to nil.
152 editline = linecorr = 0; /* make sure we start from the beginning*/
153 resultfile=mktempfile(dir,TMPFILE1);
154 editfile =mktempfile(dir,TMPFILE2);
155 fedit=nil;
156 if ((fcopy=fopen(resultfile,"w+"))==NULL) {
157 faterror("Can't open working file %s",resultfile);
162 swapeditfiles(tostdout)
163 /* Function: swaps resultfile and editfile, assigns fedit=fcopy,
164 * rewinds fedit for reading, and opens resultfile for reading and
165 * writing, using fcopy. If tostdout, fcopy is set to stdout.
167 { char * tmpptr;
168 if(ferror(fcopy))
169 faterror("write failed on %s -- file system full?",resultfile);
170 fedit=fcopy;
171 rewind(fedit);
172 editline = 1; linecorr=0;
173 tmpptr=editfile; editfile=resultfile; resultfile=tmpptr;
174 if (tostdout)
175 fcopy=stdout;
176 elsif ((fcopy=fopen(resultfile,"w+"))==NULL) {
177 faterror("Can't open working file %s",resultfile);
182 finishedit(delta)
183 struct hshentry * delta;
184 /* copy the rest of the edit file and close it (if it exists).
185 * if delta!=nil, perform keyword substitution at the same time.
188 register int c;
189 if (fedit!=nil) {
190 if (delta!=nil) {
191 while (expandline(fedit,fcopy,delta,false,false)) editline++;
192 } else {
193 while((c=getc(fedit))!=EOF) {
194 VOID putc(c,fcopy);
195 if (c=='\n') editline++;
198 ffclose(fedit);
203 copylines(line,delta)
204 register int line; struct hshentry * delta;
205 /* Function: copies input lines editline..line-1 from fedit to fcopy.
206 * If delta != nil, keyword expansion is done simultaneously.
207 * editline is updated. Rewinds a file only if necessary.
211 if (editline>line) {
212 /* swap files */
213 finishedit((struct hshentry *)nil); swapeditfiles(false);
214 /* assumes edit only during last pass, from the beginning*/
216 while (editline<line) {
217 /*copy another line*/
218 if (delta)
219 VOID expandline(fedit,fcopy,delta,false,false);
220 else
221 while (putc(getc(fedit),fcopy)!='\n');
222 editline++;
228 xpandstring(delta)
229 struct hshentry * delta;
230 /* Function: Reads a string terminated by SDELIM from finptr and writes it
231 * to fcopy. Double SDELIM is replaced with single SDELIM.
232 * Keyword expansion is performed with data from delta.
233 * If rewriteflag==true, the string is also copied unchanged to frewrite.
234 * editline is updated.
237 editline=1;
238 while (expandline(finptr,fcopy,delta,true,rewriteflag)) editline++;
239 nextc='\n';
243 copystring()
244 /* Function: copies a string terminated with a single SDELIM from finptr to
245 * fcopy, replacing all double SDELIM with a single SDELIM.
246 * If rewriteflag==true, the string also copied unchanged to frewrite.
247 * editline is set to (number of lines copied)+1.
248 * Assumption: next character read is first string character.
250 { register c;
251 register FILE *fin, *frew, *fcop;
252 register write;
254 fin=finptr; frew=frewrite; fcop=fcopy;
255 write=rewriteflag;
256 editline=1;
257 while ((c=GETC(fin,frew,write)) != EOF) {
258 if ((c==SDELIM)&&((c=GETC(fin,frew,write)) != SDELIM)){
259 /* end of string */
260 nextc = c;
261 return;
263 VOID putc(c,fcop);
264 if (c=='\n') editline++;
266 nextc = c;
267 serror("Unterminated string");
268 return;
274 editstring(delta)
275 struct hshentry * delta;
276 /* Function: reads an edit script from finptr and applies it to
277 * file fedit; the result is written to fcopy.
278 * If delta!=nil, keyword expansion is performed simultaneously.
279 * If frewrite==true, the edit script is also copied verbatim to frewrite.
280 * Assumes that all these files are open.
281 * If running out of lines in fedit, fedit and fcopy are swapped.
282 * resultfile and editfile are the names of the files that go with fcopy
283 * and fedit, respectively.
284 * Assumes the next input character from finptr is the first character of
285 * the edit script. Resets nextc on exit.
288 int ed; /* editor command */
289 register int c;
290 register FILE *fin, *frew;
291 register int write, i;
292 int line, length;
294 fin=finptr; frew=frewrite;
295 editline += linecorr; linecorr=0; /*correct line number*/
296 write=rewriteflag;
297 for (;;) {
298 /* read next command and decode */
299 /* assume next non-white character is command name*/
300 while((ed=GETC(fin,frew,write))=='\n'||
301 ed==' ' || ed=='\t');
302 if (ed==SDELIM) break;
303 /* now attempt to read numbers. */
304 /* fscanf causes trouble because of the required echoing */
305 while ((c=GETC(fin,frew,write))==' '); /*skip spaces*/
306 if (!('0'<=c && c<='9')) {
307 faterror("missing line number in edit script");
308 break;
310 line= c -'0';
311 while ('0'<=(c=GETC(fin,frew,write)) && c<='9') {
312 line = line*10 + c-'0';
314 while (c==' ') c=GETC(fin,frew,write);
315 if (!('0'<=c && c<='9')) {
316 faterror("incorrect range in edit script");
317 break;
319 length= c -'0';
320 while ('0'<=(c=GETC(fin,frew,write)) && c<='9') {
321 length = length*10 + c-'0';
323 while(c!='\n'&&c!=EOF) c=GETC(fin,frew,write); /* skip to end of line */
325 switch (ed) {
326 case 'd':
327 copylines(line,delta);
328 /* skip over unwanted lines */
329 for (i=length;i>0;i--) {
330 /*skip next line*/
331 while ((c=getc(fedit))!='\n')
332 if (c==EOF)
333 faterror("EOF during edit");
334 editline++;
336 linecorr -= length;
337 break;
338 case 'a':
339 copylines(line+1,delta); /*copy only; no delete*/
340 for (i=length;i>0;i--) {
341 /*copy next line from script*/
342 if (delta!=nil)
343 VOID expandline(fin,fcopy,delta,true,write);
344 else {
345 c = GETC(fin,frew,write);
346 while (putc(c,fcopy)!='\n'){
347 if ((c==SDELIM)&&((c=GETC(fin,frew,write))!=SDELIM)){
348 serror("Missing string delimiter in edit script");
349 VOID putc(c,fcopy);
351 c = GETC(fin,frew,write);
355 linecorr += length;
356 break;
357 default:
358 faterror("unknown command in edit script: %c", ed);
359 break;
362 nextc=GETC(fin,frew,write);
367 /* The rest is for keyword expansion */
371 expandline(in, out, delta,delimstuffed,write)
372 register FILE * in, * out; struct hshentry * delta;
373 int delimstuffed, write;
374 /* Function: Reads a line from in and writes it to out.
375 * If delimstuffed==true, double SDELIM is replaced with single SDELIM.
376 * Keyword expansion is performed with data from delta.
377 * If write==true, the string is also copied unchanged to frewrite.
378 * Returns false if end-of-string or end-of-line is detected, true otherwise.
381 register c;
382 register FILE * frew;
383 register w, ds;
384 register char * tp;
385 char keystring[keylength+2];
386 char keyval[keyvallength+2];
387 enum markers matchresult;
389 frew = frewrite;
390 w = write;
391 ds = delimstuffed;
392 c=GETC(in,frew,w);
393 for (;;) {
394 if (c==EOF) {
395 if(ds) {
396 error("unterminated string");
397 nextc=c;
399 return(false);
402 if (c==SDELIM && ds) {
403 if ((c=GETC(in,frew,w))!=SDELIM) {
404 /* end of string */
405 nextc=c;
406 return false;
409 VOID putc(c,out);
411 if (c=='\n') return true; /* end of line */
413 if (c==KDELIM) {
414 /* check for keyword */
415 /* first, copy a long enough string into keystring */
416 tp=keystring;
417 while (((c=GETC(in,frew,w))!=EOF) && (tp<keystring+keylength) && (c!='\n')
418 && (c!=SDELIM) && (c!=KDELIM) && (c!=VDELIM)) {
419 VOID putc(c,out);
420 *tp++ = c;
422 *tp++ = c; *tp = '\0';
423 matchresult=trymatch(keystring,false);
424 if (matchresult==Nomatch) continue;
425 /* last c will be dealt with properly by continue*/
427 /* Now we have a keyword terminated with a K/VDELIM */
428 if (c==VDELIM) {
429 /* try to find closing KDELIM, and replace value */
430 tp=keyval;
431 while (((c=GETC(in,frew,w)) != EOF)
432 && (c!='\n') && (c!=KDELIM) && (tp<keyval+keyvallength)) {
433 *tp++ =c;
434 if (c==SDELIM && ds) { /*skip next SDELIM */
435 c=GETC(in,frew,w);
436 /* Can't be at end of string.
437 /* always a \n before closing SDELIM */
440 if (c!=KDELIM) {
441 /* couldn't find closing KDELIM -- give up */
442 VOID putc(VDELIM,out); *tp='\0';
443 VOID fputs(keyval,out);
444 continue; /* last c handled properly */
447 /* now put out the new keyword value */
448 keyreplace(matchresult,delta,out);
450 c=GETC(in,frew,w);
451 } /* end for */
456 keyreplace(marker,delta,out)
457 enum markers marker; struct hshentry * delta; register FILE * out;
458 /* function: ouputs the keyword value(s) corresponding to marker.
459 * Attributes are derived from delta.
462 char * date;
463 register char * sp;
465 date= delta->date;
467 switch (marker) {
468 case Author:
469 VOID fprintf(out,"%c %s %c",VDELIM,delta->author,KDELIM);
470 break;
471 case Date:
472 VOID putc(VDELIM,out);VOID putc(' ',out);
473 VOID PRINTDATE(out,date);VOID putc(' ',out);
474 VOID PRINTTIME(out,date);VOID putc(' ',out);VOID putc(KDELIM,out);
475 break;
476 case Id:
477 case Header:
478 VOID putc(VDELIM,out); VOID putc(' ',out);
479 if (marker==Id)
480 VOID fputs(bindex(RCSfilename,'/'),out);
481 else VOID fputs(getfullRCSname(),out);
482 VOID fprintf(out," %s ", delta->num);
483 VOID PRINTDATE(out,date);VOID putc(' ',out);VOID PRINTTIME(out,date);
484 VOID fprintf(out, " %s %s ",delta->author,delta->state);
485 if (delta->lockedby!=nil)
486 VOID fprintf(out,"Locker: %s ",delta->lockedby);
487 VOID putc(KDELIM,out);
488 break;
489 case Locker:
490 VOID fprintf(out,"%c %s %c", VDELIM,
491 delta->lockedby==nil?"":delta->lockedby,KDELIM);
492 break;
493 case Log:
494 VOID fprintf(out, "%c\t%s %c\n%sRevision %s ",
495 VDELIM, bindex(RCSfilename,'/'), KDELIM, Comment, delta->num);
496 VOID PRINTDATE(out,date);VOID fputs(" ",out);VOID PRINTTIME(out,date);
497 VOID fprintf(out, " %s\n%s",delta->author,Comment);
498 /* do not include state here because it may change and is not updated*/
499 sp = delta->log;
500 while (*sp) if (putc(*sp++,out)=='\n') VOID fputs(Comment,out);
501 /* Comment is the comment leader */
502 break;
503 case RCSfile:
504 VOID fprintf(out,"%c %s %c",VDELIM,bindex(RCSfilename,'/'),KDELIM);
505 break;
506 case Revision:
507 VOID fprintf(out,"%c %s %c",VDELIM,delta->num,KDELIM);
508 break;
509 case Source:
510 VOID fprintf(out,"%c %s %c",VDELIM,getfullRCSname(),KDELIM);
511 break;
512 case State:
513 VOID fprintf(out,"%c %s %c",VDELIM,delta->state,KDELIM);
514 break;
515 case Nomatch:
516 VOID putc(KDELIM,out);
517 break;