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