1: /* 2: * RCS revision generation 3: */ 4: #ifndef lint 5: static char rcsid[]= "$Id: rcsgen.c,v 3.6 88/04/24 17:32:22 bostic Exp $ Purdue CS"; 6: #endif 7: /********************************************************************************* 8: ********************************************************************************* 9: * 10: * Copyright (C) 1982 by Walter F. Tichy 11: * Purdue University 12: * Computer Science Department 13: * West Lafayette, IN 47907 14: * 15: * All rights reserved. No part of this software may be sold or distributed 16: * in any form or by any means without the prior written permission of the 17: * author. 18: * Report problems and direct all inquiries to Tichy@purdue (ARPA net). 19: */ 20: 21: 22: /* $Log: rcsgen.c,v $ 23: * Revision 3.6 88/04/24 17:32:22 bostic 24: * fix for ANSI C 25: * 26: * Revision 3.5 88/02/18 11:58:44 bostic 27: * replaced with version 4 28: * 29: * Revision 4.5 87/12/18 11:43:25 narten 30: * additional lint cleanups, and a bug fix from the 4.3BSD version that 31: * keeps "ci" from sticking a '\377' into the description if you run it 32: * with a zero-length file as the description. (Guy Harris) 33: * 34: * Revision 4.4 87/10/18 10:35:10 narten 35: * Updating version numbers. Changes relative to 1.1 actually relative to 36: * 4.2 37: * 38: * Revision 1.3 87/09/24 13:59:51 narten 39: * Sources now pass through lint (if you ignore printf/sprintf/fprintf 40: * warnings) 41: * 42: * Revision 1.2 87/03/27 14:22:27 jenkins 43: * Port to suns 44: * 45: * Revision 1.1 84/01/23 14:50:28 kcs 46: * Initial revision 47: * 48: * Revision 4.2 83/12/02 23:01:39 wft 49: * merged 4.1 and 3.3.1.1 (clearerr(stdin)). 50: * 51: * Revision 4.1 83/05/10 16:03:33 wft 52: * Changed putamin() to abort if trying to reread redirected stdin. 53: * Fixed getdesc() to output a prompt on initial newline. 54: * 55: * Revision 3.3.1.1 83/10/19 04:21:51 lepreau 56: * Added clearerr(stdin) for re-reading description from stdin. 57: * 58: * Revision 3.3 82/11/28 21:36:49 wft 59: * 4.2 prerelease 60: * 61: * Revision 3.3 82/11/28 21:36:49 wft 62: * Replaced ferror() followed by fclose() with ffclose(). 63: * Putdesc() now suppresses the prompts if stdin 64: * is not a terminal. A pointer to the current log message is now 65: * inserted into the corresponding delta, rather than leaving it in a 66: * global variable. 67: * 68: * Revision 3.2 82/10/18 21:11:26 wft 69: * I added checks for write errors during editing, and improved 70: * the prompt on putdesc(). 71: * 72: * Revision 3.1 82/10/13 15:55:09 wft 73: * corrected type of variables assigned to by getc (char --> int) 74: */ 75: 76: 77: 78: 79: #include "rcsbase.h" 80: 81: extern struct hshentry * getnum(); 82: extern char * mktemp(); 83: extern FILE * fopen(); 84: extern savestring(); 85: extern struct hshentry * genrevs(); 86: extern editstring(); 87: 88: extern int nextc; /* next character from lexical analyzer */ 89: extern char * RCSfilename, * workfilename; 90: extern struct hshentry * targetdelta; /* delta to be generated */ 91: extern char * Ktext; /* keywords from syntax analyzer */ 92: extern char * Klog; /* Keyword "log" */ 93: extern char * Kdesc; /* Keyword for description */ 94: extern FILE * finptr; /* RCS input file */ 95: extern FILE * frewrite; /* new RCS file */ 96: extern FILE * fcopy; /* result file during editing */ 97: extern FILE * fedit; /* edit file */ 98: extern char * resultfile, *editfile;/* file names for fcopy and fedit */ 99: extern int rewriteflag; /* indicates whether to rewrite the input file */ 100: 101: 102: char curlogmsg[logsize];/* buffer for current log message */ 103: 104: enum stringwork {copy, edit, expand, edit_expand }; 105: /* parameter to scandeltatext() */ 106: 107: 108: 109: 110: char * buildrevision(deltas, target, dir, expandflag) 111: struct hshentry ** deltas, * target; 112: char * dir; int expandflag; 113: /* Function: Generates the revision given by target 114: * by retrieving all deltas given by parameter deltas and combining them. 115: * If dir==nil, the revision is printed on the standard output, 116: * otherwise written into a temporary file in directory dir. 117: * if expandflag==true, keyword expansion is performed. 118: * returns false on errors, the name of the file with the revision otherwise. 119: * 120: * Algorithm: Copy inital revision unchanged. Then edit all revisions but 121: * the last one into it, alternating input and output files (resultfile and 122: * editfile). The last revision is then edited in, performing simultaneous 123: * keyword substitution (this saves one extra pass). 124: * All this simplifies if only one revision needs to be generated, 125: * or no keyword expansion is necessary, or if output goes to stdout. 126: */ 127: { 128: int i; 129: 130: if (deltas[0]==target) { 131: /* only latest revision to generate */ 132: if (dir==nil) {/* print directly to stdout */ 133: fcopy=stdout; 134: scandeltatext(target,expand); 135: return(char *) true; 136: } else { 137: initeditfiles(dir); 138: scandeltatext(target,expandflag?expand:copy); 139: ffclose(fcopy); 140: return(resultfile); 141: } 142: } else { 143: /* several revisions to generate */ 144: initeditfiles(dir?dir:"/tmp/"); 145: /* write initial revision into fcopy, no keyword expansion */ 146: scandeltatext(deltas[0],copy); 147: i = 1; 148: while (deltas[i+1] != nil) { 149: /* do all deltas except last one */ 150: scandeltatext(deltas[i++],edit); 151: } 152: if (!expandflag) { 153: /* no keyword expansion; only invoked from ci */ 154: scandeltatext(deltas[i],edit); 155: finishedit((struct hshentry *)nil); 156: ffclose(fcopy); 157: } else { 158: /* perform keyword expansion*/ 159: /* first, get to beginning of file*/ 160: finishedit((struct hshentry *)nil); swapeditfiles(dir==nil); 161: scandeltatext(deltas[i],edit_expand); 162: finishedit(deltas[i]); 163: if (dir!=nil) ffclose(fcopy); 164: } 165: return(resultfile); /*doesn't matter for dir==nil*/ 166: } 167: } 168: 169: 170: 171: scandeltatext(delta,func) 172: struct hshentry * delta; enum stringwork func; 173: /* Function: Scans delta text nodes up to and including the one given 174: * by delta. For the one given by delta, the log message is saved into 175: * curlogmsg and the text is processed according to parameter func. 176: * Assumes the initial lexeme must be read in first. 177: * Does not advance nexttok after it is finished. 178: */ 179: { struct hshentry * nextdelta; 180: 181: do { 182: nextlex(); 183: if (!(nextdelta=getnum())) { 184: fatserror("Can't find delta for revision %s", delta->num); 185: } 186: if (!getkey(Klog) || nexttok!=STRING) 187: serror("Missing log entry"); 188: elsif (delta==nextdelta) { 189: VOID savestring(curlogmsg,logsize); 190: delta->log=curlogmsg; 191: } else {readstring(); 192: delta->log= ""; 193: } 194: nextlex(); 195: if (!getkey(Ktext) || nexttok!=STRING) 196: fatserror("Missing delta text"); 197: 198: if(delta==nextdelta) 199: /* got the one we're looking for */ 200: switch (func) { 201: case copy: copystring(); 202: break; 203: case expand: xpandstring(delta); 204: break; 205: case edit: editstring((struct hshentry *)nil); 206: break; 207: case edit_expand: editstring(delta); 208: break; 209: } 210: else readstring(); /* skip over it */ 211: 212: } while (delta!=nextdelta); 213: } 214: 215: 216: int stdinread = 0; /* stdinread>0 if redirected stdin has been read once */ 217: 218: int putdesc(initflag,textflag,textfile,quietflag) 219: int initflag,textflag; char * textfile; int quietflag; 220: /* Function: puts the descriptive text into file frewrite. 221: * if !initflag && !textflag, the text is simply copied from finptr. 222: * Otherwise, if the textfile!=nil, the text is read from that 223: * file, or from stdin, if textfile==nil. 224: * Increments stdinread if text is read from redirected stdin. 225: * if initflag&&quietflag&&!textflag, an empty text is inserted. 226: * if !initflag, the old descriptive text is discarded. 227: * Returns true is successful, false otherwise. 228: */ 229: { FILE * txt; register int c, old1, old2; 230: #ifdef lint 231: if (quietflag == 0) initflag = quietflag; /* silencelint */ 232: #endif 233: if (!initflag && !textflag) { 234: /* copy old description */ 235: VOID fprintf(frewrite,"\n\n%s%c",Kdesc,nextc); 236: rewriteflag=true; getdesc(false); 237: return true; 238: } else { 239: /* get new description */ 240: if (!initflag) { 241: /*skip old description*/ 242: rewriteflag=false; getdesc(false); 243: } 244: VOID fprintf(frewrite,"\n\n%s\n%c",Kdesc,SDELIM); 245: if (textfile) { 246: old1='\n'; 247: /* copy textfile */ 248: if ((txt=fopen(textfile,"r"))!=NULL) { 249: while ((c=getc(txt))!=EOF) { 250: if (c==SDELIM) VOID putc(c,frewrite); /*double up*/ 251: VOID putc(c,frewrite); 252: old1=c; 253: } 254: if (old1!='\n') VOID putc('\n',frewrite); 255: VOID fclose(txt); 256: VOID putc(SDELIM,frewrite); 257: VOID fputs("\n\n", frewrite); 258: return true; 259: } else { 260: error("Can't open file %s with description",textfile); 261: if (!isatty(fileno(stdin))) return false; 262: /* otherwise, get description from terminal */ 263: } 264: } 265: /* read text from stdin */ 266: if (isatty(fileno(stdin))) { 267: VOID fputs("enter description, terminated with ^D or '.':\n",stderr); 268: VOID fputs("NOTE: This is NOT the log message!\n>> ",stderr); 269: if (feof(stdin)) 270: clearerr(stdin); 271: } else { /* redirected stdin */ 272: if (stdinread>0) 273: faterror("Can't reread redirected stdin for description; use -t<file>"); 274: stdinread++; 275: } 276: c = '\0'; old2= '\n'; 277: if ((old1=getchar())==EOF) { 278: if (isatty(fileno(stdin))) { 279: VOID putc('\n',stderr); 280: clearerr(stdin); 281: } 282: } else { 283: if (old1=='\n' && isatty(fileno(stdin))) 284: VOID fputs(">> ",stderr); 285: for (;;) { 286: c=getchar(); 287: if (c==EOF) { 288: if (isatty(fileno(stdin))) { 289: VOID putc('\n',stderr); 290: clearerr(stdin); 291: } 292: VOID putc(old1,frewrite); 293: if (old1!='\n') VOID putc('\n',frewrite); 294: break; 295: } 296: if (c=='\n' && old1=='.' && old2=='\n') { 297: break; 298: } 299: if (c=='\n' && isatty(fileno(stdin))) VOID fputs(">> ",stderr); 300: if(old1==SDELIM) VOID putc(old1,frewrite); /* double up*/ 301: VOID putc(old1,frewrite); 302: old2=old1; 303: old1=c; 304: } /* end for */ 305: } 306: VOID putc(SDELIM,frewrite);VOID fputs("\n\n",frewrite); 307: return true; 308: } 309: }