1 /* $NetBSD: rcsgen.c,v 1.1.1.2 1996/10/13 21:56:59 veego Exp $ */
3 /* Generate RCS revisions. */
5 /* Copyright 1982, 1988, 1989 Walter Tichy
6 Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
7 Distributed under license by the Free Software Foundation, Inc.
9 This file is part of RCS.
11 RCS is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2, or (at your option)
16 RCS is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with RCS; see the file COPYING.
23 If not, write to the Free Software Foundation,
24 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 Report problems and direct all questions to:
28 rcs-bugs@cs.purdue.edu
34 * Revision 5.16 1995/06/16 06:19:24 eggert
37 * Revision 5.15 1995/06/01 16:23:43 eggert
38 * (putadmin): Open RCS file with FOPEN_WB.
40 * Revision 5.14 1994/03/17 14:05:48 eggert
41 * Work around SVR4 stdio performance bug.
42 * Flush stderr after prompt. Remove lint.
44 * Revision 5.13 1993/11/03 17:42:27 eggert
45 * Don't discard ignored phrases. Improve quality of diagnostics.
47 * Revision 5.12 1992/07/28 16:12:44 eggert
48 * Statement macro names now end in _.
49 * Be consistent about pathnames vs filenames.
51 * Revision 5.11 1992/01/24 18:44:19 eggert
52 * Move put routines here from rcssyn.c.
53 * Add support for bad_creat0.
55 * Revision 5.10 1991/10/07 17:32:46 eggert
56 * Fix log bugs, e.g. ci -t/dev/null when has_mmap.
58 * Revision 5.9 1991/09/10 22:15:46 eggert
59 * Fix test for redirected stdin.
61 * Revision 5.8 1991/08/19 03:13:55 eggert
62 * Add piece tables. Tune.
64 * Revision 5.7 1991/04/21 11:58:24 eggert
67 * Revision 5.6 1990/12/27 19:54:26 eggert
68 * Fix bug: rcs -t inserted \n, making RCS file grow.
70 * Revision 5.5 1990/12/04 05:18:45 eggert
71 * Use -I for prompts and -q for diagnostics.
73 * Revision 5.4 1990/11/01 05:03:47 eggert
74 * Add -I and new -t behavior. Permit arbitrary data in logs.
76 * Revision 5.3 1990/09/21 06:12:43 hammer
77 * made putdesc() treat stdin the same whether or not it was from a terminal
78 * by making it recognize that a single '.' was then end of the
81 * Revision 5.2 1990/09/04 08:02:25 eggert
82 * Fix `co -p1.1 -ko' bug. Standardize yes-or-no procedure.
84 * Revision 5.1 1990/08/29 07:14:01 eggert
85 * Clean old log messages too.
87 * Revision 5.0 1990/08/22 08:12:52 eggert
88 * Remove compile-time limits; use malloc instead.
89 * Ansify and Posixate.
91 * Revision 4.7 89/05/01 15:12:49 narten
92 * changed copyright header to reflect current distribution rules
94 * Revision 4.6 88/08/28 14:59:10 eggert
95 * Shrink stdio code size; allow cc -R; remove lint; isatty() -> ttystdin()
97 * Revision 4.5 87/12/18 11:43:25 narten
98 * additional lint cleanups, and a bug fix from the 4.3BSD version that
99 * keeps "ci" from sticking a '\377' into the description if you run it
100 * with a zero-length file as the description. (Guy Harris)
102 * Revision 4.4 87/10/18 10:35:10 narten
103 * Updating version numbers. Changes relative to 1.1 actually relative to
106 * Revision 1.3 87/09/24 13:59:51 narten
107 * Sources now pass through lint (if you ignore printf/sprintf/fprintf
110 * Revision 1.2 87/03/27 14:22:27 jenkins
113 * Revision 4.2 83/12/02 23:01:39 wft
114 * merged 4.1 and 3.3.1.1 (clearerr(stdin)).
116 * Revision 4.1 83/05/10 16:03:33 wft
117 * Changed putamin() to abort if trying to reread redirected stdin.
118 * Fixed getdesc() to output a prompt on initial newline.
120 * Revision 3.3.1.1 83/10/19 04:21:51 lepreau
121 * Added clearerr(stdin) for re-reading description from stdin.
123 * Revision 3.3 82/11/28 21:36:49 wft
126 * Revision 3.3 82/11/28 21:36:49 wft
127 * Replaced ferror() followed by fclose() with ffclose().
128 * Putdesc() now suppresses the prompts if stdin
129 * is not a terminal. A pointer to the current log message is now
130 * inserted into the corresponding delta, rather than leaving it in a
133 * Revision 3.2 82/10/18 21:11:26 wft
134 * I added checks for write errors during editing, and improved
135 * the prompt on putdesc().
137 * Revision 3.1 82/10/13 15:55:09 wft
138 * corrected type of variables assigned to by getc (char --> int)
146 libId(genId
, "Id: rcsgen.c,v 5.16 1995/06/16 06:19:24 eggert Exp")
148 int interactiveflag
; /* Should we act as if stdin is a tty? */
149 struct buf curlogbuf
; /* buffer for current log message */
151 enum stringwork
{ enter
, copy
, edit
, expand
, edit_expand
};
153 static void putdelta
P((struct hshentry
const*,FILE*));
154 static void scandeltatext
P((struct hshentry
*,enum stringwork
,int));
160 buildrevision(deltas
, target
, outfile
, expandflag
)
161 struct hshentries
const *deltas
;
162 struct hshentry
*target
;
165 /* Function: Generates the revision given by target
166 * by retrieving all deltas given by parameter deltas and combining them.
167 * If outfile is set, the revision is output to it,
168 * otherwise written into a temporary file.
169 * Temporary files are allocated by maketemp().
170 * if expandflag is set, keyword expansion is performed.
171 * Return 0 if outfile is set, the name of the temporary file otherwise.
173 * Algorithm: Copy initial revision unchanged. Then edit all revisions but
174 * the last one into it, alternating input and output files (resultname and
175 * editname). The last revision is then edited in, performing simultaneous
176 * keyword substitution (this saves one extra pass).
177 * All this simplifies if only one revision needs to be generated,
178 * or no keyword expansion is necessary, or if output goes to stdout.
181 if (deltas
->first
== target
) {
182 /* only latest revision to generate */
184 scandeltatext(target
, expandflag
?expand
:copy
, true);
192 /* several revisions to generate */
193 /* Get initial revision without keyword expansion. */
194 scandeltatext(deltas
->first
, enter
, false);
195 while ((deltas
=deltas
->rest
)->rest
) {
196 /* do all deltas except last one */
197 scandeltatext(deltas
->first
, edit
, false);
199 if (expandflag
|| outfile
) {
200 /* first, get to beginning of file*/
201 finishedit((struct hshentry
*)0, outfile
, false);
203 scandeltatext(target
, expandflag
?edit_expand
:edit
, true);
205 expandflag
? target
: (struct hshentry
*)0,
218 scandeltatext(delta
, func
, needlog
)
219 struct hshentry
*delta
;
220 enum stringwork func
;
222 /* Function: Scans delta text nodes up to and including the one given
223 * by delta. For the one given by delta, the log message is saved into
224 * delta->log if needlog is set; func specifies how to handle the text.
225 * Similarly, if needlog, delta->igtext is set to the ignored phrases.
226 * Assumes the initial lexeme must be read in first.
227 * Does not advance nexttok after it is finished.
230 struct hshentry
const *nextdelta
;
235 fatserror("can't find delta for revision %s", delta
->num
);
237 if (!(nextdelta
=getnum())) {
238 fatserror("delta number corrupted");
241 if (needlog
&& delta
==nextdelta
) {
242 cb
= savestring(&curlogbuf
);
243 delta
->log
= cleanlogmsg(curlogbuf
.string
, cb
.size
);
245 delta
->igtext
= getphrases(Ktext
);
246 } else {readstring();
247 ignorephrases(Ktext
);
251 if (delta
==nextdelta
)
253 readstring(); /* skip over it */
257 case enter
: enterstring(); break;
258 case copy
: copystring(); break;
259 case expand
: xpandstring(delta
); break;
260 case edit
: editstring((struct hshentry
*)0); break;
261 case edit_expand
: editstring(delta
); break;
270 register char *t
= m
;
271 register char const *f
= t
;
275 if ((*t
++ = *f
++) == '\n')
277 if (t
[-1]!=' ' && t
[-1]!='\t') {
282 while (m
< t
&& (t
[-1]==' ' || t
[-1]=='\t' || t
[-1]=='\n'))
292 static int initialized
;
294 if (!interactiveflag
)
295 interactiveflag
= isatty(STDIN_FILENO
);
298 return interactiveflag
;
308 if (feof(in
) && ttystdin())
313 if (feof(in
) && ttystdin())
321 yesorno(int default_answer
, char const *question
, ...)
324 yesorno(default_answer
, question
, va_alist
)
325 int default_answer
; char const *question
; va_dcl
330 if (!quietflag
&& ttystdin()) {
332 vararg_start(args
, question
);
333 fvfprintf(stderr
, question
, args
);
337 while (c
!='\n' && !feof(stdin
))
339 if (r
=='y' || r
=='Y')
341 if (r
=='n' || r
=='N')
344 return default_answer
;
349 putdesc(textflag
, textfile
)
352 /* Function: puts the descriptive text into file frewrite.
353 * if finptr && !textflag, the text is copied from the old description.
354 * Otherwise, if textfile, the text is read from that
355 * file, or from stdin, if !textfile.
356 * A textfile with a leading '-' is treated as a string, not a pathname.
357 * If finptr, the old descriptive text is discarded.
358 * Always clears foutptr.
361 static struct buf desc
;
362 static struct cbuf desclean
;
366 register FILE * frew
;
372 if (finptr
&& !textflag
) {
373 /* copy old description */
374 aprintf(frew
, "\n\n%s%c", Kdesc
, nextc
);
380 /* get new description */
382 /*skip old description*/
385 aprintf(frew
,"\n\n%s\n%c",Kdesc
,SDELIM
);
387 desclean
= getsstdin(
389 "NOTE: This is NOT the log message!\n", &desc
391 else if (!desclean
.string
) {
392 if (*textfile
== '-') {
396 if (!(txt
= fopenSafer(textfile
, "r")))
400 plim
= p
+ desc
.size
;
402 if ((c
=getc(txt
)) == EOF
) {
408 p
= bufenlarge(&desc
, &plim
);
411 if (fclose(txt
) != 0)
416 desclean
= cleanlogmsg(p
, s
);
418 putstring(frew
, false, desclean
, true);
424 getsstdin(option
, name
, note
, buf
)
425 char const *option
, *name
, *note
;
431 register int tty
= ttystdin();
435 "enter %s, terminated with single '.' or end of file:\n%s>> ",
439 } else if (feof(stdin
))
440 rcsfaterror("can't reread redirected stdin for %s; use -%s<%s>",
446 c
= getcstdin(), !feof(stdin
);
447 bufrealloc(buf
, i
+1), p
= buf
->string
, p
[i
++] = c
450 if (i
&& p
[i
-1]=='.' && (i
==1 || p
[i
-2]=='\n')) {
451 /* Remove trailing '.'. */
455 aputs(">> ", stderr
);
458 return cleanlogmsg(p
, i
);
464 /* Output the admin node. */
467 struct assoc
const *curassoc
;
468 struct rcslock
const *curlock
;
469 struct access
const *curaccess
;
471 if (!(fout
= frewrite
)) {
474 fout
= fopenSafer(makedirtemp(0), FOPEN_WB
);
478 fout
= fdopen(fo
, FOPEN_WB
);
481 if (!(frewrite
= fout
))
486 * Output the first character with putc, not printf.
487 * Otherwise, an SVR4 stdio bug buffers output inefficiently.
490 aprintf(fout
, "%s\t%s;\n", Khead
+ 1, Head
?Head
->num
:"");
491 if (Dbranch
&& VERSION(4)<=RCSversion
)
492 aprintf(fout
, "%s\t%s;\n", Kbranch
, Dbranch
);
494 aputs(Kaccess
, fout
);
495 curaccess
= AccessList
;
497 aprintf(fout
, "\n\t%s", curaccess
->login
);
498 curaccess
= curaccess
->nextaccess
;
500 aprintf(fout
, ";\n%s", Ksymbols
);
503 aprintf(fout
, "\n\t%s:%s", curassoc
->symbol
, curassoc
->num
);
504 curassoc
= curassoc
->nextassoc
;
506 aprintf(fout
, ";\n%s", Klocks
);
509 aprintf(fout
, "\n\t%s:%s", curlock
->login
, curlock
->delta
->num
);
510 curlock
= curlock
->nextlock
;
512 if (StrictLocks
) aprintf(fout
, "; %s", Kstrict
);
513 aprintf(fout
, ";\n");
515 aprintf(fout
, "%s\t", Kcomment
);
516 putstring(fout
, true, Comment
, false);
517 aprintf(fout
, ";\n");
519 if (Expand
!= KEYVAL_EXPAND
)
520 aprintf(fout
, "%s\t%c%s%c;\n",
521 Kexpand
, SDELIM
, expand_names
[Expand
], SDELIM
523 awrite(Ignored
.string
, Ignored
.size
, fout
);
530 register struct hshentry
const *node
;
531 register FILE * fout
;
532 /* Output the delta NODE to FOUT. */
534 struct branchhead
const *nextbranch
;
538 aprintf(fout
, "\n%s\n%s\t%s;\t%s %s;\t%s %s;\nbranches",
541 Kauthor
, node
->author
,
542 Kstate
, node
->state
?node
->state
:""
544 nextbranch
= node
->branches
;
546 aprintf(fout
, "\n\t%s", nextbranch
->hsh
->num
);
547 nextbranch
= nextbranch
->nextbranch
;
550 aprintf(fout
, ";\n%s\t%s;\n", Knext
, node
->next
?node
->next
->num
:"");
551 awrite(node
->ig
.string
, node
->ig
.size
, fout
);
557 struct hshentry
const *root
;
559 /* Output the delta tree with base ROOT in preorder to FOUT. */
561 struct branchhead
const *nextbranch
;
566 putdelta(root
, fout
);
568 puttree(root
->next
, fout
);
570 nextbranch
= root
->branches
;
572 puttree(nextbranch
->hsh
, fout
);
573 nextbranch
= nextbranch
->nextbranch
;
579 putdtext(delta
, srcname
, fout
, diffmt
)
580 struct hshentry
const *delta
;
585 * Output a deltatext node with delta number DELTA->num, log message DELTA->log,
586 * ignored phrases DELTA->igtext and text SRCNAME to FOUT.
587 * Double up all SDELIMs in both the log and the text.
588 * Make sure the log message ends in \n.
589 * Return false on error.
590 * If DIFFMT, also check that the text is valid diff -n output.
594 if (!(fin
= Iopen(srcname
, "r", (struct stat
*)0))) {
598 putdftext(delta
, fin
, fout
, diffmt
);
604 putstring(out
, delim
, s
, log
)
609 * Output to OUT one SDELIM if DELIM, then the string S with SDELIMs doubled.
610 * If LOG is set then S is a log string; append a newline if S is nonempty.
613 register char const *sp
;
619 for (ss
= s
.size
; ss
; --ss
) {
630 putdftext(delta
, finfile
, foutfile
, diffmt
)
631 struct hshentry
const *delta
;
635 /* like putdtext(), except the source file is already open */
645 aprintf(fout
, DELNUMFORM
, delta
->num
, Klog
);
648 putstring(fout
, true, delta
->log
, true);
651 /* put ignored phrases */
652 awrite(delta
->igtext
.string
, delta
->igtext
.size
, fout
);
655 aprintf(fout
, "%s\n%c", Ktext
, SDELIM
);
663 cachegeteof_(c
, break;)
664 if (c
==SDELIM
) aputc_(SDELIM
, fout
) /*double up SDELIM*/
669 while (0 <= (ed
= getdiffcmd(fin
, false, fout
, &dc
)))
674 cachegeteof_(c
, { if (!dc
.nlines
) goto OK_EOF
; unexpected_EOF(); })
683 aprintf(fout
, "%c\n", SDELIM
);