xref: /dflybsd-src/gnu/usr.bin/rcs/lib/rcsgen.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
1*86d7f5d3SJohn Marino /* Generate RCS revisions.  */
2*86d7f5d3SJohn Marino 
3*86d7f5d3SJohn Marino /* Copyright 1982, 1988, 1989 Walter Tichy
4*86d7f5d3SJohn Marino    Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
5*86d7f5d3SJohn Marino    Distributed under license by the Free Software Foundation, Inc.
6*86d7f5d3SJohn Marino 
7*86d7f5d3SJohn Marino This file is part of RCS.
8*86d7f5d3SJohn Marino 
9*86d7f5d3SJohn Marino RCS is free software; you can redistribute it and/or modify
10*86d7f5d3SJohn Marino it under the terms of the GNU General Public License as published by
11*86d7f5d3SJohn Marino the Free Software Foundation; either version 2, or (at your option)
12*86d7f5d3SJohn Marino any later version.
13*86d7f5d3SJohn Marino 
14*86d7f5d3SJohn Marino RCS is distributed in the hope that it will be useful,
15*86d7f5d3SJohn Marino but WITHOUT ANY WARRANTY; without even the implied warranty of
16*86d7f5d3SJohn Marino MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17*86d7f5d3SJohn Marino GNU General Public License for more details.
18*86d7f5d3SJohn Marino 
19*86d7f5d3SJohn Marino You should have received a copy of the GNU General Public License
20*86d7f5d3SJohn Marino along with RCS; see the file COPYING.
21*86d7f5d3SJohn Marino If not, write to the Free Software Foundation,
22*86d7f5d3SJohn Marino 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23*86d7f5d3SJohn Marino 
24*86d7f5d3SJohn Marino Report problems and direct all questions to:
25*86d7f5d3SJohn Marino 
26*86d7f5d3SJohn Marino     rcs-bugs@cs.purdue.edu
27*86d7f5d3SJohn Marino 
28*86d7f5d3SJohn Marino */
29*86d7f5d3SJohn Marino 
30*86d7f5d3SJohn Marino /*
31*86d7f5d3SJohn Marino  * $FreeBSD: src/gnu/usr.bin/rcs/lib/rcsgen.c,v 1.7 1999/08/27 23:36:46 peter Exp $
32*86d7f5d3SJohn Marino  * $DragonFly: src/gnu/usr.bin/rcs/lib/rcsgen.c,v 1.3 2007/01/17 17:56:23 y0netan1 Exp $
33*86d7f5d3SJohn Marino  *
34*86d7f5d3SJohn Marino  * Revision 5.16  1995/06/16 06:19:24  eggert
35*86d7f5d3SJohn Marino  * Update FSF address.
36*86d7f5d3SJohn Marino  *
37*86d7f5d3SJohn Marino  * Revision 5.15  1995/06/01 16:23:43  eggert
38*86d7f5d3SJohn Marino  * (putadmin): Open RCS file with FOPEN_WB.
39*86d7f5d3SJohn Marino  *
40*86d7f5d3SJohn Marino  * Revision 5.14  1994/03/17 14:05:48  eggert
41*86d7f5d3SJohn Marino  * Work around SVR4 stdio performance bug.
42*86d7f5d3SJohn Marino  * Flush stderr after prompt.  Remove lint.
43*86d7f5d3SJohn Marino  *
44*86d7f5d3SJohn Marino  * Revision 5.13  1993/11/03 17:42:27  eggert
45*86d7f5d3SJohn Marino  * Don't discard ignored phrases.  Improve quality of diagnostics.
46*86d7f5d3SJohn Marino  *
47*86d7f5d3SJohn Marino  * Revision 5.12  1992/07/28  16:12:44  eggert
48*86d7f5d3SJohn Marino  * Statement macro names now end in _.
49*86d7f5d3SJohn Marino  * Be consistent about pathnames vs filenames.
50*86d7f5d3SJohn Marino  *
51*86d7f5d3SJohn Marino  * Revision 5.11  1992/01/24  18:44:19  eggert
52*86d7f5d3SJohn Marino  * Move put routines here from rcssyn.c.
53*86d7f5d3SJohn Marino  * Add support for bad_creat0.
54*86d7f5d3SJohn Marino  *
55*86d7f5d3SJohn Marino  * Revision 5.10  1991/10/07  17:32:46  eggert
56*86d7f5d3SJohn Marino  * Fix log bugs, e.g. ci -t/dev/null when has_mmap.
57*86d7f5d3SJohn Marino  *
58*86d7f5d3SJohn Marino  * Revision 5.9  1991/09/10  22:15:46  eggert
59*86d7f5d3SJohn Marino  * Fix test for redirected stdin.
60*86d7f5d3SJohn Marino  *
61*86d7f5d3SJohn Marino  * Revision 5.8  1991/08/19  03:13:55  eggert
62*86d7f5d3SJohn Marino  * Add piece tables.  Tune.
63*86d7f5d3SJohn Marino  *
64*86d7f5d3SJohn Marino  * Revision 5.7  1991/04/21  11:58:24  eggert
65*86d7f5d3SJohn Marino  * Add MS-DOS support.
66*86d7f5d3SJohn Marino  *
67*86d7f5d3SJohn Marino  * Revision 5.6  1990/12/27  19:54:26  eggert
68*86d7f5d3SJohn Marino  * Fix bug: rcs -t inserted \n, making RCS file grow.
69*86d7f5d3SJohn Marino  *
70*86d7f5d3SJohn Marino  * Revision 5.5  1990/12/04  05:18:45  eggert
71*86d7f5d3SJohn Marino  * Use -I for prompts and -q for diagnostics.
72*86d7f5d3SJohn Marino  *
73*86d7f5d3SJohn Marino  * Revision 5.4  1990/11/01  05:03:47  eggert
74*86d7f5d3SJohn Marino  * Add -I and new -t behavior.  Permit arbitrary data in logs.
75*86d7f5d3SJohn Marino  *
76*86d7f5d3SJohn Marino  * Revision 5.3  1990/09/21  06:12:43  hammer
77*86d7f5d3SJohn Marino  * made putdesc() treat stdin the same whether or not it was from a terminal
78*86d7f5d3SJohn Marino  * by making it recognize that a single '.' was then end of the
79*86d7f5d3SJohn Marino  * description always
80*86d7f5d3SJohn Marino  *
81*86d7f5d3SJohn Marino  * Revision 5.2  1990/09/04  08:02:25  eggert
82*86d7f5d3SJohn Marino  * Fix `co -p1.1 -ko' bug.  Standardize yes-or-no procedure.
83*86d7f5d3SJohn Marino  *
84*86d7f5d3SJohn Marino  * Revision 5.1  1990/08/29  07:14:01  eggert
85*86d7f5d3SJohn Marino  * Clean old log messages too.
86*86d7f5d3SJohn Marino  *
87*86d7f5d3SJohn Marino  * Revision 5.0  1990/08/22  08:12:52  eggert
88*86d7f5d3SJohn Marino  * Remove compile-time limits; use malloc instead.
89*86d7f5d3SJohn Marino  * Ansify and Posixate.
90*86d7f5d3SJohn Marino  *
91*86d7f5d3SJohn Marino  * Revision 4.7  89/05/01  15:12:49  narten
92*86d7f5d3SJohn Marino  * changed copyright header to reflect current distribution rules
93*86d7f5d3SJohn Marino  *
94*86d7f5d3SJohn Marino  * Revision 4.6  88/08/28  14:59:10  eggert
95*86d7f5d3SJohn Marino  * Shrink stdio code size; allow cc -R; remove lint; isatty() -> ttystdin()
96*86d7f5d3SJohn Marino  *
97*86d7f5d3SJohn Marino  * Revision 4.5  87/12/18  11:43:25  narten
98*86d7f5d3SJohn Marino  * additional lint cleanups, and a bug fix from the 4.3BSD version that
99*86d7f5d3SJohn Marino  * keeps "ci" from sticking a '\377' into the description if you run it
100*86d7f5d3SJohn Marino  * with a zero-length file as the description. (Guy Harris)
101*86d7f5d3SJohn Marino  *
102*86d7f5d3SJohn Marino  * Revision 4.4  87/10/18  10:35:10  narten
103*86d7f5d3SJohn Marino  * Updating version numbers. Changes relative to 1.1 actually relative to
104*86d7f5d3SJohn Marino  * 4.2
105*86d7f5d3SJohn Marino  *
106*86d7f5d3SJohn Marino  * Revision 1.3  87/09/24  13:59:51  narten
107*86d7f5d3SJohn Marino  * Sources now pass through lint (if you ignore printf/sprintf/fprintf
108*86d7f5d3SJohn Marino  * warnings)
109*86d7f5d3SJohn Marino  *
110*86d7f5d3SJohn Marino  * Revision 1.2  87/03/27  14:22:27  jenkins
111*86d7f5d3SJohn Marino  * Port to suns
112*86d7f5d3SJohn Marino  *
113*86d7f5d3SJohn Marino  * Revision 4.2  83/12/02  23:01:39  wft
114*86d7f5d3SJohn Marino  * merged 4.1 and 3.3.1.1 (clearerr(stdin)).
115*86d7f5d3SJohn Marino  *
116*86d7f5d3SJohn Marino  * Revision 4.1  83/05/10  16:03:33  wft
117*86d7f5d3SJohn Marino  * Changed putamin() to abort if trying to reread redirected stdin.
118*86d7f5d3SJohn Marino  * Fixed getdesc() to output a prompt on initial newline.
119*86d7f5d3SJohn Marino  *
120*86d7f5d3SJohn Marino  * Revision 3.3.1.1  83/10/19  04:21:51  lepreau
121*86d7f5d3SJohn Marino  * Added clearerr(stdin) for re-reading description from stdin.
122*86d7f5d3SJohn Marino  *
123*86d7f5d3SJohn Marino  * Revision 3.3  82/11/28  21:36:49  wft
124*86d7f5d3SJohn Marino  * 4.2 prerelease
125*86d7f5d3SJohn Marino  *
126*86d7f5d3SJohn Marino  * Revision 3.3  82/11/28  21:36:49  wft
127*86d7f5d3SJohn Marino  * Replaced ferror() followed by fclose() with ffclose().
128*86d7f5d3SJohn Marino  * Putdesc() now suppresses the prompts if stdin
129*86d7f5d3SJohn Marino  * is not a terminal. A pointer to the current log message is now
130*86d7f5d3SJohn Marino  * inserted into the corresponding delta, rather than leaving it in a
131*86d7f5d3SJohn Marino  * global variable.
132*86d7f5d3SJohn Marino  *
133*86d7f5d3SJohn Marino  * Revision 3.2  82/10/18  21:11:26  wft
134*86d7f5d3SJohn Marino  * I added checks for write errors during editing, and improved
135*86d7f5d3SJohn Marino  * the prompt on putdesc().
136*86d7f5d3SJohn Marino  *
137*86d7f5d3SJohn Marino  * Revision 3.1  82/10/13  15:55:09  wft
138*86d7f5d3SJohn Marino  * corrected type of variables assigned to by getc (char --> int)
139*86d7f5d3SJohn Marino  */
140*86d7f5d3SJohn Marino 
141*86d7f5d3SJohn Marino 
142*86d7f5d3SJohn Marino 
143*86d7f5d3SJohn Marino 
144*86d7f5d3SJohn Marino #include "rcsbase.h"
145*86d7f5d3SJohn Marino 
146*86d7f5d3SJohn Marino libId(genId, "$DragonFly: src/gnu/usr.bin/rcs/lib/rcsgen.c,v 1.3 2007/01/17 17:56:23 y0netan1 Exp $")
147*86d7f5d3SJohn Marino 
148*86d7f5d3SJohn Marino int interactiveflag;  /* Should we act as if stdin is a tty?  */
149*86d7f5d3SJohn Marino struct buf curlogbuf;  /* buffer for current log message */
150*86d7f5d3SJohn Marino 
151*86d7f5d3SJohn Marino enum stringwork { enter, copy, edit, expand, edit_expand };
152*86d7f5d3SJohn Marino 
153*86d7f5d3SJohn Marino static void putdelta P((struct hshentry const*,FILE*));
154*86d7f5d3SJohn Marino static void scandeltatext P((struct hshentry*,enum stringwork,int));
155*86d7f5d3SJohn Marino 
156*86d7f5d3SJohn Marino 
157*86d7f5d3SJohn Marino 
158*86d7f5d3SJohn Marino 
159*86d7f5d3SJohn Marino 	char const *
buildrevision(deltas,target,outfile,expandflag)160*86d7f5d3SJohn Marino buildrevision(deltas, target, outfile, expandflag)
161*86d7f5d3SJohn Marino 	struct hshentries const *deltas;
162*86d7f5d3SJohn Marino 	struct hshentry *target;
163*86d7f5d3SJohn Marino 	FILE *outfile;
164*86d7f5d3SJohn Marino 	int expandflag;
165*86d7f5d3SJohn Marino /* Function: Generates the revision given by target
166*86d7f5d3SJohn Marino  * by retrieving all deltas given by parameter deltas and combining them.
167*86d7f5d3SJohn Marino  * If outfile is set, the revision is output to it,
168*86d7f5d3SJohn Marino  * otherwise written into a temporary file.
169*86d7f5d3SJohn Marino  * Temporary files are allocated by maketemp().
170*86d7f5d3SJohn Marino  * if expandflag is set, keyword expansion is performed.
171*86d7f5d3SJohn Marino  * Return 0 if outfile is set, the name of the temporary file otherwise.
172*86d7f5d3SJohn Marino  *
173*86d7f5d3SJohn Marino  * Algorithm: Copy initial revision unchanged.  Then edit all revisions but
174*86d7f5d3SJohn Marino  * the last one into it, alternating input and output files (resultname and
175*86d7f5d3SJohn Marino  * editname). The last revision is then edited in, performing simultaneous
176*86d7f5d3SJohn Marino  * keyword substitution (this saves one extra pass).
177*86d7f5d3SJohn Marino  * All this simplifies if only one revision needs to be generated,
178*86d7f5d3SJohn Marino  * or no keyword expansion is necessary, or if output goes to stdout.
179*86d7f5d3SJohn Marino  */
180*86d7f5d3SJohn Marino {
181*86d7f5d3SJohn Marino 	if (deltas->first == target) {
182*86d7f5d3SJohn Marino                 /* only latest revision to generate */
183*86d7f5d3SJohn Marino 		openfcopy(outfile);
184*86d7f5d3SJohn Marino 		scandeltatext(target, expandflag?expand:copy, true);
185*86d7f5d3SJohn Marino 		if (outfile)
186*86d7f5d3SJohn Marino 			return 0;
187*86d7f5d3SJohn Marino 		else {
188*86d7f5d3SJohn Marino 			Ozclose(&fcopy);
189*86d7f5d3SJohn Marino 			return resultname;
190*86d7f5d3SJohn Marino 		}
191*86d7f5d3SJohn Marino         } else {
192*86d7f5d3SJohn Marino                 /* several revisions to generate */
193*86d7f5d3SJohn Marino 		/* Get initial revision without keyword expansion.  */
194*86d7f5d3SJohn Marino 		scandeltatext(deltas->first, enter, false);
195*86d7f5d3SJohn Marino 		while ((deltas=deltas->rest)->rest) {
196*86d7f5d3SJohn Marino                         /* do all deltas except last one */
197*86d7f5d3SJohn Marino 			scandeltatext(deltas->first, edit, false);
198*86d7f5d3SJohn Marino                 }
199*86d7f5d3SJohn Marino 		if (expandflag || outfile) {
200*86d7f5d3SJohn Marino                         /* first, get to beginning of file*/
201*86d7f5d3SJohn Marino 			finishedit((struct hshentry*)0, outfile, false);
202*86d7f5d3SJohn Marino                 }
203*86d7f5d3SJohn Marino 		scandeltatext(target, expandflag?edit_expand:edit, true);
204*86d7f5d3SJohn Marino 		finishedit(
205*86d7f5d3SJohn Marino 			expandflag ? target : (struct hshentry*)0,
206*86d7f5d3SJohn Marino 			outfile, true
207*86d7f5d3SJohn Marino 		);
208*86d7f5d3SJohn Marino 		if (outfile)
209*86d7f5d3SJohn Marino 			return 0;
210*86d7f5d3SJohn Marino 		Ozclose(&fcopy);
211*86d7f5d3SJohn Marino 		return resultname;
212*86d7f5d3SJohn Marino         }
213*86d7f5d3SJohn Marino }
214*86d7f5d3SJohn Marino 
215*86d7f5d3SJohn Marino 
216*86d7f5d3SJohn Marino 
217*86d7f5d3SJohn Marino 	static void
scandeltatext(delta,func,needlog)218*86d7f5d3SJohn Marino scandeltatext(delta, func, needlog)
219*86d7f5d3SJohn Marino 	struct hshentry *delta;
220*86d7f5d3SJohn Marino 	enum stringwork func;
221*86d7f5d3SJohn Marino 	int needlog;
222*86d7f5d3SJohn Marino /* Function: Scans delta text nodes up to and including the one given
223*86d7f5d3SJohn Marino  * by delta. For the one given by delta, the log message is saved into
224*86d7f5d3SJohn Marino  * delta->log if needlog is set; func specifies how to handle the text.
225*86d7f5d3SJohn Marino  * Similarly, if needlog, delta->igtext is set to the ignored phrases.
226*86d7f5d3SJohn Marino  * Assumes the initial lexeme must be read in first.
227*86d7f5d3SJohn Marino  * Does not advance nexttok after it is finished.
228*86d7f5d3SJohn Marino  */
229*86d7f5d3SJohn Marino {
230*86d7f5d3SJohn Marino 	struct hshentry const *nextdelta;
231*86d7f5d3SJohn Marino 	struct cbuf cb;
232*86d7f5d3SJohn Marino 
233*86d7f5d3SJohn Marino         for (;;) {
234*86d7f5d3SJohn Marino 		if (eoflex())
235*86d7f5d3SJohn Marino 		    fatserror("can't find delta for revision %s", delta->num);
236*86d7f5d3SJohn Marino                 nextlex();
237*86d7f5d3SJohn Marino                 if (!(nextdelta=getnum())) {
238*86d7f5d3SJohn Marino 		    fatserror("delta number corrupted");
239*86d7f5d3SJohn Marino                 }
240*86d7f5d3SJohn Marino 		getkeystring(Klog);
241*86d7f5d3SJohn Marino 		if (needlog && delta==nextdelta) {
242*86d7f5d3SJohn Marino 			cb = savestring(&curlogbuf);
243*86d7f5d3SJohn Marino 			delta->log = cleanlogmsg(curlogbuf.string, cb.size);
244*86d7f5d3SJohn Marino 			nextlex();
245*86d7f5d3SJohn Marino 			delta->igtext = getphrases(Ktext);
246*86d7f5d3SJohn Marino                 } else {readstring();
247*86d7f5d3SJohn Marino 			ignorephrases(Ktext);
248*86d7f5d3SJohn Marino                 }
249*86d7f5d3SJohn Marino 		getkeystring(Ktext);
250*86d7f5d3SJohn Marino 
251*86d7f5d3SJohn Marino 		if (delta==nextdelta)
252*86d7f5d3SJohn Marino 			break;
253*86d7f5d3SJohn Marino 		readstring(); /* skip over it */
254*86d7f5d3SJohn Marino 
255*86d7f5d3SJohn Marino 	}
256*86d7f5d3SJohn Marino 	switch (func) {
257*86d7f5d3SJohn Marino 		case enter: enterstring(); break;
258*86d7f5d3SJohn Marino 		case copy: copystring(); break;
259*86d7f5d3SJohn Marino 		case expand: xpandstring(delta); break;
260*86d7f5d3SJohn Marino 		case edit: editstring((struct hshentry *)0); break;
261*86d7f5d3SJohn Marino 		case edit_expand: editstring(delta); break;
262*86d7f5d3SJohn Marino 	}
263*86d7f5d3SJohn Marino }
264*86d7f5d3SJohn Marino 
265*86d7f5d3SJohn Marino 	struct cbuf
cleanlogmsg(m,s)266*86d7f5d3SJohn Marino cleanlogmsg(m, s)
267*86d7f5d3SJohn Marino 	char *m;
268*86d7f5d3SJohn Marino 	size_t s;
269*86d7f5d3SJohn Marino {
270*86d7f5d3SJohn Marino 	register char *t = m;
271*86d7f5d3SJohn Marino 	register char const *f = t;
272*86d7f5d3SJohn Marino 	struct cbuf r;
273*86d7f5d3SJohn Marino 	while (s) {
274*86d7f5d3SJohn Marino 	    --s;
275*86d7f5d3SJohn Marino 	    if ((*t++ = *f++) == '\n')
276*86d7f5d3SJohn Marino 		while (m < --t)
277*86d7f5d3SJohn Marino 		    if (t[-1]!=' ' && t[-1]!='\t') {
278*86d7f5d3SJohn Marino 			*t++ = '\n';
279*86d7f5d3SJohn Marino 			break;
280*86d7f5d3SJohn Marino 		    }
281*86d7f5d3SJohn Marino 	}
282*86d7f5d3SJohn Marino 	while (m < t  &&  (t[-1]==' ' || t[-1]=='\t' || t[-1]=='\n'))
283*86d7f5d3SJohn Marino 	    --t;
284*86d7f5d3SJohn Marino 	r.string = m;
285*86d7f5d3SJohn Marino 	r.size = t - m;
286*86d7f5d3SJohn Marino 	return r;
287*86d7f5d3SJohn Marino }
288*86d7f5d3SJohn Marino 
289*86d7f5d3SJohn Marino 
ttystdin()290*86d7f5d3SJohn Marino int ttystdin()
291*86d7f5d3SJohn Marino {
292*86d7f5d3SJohn Marino 	static int initialized;
293*86d7f5d3SJohn Marino 	if (!initialized) {
294*86d7f5d3SJohn Marino 		if (!interactiveflag)
295*86d7f5d3SJohn Marino 			interactiveflag = isatty(STDIN_FILENO);
296*86d7f5d3SJohn Marino 		initialized = true;
297*86d7f5d3SJohn Marino 	}
298*86d7f5d3SJohn Marino 	return interactiveflag;
299*86d7f5d3SJohn Marino }
300*86d7f5d3SJohn Marino 
301*86d7f5d3SJohn Marino 	int
getcstdin()302*86d7f5d3SJohn Marino getcstdin()
303*86d7f5d3SJohn Marino {
304*86d7f5d3SJohn Marino 	register FILE *in;
305*86d7f5d3SJohn Marino 	register int c;
306*86d7f5d3SJohn Marino 
307*86d7f5d3SJohn Marino 	in = stdin;
308*86d7f5d3SJohn Marino 	if (feof(in) && ttystdin())
309*86d7f5d3SJohn Marino 		clearerr(in);
310*86d7f5d3SJohn Marino 	c = getc(in);
311*86d7f5d3SJohn Marino 	if (c == EOF) {
312*86d7f5d3SJohn Marino 		testIerror(in);
313*86d7f5d3SJohn Marino 		if (feof(in) && ttystdin())
314*86d7f5d3SJohn Marino 			afputc('\n',stderr);
315*86d7f5d3SJohn Marino 	}
316*86d7f5d3SJohn Marino 	return c;
317*86d7f5d3SJohn Marino }
318*86d7f5d3SJohn Marino 
319*86d7f5d3SJohn Marino #if has_prototypes
320*86d7f5d3SJohn Marino 	int
yesorno(int default_answer,char const * question,...)321*86d7f5d3SJohn Marino yesorno(int default_answer, char const *question, ...)
322*86d7f5d3SJohn Marino #else
323*86d7f5d3SJohn Marino 		/*VARARGS2*/ int
324*86d7f5d3SJohn Marino 	yesorno(default_answer, question, va_alist)
325*86d7f5d3SJohn Marino 		int default_answer; char const *question; va_dcl
326*86d7f5d3SJohn Marino #endif
327*86d7f5d3SJohn Marino {
328*86d7f5d3SJohn Marino 	va_list args;
329*86d7f5d3SJohn Marino 	register int c, r;
330*86d7f5d3SJohn Marino 	if (!quietflag && ttystdin()) {
331*86d7f5d3SJohn Marino 		oflush();
332*86d7f5d3SJohn Marino 		vararg_start(args, question);
333*86d7f5d3SJohn Marino 		fvfprintf(stderr, question, args);
334*86d7f5d3SJohn Marino 		va_end(args);
335*86d7f5d3SJohn Marino 		eflush();
336*86d7f5d3SJohn Marino 		r = c = getcstdin();
337*86d7f5d3SJohn Marino 		while (c!='\n' && !feof(stdin))
338*86d7f5d3SJohn Marino 			c = getcstdin();
339*86d7f5d3SJohn Marino 		if (r=='y' || r=='Y')
340*86d7f5d3SJohn Marino 			return true;
341*86d7f5d3SJohn Marino 		if (r=='n' || r=='N')
342*86d7f5d3SJohn Marino 			return false;
343*86d7f5d3SJohn Marino 	}
344*86d7f5d3SJohn Marino 	return default_answer;
345*86d7f5d3SJohn Marino }
346*86d7f5d3SJohn Marino 
347*86d7f5d3SJohn Marino 
348*86d7f5d3SJohn Marino 	void
putdesc(textflag,textfile)349*86d7f5d3SJohn Marino putdesc(textflag, textfile)
350*86d7f5d3SJohn Marino 	int textflag;
351*86d7f5d3SJohn Marino 	char *textfile;
352*86d7f5d3SJohn Marino /* Function: puts the descriptive text into file frewrite.
353*86d7f5d3SJohn Marino  * if finptr && !textflag, the text is copied from the old description.
354*86d7f5d3SJohn Marino  * Otherwise, if textfile, the text is read from that
355*86d7f5d3SJohn Marino  * file, or from stdin, if !textfile.
356*86d7f5d3SJohn Marino  * A textfile with a leading '-' is treated as a string, not a pathname.
357*86d7f5d3SJohn Marino  * If finptr, the old descriptive text is discarded.
358*86d7f5d3SJohn Marino  * Always clears foutptr.
359*86d7f5d3SJohn Marino  */
360*86d7f5d3SJohn Marino {
361*86d7f5d3SJohn Marino 	static struct buf desc;
362*86d7f5d3SJohn Marino 	static struct cbuf desclean;
363*86d7f5d3SJohn Marino 
364*86d7f5d3SJohn Marino 	register FILE *txt;
365*86d7f5d3SJohn Marino 	register int c;
366*86d7f5d3SJohn Marino 	register FILE * frew;
367*86d7f5d3SJohn Marino 	register char *p;
368*86d7f5d3SJohn Marino 	register size_t s;
369*86d7f5d3SJohn Marino 	char const *plim;
370*86d7f5d3SJohn Marino 
371*86d7f5d3SJohn Marino 	frew = frewrite;
372*86d7f5d3SJohn Marino 	if (finptr && !textflag) {
373*86d7f5d3SJohn Marino                 /* copy old description */
374*86d7f5d3SJohn Marino 		aprintf(frew, "\n\n%s%c", Kdesc, nextc);
375*86d7f5d3SJohn Marino 		foutptr = frewrite;
376*86d7f5d3SJohn Marino 		getdesc(false);
377*86d7f5d3SJohn Marino 		foutptr = 0;
378*86d7f5d3SJohn Marino         } else {
379*86d7f5d3SJohn Marino 		foutptr = 0;
380*86d7f5d3SJohn Marino                 /* get new description */
381*86d7f5d3SJohn Marino 		if (finptr) {
382*86d7f5d3SJohn Marino                         /*skip old description*/
383*86d7f5d3SJohn Marino 			getdesc(false);
384*86d7f5d3SJohn Marino                 }
385*86d7f5d3SJohn Marino 		aprintf(frew,"\n\n%s\n%c",Kdesc,SDELIM);
386*86d7f5d3SJohn Marino 		if (!textfile)
387*86d7f5d3SJohn Marino 			desclean = getsstdin(
388*86d7f5d3SJohn Marino 				"t-", "description",
389*86d7f5d3SJohn Marino 				"NOTE: This is NOT the log message!\n", &desc
390*86d7f5d3SJohn Marino 			);
391*86d7f5d3SJohn Marino 		else if (!desclean.string) {
392*86d7f5d3SJohn Marino 			if (*textfile == '-') {
393*86d7f5d3SJohn Marino 				p = textfile + 1;
394*86d7f5d3SJohn Marino 				s = strlen(p);
395*86d7f5d3SJohn Marino 			} else {
396*86d7f5d3SJohn Marino 				if (!(txt = fopenSafer(textfile, "r")))
397*86d7f5d3SJohn Marino 					efaterror(textfile);
398*86d7f5d3SJohn Marino 				bufalloc(&desc, 1);
399*86d7f5d3SJohn Marino 				p = desc.string;
400*86d7f5d3SJohn Marino 				plim = p + desc.size;
401*86d7f5d3SJohn Marino 				for (;;) {
402*86d7f5d3SJohn Marino 					if ((c=getc(txt)) == EOF) {
403*86d7f5d3SJohn Marino 						testIerror(txt);
404*86d7f5d3SJohn Marino 						if (feof(txt))
405*86d7f5d3SJohn Marino 							break;
406*86d7f5d3SJohn Marino 					}
407*86d7f5d3SJohn Marino 					if (plim <= p)
408*86d7f5d3SJohn Marino 					    p = bufenlarge(&desc, &plim);
409*86d7f5d3SJohn Marino 					*p++ = c;
410*86d7f5d3SJohn Marino 				}
411*86d7f5d3SJohn Marino 				if (fclose(txt) != 0)
412*86d7f5d3SJohn Marino 					Ierror();
413*86d7f5d3SJohn Marino 				s = p - desc.string;
414*86d7f5d3SJohn Marino 				p = desc.string;
415*86d7f5d3SJohn Marino 			}
416*86d7f5d3SJohn Marino 			desclean = cleanlogmsg(p, s);
417*86d7f5d3SJohn Marino 		}
418*86d7f5d3SJohn Marino 		putstring(frew, false, desclean, true);
419*86d7f5d3SJohn Marino 		aputc_('\n', frew)
420*86d7f5d3SJohn Marino         }
421*86d7f5d3SJohn Marino }
422*86d7f5d3SJohn Marino 
423*86d7f5d3SJohn Marino 	struct cbuf
getsstdin(option,name,note,buf)424*86d7f5d3SJohn Marino getsstdin(option, name, note, buf)
425*86d7f5d3SJohn Marino 	char const *option, *name, *note;
426*86d7f5d3SJohn Marino 	struct buf *buf;
427*86d7f5d3SJohn Marino {
428*86d7f5d3SJohn Marino 	register int c;
429*86d7f5d3SJohn Marino 	register char *p;
430*86d7f5d3SJohn Marino 	register size_t i;
431*86d7f5d3SJohn Marino 	register int tty = ttystdin();
432*86d7f5d3SJohn Marino 
433*86d7f5d3SJohn Marino 	if (tty) {
434*86d7f5d3SJohn Marino 	    aprintf(stderr,
435*86d7f5d3SJohn Marino 		"enter %s, terminated with single '.' or end of file:\n%s>> ",
436*86d7f5d3SJohn Marino 		name, note
437*86d7f5d3SJohn Marino 	    );
438*86d7f5d3SJohn Marino 	    eflush();
439*86d7f5d3SJohn Marino 	} else if (feof(stdin))
440*86d7f5d3SJohn Marino 	    rcsfaterror("can't reread redirected stdin for %s; use -%s<%s>",
441*86d7f5d3SJohn Marino 		name, option, name
442*86d7f5d3SJohn Marino 	    );
443*86d7f5d3SJohn Marino 
444*86d7f5d3SJohn Marino 	for (
445*86d7f5d3SJohn Marino 	   i = 0,  p = 0;
446*86d7f5d3SJohn Marino 	   c = getcstdin(),  !feof(stdin);
447*86d7f5d3SJohn Marino 	   bufrealloc(buf, i+1),  p = buf->string,  p[i++] = c
448*86d7f5d3SJohn Marino 	)
449*86d7f5d3SJohn Marino 		if (c == '\n') {
450*86d7f5d3SJohn Marino 			if (i  &&  p[i-1]=='.'  &&  (i==1 || p[i-2]=='\n')) {
451*86d7f5d3SJohn Marino 				/* Remove trailing '.'.  */
452*86d7f5d3SJohn Marino 				--i;
453*86d7f5d3SJohn Marino 				break;
454*86d7f5d3SJohn Marino 			} else if (tty) {
455*86d7f5d3SJohn Marino 				aputs(">> ", stderr);
456*86d7f5d3SJohn Marino 				eflush();
457*86d7f5d3SJohn Marino 			}
458*86d7f5d3SJohn Marino 		}
459*86d7f5d3SJohn Marino 	return cleanlogmsg(p, i);
460*86d7f5d3SJohn Marino }
461*86d7f5d3SJohn Marino 
462*86d7f5d3SJohn Marino 
463*86d7f5d3SJohn Marino 	void
putadmin()464*86d7f5d3SJohn Marino putadmin()
465*86d7f5d3SJohn Marino /* Output the admin node.  */
466*86d7f5d3SJohn Marino {
467*86d7f5d3SJohn Marino 	register FILE *fout;
468*86d7f5d3SJohn Marino 	struct assoc const *curassoc;
469*86d7f5d3SJohn Marino 	struct rcslock const *curlock;
470*86d7f5d3SJohn Marino 	struct access const *curaccess;
471*86d7f5d3SJohn Marino 
472*86d7f5d3SJohn Marino 	if (!(fout = frewrite)) {
473*86d7f5d3SJohn Marino #		if bad_creat0
474*86d7f5d3SJohn Marino 			ORCSclose();
475*86d7f5d3SJohn Marino 			fout = fopenSafer(makedirtemp(0), FOPEN_WB);
476*86d7f5d3SJohn Marino #		else
477*86d7f5d3SJohn Marino 			int fo = fdlock;
478*86d7f5d3SJohn Marino 			fdlock = -1;
479*86d7f5d3SJohn Marino 			fout = fdopen(fo, FOPEN_WB);
480*86d7f5d3SJohn Marino #		endif
481*86d7f5d3SJohn Marino 
482*86d7f5d3SJohn Marino 		if (!(frewrite = fout))
483*86d7f5d3SJohn Marino 			efaterror(RCSname);
484*86d7f5d3SJohn Marino 	}
485*86d7f5d3SJohn Marino 
486*86d7f5d3SJohn Marino 	/*
487*86d7f5d3SJohn Marino 	* Output the first character with putc, not printf.
488*86d7f5d3SJohn Marino 	* Otherwise, an SVR4 stdio bug buffers output inefficiently.
489*86d7f5d3SJohn Marino 	*/
490*86d7f5d3SJohn Marino 	aputc_(*Khead, fout)
491*86d7f5d3SJohn Marino 	aprintf(fout, "%s\t%s;\n", Khead + 1, Head?Head->num:"");
492*86d7f5d3SJohn Marino 	if (Dbranch && VERSION(4)<=RCSversion)
493*86d7f5d3SJohn Marino 		aprintf(fout, "%s\t%s;\n", Kbranch, Dbranch);
494*86d7f5d3SJohn Marino 
495*86d7f5d3SJohn Marino 	aputs(Kaccess, fout);
496*86d7f5d3SJohn Marino 	curaccess = AccessList;
497*86d7f5d3SJohn Marino 	while (curaccess) {
498*86d7f5d3SJohn Marino 	       aprintf(fout, "\n\t%s", curaccess->login);
499*86d7f5d3SJohn Marino 	       curaccess = curaccess->nextaccess;
500*86d7f5d3SJohn Marino 	}
501*86d7f5d3SJohn Marino 	aprintf(fout, ";\n%s", Ksymbols);
502*86d7f5d3SJohn Marino 	curassoc = Symbols;
503*86d7f5d3SJohn Marino 	while (curassoc) {
504*86d7f5d3SJohn Marino 	       aprintf(fout, "\n\t%s:%s", curassoc->symbol, curassoc->num);
505*86d7f5d3SJohn Marino 	       curassoc = curassoc->nextassoc;
506*86d7f5d3SJohn Marino 	}
507*86d7f5d3SJohn Marino 	aprintf(fout, ";\n%s", Klocks);
508*86d7f5d3SJohn Marino 	curlock = Locks;
509*86d7f5d3SJohn Marino 	while (curlock) {
510*86d7f5d3SJohn Marino 	       aprintf(fout, "\n\t%s:%s", curlock->login, curlock->delta->num);
511*86d7f5d3SJohn Marino 	       curlock = curlock->nextlock;
512*86d7f5d3SJohn Marino 	}
513*86d7f5d3SJohn Marino 	if (StrictLocks) aprintf(fout, "; %s", Kstrict);
514*86d7f5d3SJohn Marino 	aprintf(fout, ";\n");
515*86d7f5d3SJohn Marino 	if (Comment.size) {
516*86d7f5d3SJohn Marino 		aprintf(fout, "%s\t", Kcomment);
517*86d7f5d3SJohn Marino 		putstring(fout, true, Comment, false);
518*86d7f5d3SJohn Marino 		aprintf(fout, ";\n");
519*86d7f5d3SJohn Marino 	}
520*86d7f5d3SJohn Marino 	if (Expand != KEYVAL_EXPAND)
521*86d7f5d3SJohn Marino 		aprintf(fout, "%s\t%c%s%c;\n",
522*86d7f5d3SJohn Marino 			Kexpand, SDELIM, expand_names[Expand], SDELIM
523*86d7f5d3SJohn Marino 		);
524*86d7f5d3SJohn Marino 	awrite(Ignored.string, Ignored.size, fout);
525*86d7f5d3SJohn Marino 	aputc_('\n', fout)
526*86d7f5d3SJohn Marino }
527*86d7f5d3SJohn Marino 
528*86d7f5d3SJohn Marino 
529*86d7f5d3SJohn Marino 	static void
putdelta(node,fout)530*86d7f5d3SJohn Marino putdelta(node, fout)
531*86d7f5d3SJohn Marino 	register struct hshentry const *node;
532*86d7f5d3SJohn Marino 	register FILE * fout;
533*86d7f5d3SJohn Marino /* Output the delta NODE to FOUT.  */
534*86d7f5d3SJohn Marino {
535*86d7f5d3SJohn Marino 	struct branchhead const *nextbranch;
536*86d7f5d3SJohn Marino 
537*86d7f5d3SJohn Marino 	if (!node) return;
538*86d7f5d3SJohn Marino 
539*86d7f5d3SJohn Marino 	aprintf(fout, "\n%s\n%s\t%s;\t%s %s;\t%s %s;\nbranches",
540*86d7f5d3SJohn Marino 		node->num,
541*86d7f5d3SJohn Marino 		Kdate, node->date,
542*86d7f5d3SJohn Marino 		Kauthor, node->author,
543*86d7f5d3SJohn Marino 		Kstate, node->state?node->state:""
544*86d7f5d3SJohn Marino 	);
545*86d7f5d3SJohn Marino 	nextbranch = node->branches;
546*86d7f5d3SJohn Marino 	while (nextbranch) {
547*86d7f5d3SJohn Marino 	       aprintf(fout, "\n\t%s", nextbranch->hsh->num);
548*86d7f5d3SJohn Marino 	       nextbranch = nextbranch->nextbranch;
549*86d7f5d3SJohn Marino 	}
550*86d7f5d3SJohn Marino 
551*86d7f5d3SJohn Marino 	aprintf(fout, ";\n%s\t%s;\n", Knext, node->next?node->next->num:"");
552*86d7f5d3SJohn Marino 	awrite(node->ig.string, node->ig.size, fout);
553*86d7f5d3SJohn Marino #ifdef RCS_EMIT_COMMITID
554*86d7f5d3SJohn Marino 	if (node->commitid)
555*86d7f5d3SJohn Marino 		aprintf(fout, "%s\t%s;\n", Kcommitid, node->commitid);
556*86d7f5d3SJohn Marino #endif
557*86d7f5d3SJohn Marino }
558*86d7f5d3SJohn Marino 
559*86d7f5d3SJohn Marino 
560*86d7f5d3SJohn Marino 	void
puttree(root,fout)561*86d7f5d3SJohn Marino puttree(root, fout)
562*86d7f5d3SJohn Marino 	struct hshentry const *root;
563*86d7f5d3SJohn Marino 	register FILE *fout;
564*86d7f5d3SJohn Marino /* Output the delta tree with base ROOT in preorder to FOUT.  */
565*86d7f5d3SJohn Marino {
566*86d7f5d3SJohn Marino 	struct branchhead const *nextbranch;
567*86d7f5d3SJohn Marino 
568*86d7f5d3SJohn Marino 	if (!root) return;
569*86d7f5d3SJohn Marino 
570*86d7f5d3SJohn Marino 	if (root->selector)
571*86d7f5d3SJohn Marino 		putdelta(root, fout);
572*86d7f5d3SJohn Marino 
573*86d7f5d3SJohn Marino 	puttree(root->next, fout);
574*86d7f5d3SJohn Marino 
575*86d7f5d3SJohn Marino 	nextbranch = root->branches;
576*86d7f5d3SJohn Marino 	while (nextbranch) {
577*86d7f5d3SJohn Marino 	     puttree(nextbranch->hsh, fout);
578*86d7f5d3SJohn Marino 	     nextbranch = nextbranch->nextbranch;
579*86d7f5d3SJohn Marino 	}
580*86d7f5d3SJohn Marino }
581*86d7f5d3SJohn Marino 
582*86d7f5d3SJohn Marino 
583*86d7f5d3SJohn Marino 	int
putdtext(delta,srcname,fout,diffmt)584*86d7f5d3SJohn Marino putdtext(delta, srcname, fout, diffmt)
585*86d7f5d3SJohn Marino 	struct hshentry const *delta;
586*86d7f5d3SJohn Marino 	char const *srcname;
587*86d7f5d3SJohn Marino 	FILE *fout;
588*86d7f5d3SJohn Marino 	int diffmt;
589*86d7f5d3SJohn Marino /*
590*86d7f5d3SJohn Marino  * Output a deltatext node with delta number DELTA->num, log message DELTA->log,
591*86d7f5d3SJohn Marino  * ignored phrases DELTA->igtext and text SRCNAME to FOUT.
592*86d7f5d3SJohn Marino  * Double up all SDELIMs in both the log and the text.
593*86d7f5d3SJohn Marino  * Make sure the log message ends in \n.
594*86d7f5d3SJohn Marino  * Return false on error.
595*86d7f5d3SJohn Marino  * If DIFFMT, also check that the text is valid diff -n output.
596*86d7f5d3SJohn Marino  */
597*86d7f5d3SJohn Marino {
598*86d7f5d3SJohn Marino 	RILE *fin;
599*86d7f5d3SJohn Marino 	if (!(fin = Iopen(srcname, "r", (struct stat*)0))) {
600*86d7f5d3SJohn Marino 		eerror(srcname);
601*86d7f5d3SJohn Marino 		return false;
602*86d7f5d3SJohn Marino 	}
603*86d7f5d3SJohn Marino 	putdftext(delta, fin, fout, diffmt);
604*86d7f5d3SJohn Marino 	Ifclose(fin);
605*86d7f5d3SJohn Marino 	return true;
606*86d7f5d3SJohn Marino }
607*86d7f5d3SJohn Marino 
608*86d7f5d3SJohn Marino 	void
putstring(out,delim,s,log)609*86d7f5d3SJohn Marino putstring(out, delim, s, log)
610*86d7f5d3SJohn Marino 	register FILE *out;
611*86d7f5d3SJohn Marino 	struct cbuf s;
612*86d7f5d3SJohn Marino 	int delim, log;
613*86d7f5d3SJohn Marino /*
614*86d7f5d3SJohn Marino  * Output to OUT one SDELIM if DELIM, then the string S with SDELIMs doubled.
615*86d7f5d3SJohn Marino  * If LOG is set then S is a log string; append a newline if S is nonempty.
616*86d7f5d3SJohn Marino  */
617*86d7f5d3SJohn Marino {
618*86d7f5d3SJohn Marino 	register char const *sp;
619*86d7f5d3SJohn Marino 	register size_t ss;
620*86d7f5d3SJohn Marino 
621*86d7f5d3SJohn Marino 	if (delim)
622*86d7f5d3SJohn Marino 		aputc_(SDELIM, out)
623*86d7f5d3SJohn Marino 	sp = s.string;
624*86d7f5d3SJohn Marino 	for (ss = s.size;  ss;  --ss) {
625*86d7f5d3SJohn Marino 		if (*sp == SDELIM)
626*86d7f5d3SJohn Marino 			aputc_(SDELIM, out)
627*86d7f5d3SJohn Marino 		aputc_(*sp++, out)
628*86d7f5d3SJohn Marino 	}
629*86d7f5d3SJohn Marino 	if (s.size && log)
630*86d7f5d3SJohn Marino 		aputc_('\n', out)
631*86d7f5d3SJohn Marino 	aputc_(SDELIM, out)
632*86d7f5d3SJohn Marino }
633*86d7f5d3SJohn Marino 
634*86d7f5d3SJohn Marino 	void
putdftext(delta,finfile,foutfile,diffmt)635*86d7f5d3SJohn Marino putdftext(delta, finfile, foutfile, diffmt)
636*86d7f5d3SJohn Marino 	struct hshentry const *delta;
637*86d7f5d3SJohn Marino 	RILE *finfile;
638*86d7f5d3SJohn Marino 	FILE *foutfile;
639*86d7f5d3SJohn Marino 	int diffmt;
640*86d7f5d3SJohn Marino /* like putdtext(), except the source file is already open */
641*86d7f5d3SJohn Marino {
642*86d7f5d3SJohn Marino 	declarecache;
643*86d7f5d3SJohn Marino 	register FILE *fout;
644*86d7f5d3SJohn Marino 	register int c;
645*86d7f5d3SJohn Marino 	register RILE *fin;
646*86d7f5d3SJohn Marino 	int ed;
647*86d7f5d3SJohn Marino 	struct diffcmd dc;
648*86d7f5d3SJohn Marino 
649*86d7f5d3SJohn Marino 	fout = foutfile;
650*86d7f5d3SJohn Marino 	aprintf(fout, DELNUMFORM, delta->num, Klog);
651*86d7f5d3SJohn Marino 
652*86d7f5d3SJohn Marino 	/* put log */
653*86d7f5d3SJohn Marino 	putstring(fout, true, delta->log, true);
654*86d7f5d3SJohn Marino 	aputc_('\n', fout)
655*86d7f5d3SJohn Marino 
656*86d7f5d3SJohn Marino 	/* put ignored phrases */
657*86d7f5d3SJohn Marino 	awrite(delta->igtext.string, delta->igtext.size, fout);
658*86d7f5d3SJohn Marino 
659*86d7f5d3SJohn Marino 	/* put text */
660*86d7f5d3SJohn Marino 	aprintf(fout, "%s\n%c", Ktext, SDELIM);
661*86d7f5d3SJohn Marino 
662*86d7f5d3SJohn Marino 	fin = finfile;
663*86d7f5d3SJohn Marino 	setupcache(fin);
664*86d7f5d3SJohn Marino 	if (!diffmt) {
665*86d7f5d3SJohn Marino 	    /* Copy the file */
666*86d7f5d3SJohn Marino 	    cache(fin);
667*86d7f5d3SJohn Marino 	    for (;;) {
668*86d7f5d3SJohn Marino 		cachegeteof_(c, break;)
669*86d7f5d3SJohn Marino 		if (c==SDELIM) aputc_(SDELIM, fout) /*double up SDELIM*/
670*86d7f5d3SJohn Marino 		aputc_(c, fout)
671*86d7f5d3SJohn Marino 	    }
672*86d7f5d3SJohn Marino 	} else {
673*86d7f5d3SJohn Marino 	    initdiffcmd(&dc);
674*86d7f5d3SJohn Marino 	    while (0  <=  (ed = getdiffcmd(fin, false, fout, &dc)))
675*86d7f5d3SJohn Marino 		if (ed) {
676*86d7f5d3SJohn Marino 		    cache(fin);
677*86d7f5d3SJohn Marino 		    while (dc.nlines--)
678*86d7f5d3SJohn Marino 			do {
679*86d7f5d3SJohn Marino 			    cachegeteof_(c, { if (!dc.nlines) goto OK_EOF; unexpected_EOF(); })
680*86d7f5d3SJohn Marino 			    if (c == SDELIM)
681*86d7f5d3SJohn Marino 				aputc_(SDELIM, fout)
682*86d7f5d3SJohn Marino 			    aputc_(c, fout)
683*86d7f5d3SJohn Marino 			} while (c != '\n');
684*86d7f5d3SJohn Marino 		    uncache(fin);
685*86d7f5d3SJohn Marino 		}
686*86d7f5d3SJohn Marino 	}
687*86d7f5d3SJohn Marino     OK_EOF:
688*86d7f5d3SJohn Marino 	aprintf(fout, "%c\n", SDELIM);
689*86d7f5d3SJohn Marino }
690