rcsgen.c revision 8858
1/* 2 * RCS revision generation 3 */ 4 5/* Copyright (C) 1982, 1988, 1989 Walter Tichy 6 Copyright 1990, 1991 by Paul Eggert 7 Distributed under license by the Free Software Foundation, Inc. 8 9This file is part of RCS. 10 11RCS is free software; you can redistribute it and/or modify 12it under the terms of the GNU General Public License as published by 13the Free Software Foundation; either version 2, or (at your option) 14any later version. 15 16RCS is distributed in the hope that it will be useful, 17but WITHOUT ANY WARRANTY; without even the implied warranty of 18MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19GNU General Public License for more details. 20 21You should have received a copy of the GNU General Public License 22along with RCS; see the file COPYING. If not, write to 23the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 24 25Report problems and direct all questions to: 26 27 rcs-bugs@cs.purdue.edu 28 29*/ 30 31 32 33/* $Log: rcsgen.c,v $ 34 * Revision 1.1.1.1 1993/06/18 04:22:12 jkh 35 * Updated GNU utilities 36 * 37 * Revision 5.10 1991/10/07 17:32:46 eggert 38 * Fix log bugs, e.g. ci -t/dev/null when has_mmap. 39 * 40 * Revision 5.9 1991/09/10 22:15:46 eggert 41 * Fix test for redirected stdin. 42 * 43 * Revision 5.8 1991/08/19 03:13:55 eggert 44 * Add piece tables. Tune. 45 * 46 * Revision 5.7 1991/04/21 11:58:24 eggert 47 * Add MS-DOS support. 48 * 49 * Revision 5.6 1990/12/27 19:54:26 eggert 50 * Fix bug: rcs -t inserted \n, making RCS file grow. 51 * 52 * Revision 5.5 1990/12/04 05:18:45 eggert 53 * Use -I for prompts and -q for diagnostics. 54 * 55 * Revision 5.4 1990/11/01 05:03:47 eggert 56 * Add -I and new -t behavior. Permit arbitrary data in logs. 57 * 58 * Revision 5.3 1990/09/21 06:12:43 hammer 59 * made putdesc() treat stdin the same whether or not it was from a terminal 60 * by making it recognize that a single '.' was then end of the 61 * description always 62 * 63 * Revision 5.2 1990/09/04 08:02:25 eggert 64 * Fix `co -p1.1 -ko' bug. Standardize yes-or-no procedure. 65 * 66 * Revision 5.1 1990/08/29 07:14:01 eggert 67 * Clean old log messages too. 68 * 69 * Revision 5.0 1990/08/22 08:12:52 eggert 70 * Remove compile-time limits; use malloc instead. 71 * Ansify and Posixate. 72 * 73 * Revision 4.7 89/05/01 15:12:49 narten 74 * changed copyright header to reflect current distribution rules 75 * 76 * Revision 4.6 88/08/28 14:59:10 eggert 77 * Shrink stdio code size; allow cc -R; remove lint; isatty() -> ttystdin() 78 * 79 * Revision 4.5 87/12/18 11:43:25 narten 80 * additional lint cleanups, and a bug fix from the 4.3BSD version that 81 * keeps "ci" from sticking a '\377' into the description if you run it 82 * with a zero-length file as the description. (Guy Harris) 83 * 84 * Revision 4.4 87/10/18 10:35:10 narten 85 * Updating version numbers. Changes relative to 1.1 actually relative to 86 * 4.2 87 * 88 * Revision 1.3 87/09/24 13:59:51 narten 89 * Sources now pass through lint (if you ignore printf/sprintf/fprintf 90 * warnings) 91 * 92 * Revision 1.2 87/03/27 14:22:27 jenkins 93 * Port to suns 94 * 95 * Revision 4.2 83/12/02 23:01:39 wft 96 * merged 4.1 and 3.3.1.1 (clearerr(stdin)). 97 * 98 * Revision 4.1 83/05/10 16:03:33 wft 99 * Changed putamin() to abort if trying to reread redirected stdin. 100 * Fixed getdesc() to output a prompt on initial newline. 101 * 102 * Revision 3.3.1.1 83/10/19 04:21:51 lepreau 103 * Added clearerr(stdin) for re-reading description from stdin. 104 * 105 * Revision 3.3 82/11/28 21:36:49 wft 106 * 4.2 prerelease 107 * 108 * Revision 3.3 82/11/28 21:36:49 wft 109 * Replaced ferror() followed by fclose() with ffclose(). 110 * Putdesc() now suppresses the prompts if stdin 111 * is not a terminal. A pointer to the current log message is now 112 * inserted into the corresponding delta, rather than leaving it in a 113 * global variable. 114 * 115 * Revision 3.2 82/10/18 21:11:26 wft 116 * I added checks for write errors during editing, and improved 117 * the prompt on putdesc(). 118 * 119 * Revision 3.1 82/10/13 15:55:09 wft 120 * corrected type of variables assigned to by getc (char --> int) 121 */ 122 123 124 125 126#include "rcsbase.h" 127 128libId(genId, "$Id: rcsgen.c,v 1.1.1.1 1993/06/18 04:22:12 jkh Exp $") 129 130int interactiveflag; /* Should we act as if stdin is a tty? */ 131struct buf curlogbuf; /* buffer for current log message */ 132 133enum stringwork { enter, copy, edit, expand, edit_expand }; 134static void scandeltatext P((struct hshentry*,enum stringwork,int)); 135 136 137 138 139 char const * 140buildrevision(deltas, target, outfile, expandflag) 141 struct hshentries const *deltas; 142 struct hshentry *target; 143 FILE *outfile; 144 int expandflag; 145/* Function: Generates the revision given by target 146 * by retrieving all deltas given by parameter deltas and combining them. 147 * If outfile is set, the revision is output to it, 148 * otherwise written into a temporary file. 149 * Temporary files are allocated by maketemp(). 150 * if expandflag is set, keyword expansion is performed. 151 * Return nil if outfile is set, the name of the temporary file otherwise. 152 * 153 * Algorithm: Copy initial revision unchanged. Then edit all revisions but 154 * the last one into it, alternating input and output files (resultfile and 155 * editfile). The last revision is then edited in, performing simultaneous 156 * keyword substitution (this saves one extra pass). 157 * All this simplifies if only one revision needs to be generated, 158 * or no keyword expansion is necessary, or if output goes to stdout. 159 */ 160{ 161 if (deltas->first == target) { 162 /* only latest revision to generate */ 163 openfcopy(outfile); 164 scandeltatext(target, expandflag?expand:copy, true); 165 if (outfile) 166 return 0; 167 else { 168 Ozclose(&fcopy); 169 return(resultfile); 170 } 171 } else { 172 /* several revisions to generate */ 173 /* Get initial revision without keyword expansion. */ 174 scandeltatext(deltas->first, enter, false); 175 while ((deltas=deltas->rest)->rest) { 176 /* do all deltas except last one */ 177 scandeltatext(deltas->first, edit, false); 178 } 179 if (expandflag || outfile) { 180 /* first, get to beginning of file*/ 181 finishedit((struct hshentry *)nil, outfile, false); 182 } 183 scandeltatext(deltas->first, expandflag?edit_expand:edit, true); 184 finishedit( 185 expandflag ? deltas->first : (struct hshentry*)nil, 186 outfile, true 187 ); 188 if (outfile) 189 return 0; 190 Ozclose(&fcopy); 191 return resultfile; 192 } 193} 194 195 196 197 static void 198scandeltatext(delta, func, needlog) 199 struct hshentry * delta; 200 enum stringwork func; 201 int needlog; 202/* Function: Scans delta text nodes up to and including the one given 203 * by delta. For the one given by delta, the log message is saved into 204 * delta->log if needlog is set; func specifies how to handle the text. 205 * Assumes the initial lexeme must be read in first. 206 * Does not advance nexttok after it is finished. 207 */ 208{ 209 struct hshentry const *nextdelta; 210 struct cbuf cb; 211 212 for (;;) { 213 if (eoflex()) 214 fatserror("can't find delta for revision %s", delta->num); 215 nextlex(); 216 if (!(nextdelta=getnum())) { 217 fatserror("delta number corrupted"); 218 } 219 getkeystring(Klog); 220 if (needlog && delta==nextdelta) { 221 cb = savestring(&curlogbuf); 222 delta->log = cleanlogmsg(curlogbuf.string, cb.size); 223 } else {readstring(); 224 } 225 nextlex(); 226 while (nexttok==ID && strcmp(NextString,Ktext)!=0) 227 ignorephrase(); 228 getkeystring(Ktext); 229 230 if (delta==nextdelta) 231 break; 232 readstring(); /* skip over it */ 233 234 } 235 switch (func) { 236 case enter: enterstring(); break; 237 case copy: copystring(); break; 238 case expand: xpandstring(delta); break; 239 case edit: editstring((struct hshentry *)nil); break; 240 case edit_expand: editstring(delta); break; 241 } 242} 243 244 struct cbuf 245cleanlogmsg(m, s) 246 char *m; 247 size_t s; 248{ 249 register char *t = m; 250 register char const *f = t; 251 struct cbuf r; 252 while (s) { 253 --s; 254 if ((*t++ = *f++) == '\n') 255 while (m < --t) 256 if (t[-1]!=' ' && t[-1]!='\t') { 257 *t++ = '\n'; 258 break; 259 } 260 } 261 while (m < t && (t[-1]==' ' || t[-1]=='\t' || t[-1]=='\n')) 262 --t; 263 r.string = m; 264 r.size = t - m; 265 return r; 266} 267 268 269int ttystdin() 270{ 271 static int initialized; 272 if (!initialized) { 273 if (!interactiveflag) 274 interactiveflag = isatty(STDIN_FILENO); 275 initialized = true; 276 } 277 return interactiveflag; 278} 279 280 int 281getcstdin() 282{ 283 register FILE *in; 284 register int c; 285 286 in = stdin; 287 if (feof(in) && ttystdin()) 288 clearerr(in); 289 c = getc(in); 290 if (c < 0) { 291 testIerror(in); 292 if (feof(in) && ttystdin()) 293 afputc('\n',stderr); 294 } 295 return c; 296} 297 298#if has_prototypes 299 int 300yesorno(int default_answer, char const *question, ...) 301#else 302 /*VARARGS2*/ int 303 yesorno(default_answer, question, va_alist) 304 int default_answer; char const *question; va_dcl 305#endif 306{ 307 va_list args; 308 register int c, r; 309 if (!quietflag && ttystdin()) { 310 oflush(); 311 vararg_start(args, question); 312 fvfprintf(stderr, question, args); 313 va_end(args); 314 eflush(); 315 r = c = getcstdin(); 316 while (c!='\n' && !feof(stdin)) 317 c = getcstdin(); 318 if (r=='y' || r=='Y') 319 return true; 320 if (r=='n' || r=='N') 321 return false; 322 } 323 return default_answer; 324} 325 326 327 void 328putdesc(textflag, textfile) 329 int textflag; 330 char *textfile; 331/* Function: puts the descriptive text into file frewrite. 332 * if finptr && !textflag, the text is copied from the old description. 333 * Otherwise, if the textfile!=nil, the text is read from that 334 * file, or from stdin, if textfile==nil. 335 * A textfile with a leading '-' is treated as a string, not a file name. 336 * If finptr, the old descriptive text is discarded. 337 * Always clears foutptr. 338 */ 339{ 340 static struct buf desc; 341 static struct cbuf desclean; 342 343 register FILE *txt; 344 register int c; 345 register FILE * frew; 346 register char *p; 347 register size_t s; 348 char const *plim; 349 350 frew = frewrite; 351 if (finptr && !textflag) { 352 /* copy old description */ 353 aprintf(frew, "\n\n%s%c", Kdesc, nextc); 354 foutptr = frewrite; 355 getdesc(false); 356 foutptr = 0; 357 } else { 358 foutptr = 0; 359 /* get new description */ 360 if (finptr) { 361 /*skip old description*/ 362 getdesc(false); 363 } 364 aprintf(frew,"\n\n%s\n%c",Kdesc,SDELIM); 365 if (!textfile) 366 desclean = getsstdin( 367 "t-", "description", 368 "NOTE: This is NOT the log message!\n", &desc 369 ); 370 else if (!desclean.string) { 371 if (*textfile == '-') { 372 p = textfile + 1; 373 s = strlen(p); 374 } else { 375 if (!(txt = fopen(textfile, "r"))) 376 efaterror(textfile); 377 bufalloc(&desc, 1); 378 p = desc.string; 379 plim = p + desc.size; 380 for (;;) { 381 if ((c=getc(txt)) < 0) { 382 testIerror(txt); 383 if (feof(txt)) 384 break; 385 } 386 if (plim <= p) 387 p = bufenlarge(&desc, &plim); 388 *p++ = c; 389 } 390 if (fclose(txt) != 0) 391 Ierror(); 392 s = p - desc.string; 393 p = desc.string; 394 } 395 desclean = cleanlogmsg(p, s); 396 } 397 putstring(frew, false, desclean, true); 398 aputc('\n', frew); 399 } 400} 401 402 struct cbuf 403getsstdin(option, name, note, buf) 404 char const *option, *name, *note; 405 struct buf *buf; 406{ 407 register int c; 408 register char *p; 409 register size_t i; 410 register int tty = ttystdin(); 411 412 if (tty) 413 aprintf(stderr, 414 "enter %s, terminated with single '.' or end of file:\n%s>> ", 415 name, note 416 ); 417 else if (feof(stdin)) 418 faterror("can't reread redirected stdin for %s; use -%s<%s>", 419 name, option, name 420 ); 421 422 for ( 423 i = 0, p = 0; 424 c = getcstdin(), !feof(stdin); 425 bufrealloc(buf, i+1), p = buf->string, p[i++] = c 426 ) 427 if (c == '\n') 428 if (i && p[i-1]=='.' && (i==1 || p[i-2]=='\n')) { 429 /* Remove trailing '.'. */ 430 --i; 431 break; 432 } else if (tty) 433 aputs(">> ", stderr); 434 return cleanlogmsg(p, i); 435} 436