111894Speter/* Generate RCS revisions.  */
29Sjkh
311894Speter/* Copyright 1982, 1988, 1989 Walter Tichy
411894Speter   Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
59Sjkh   Distributed under license by the Free Software Foundation, Inc.
69Sjkh
79SjkhThis file is part of RCS.
89Sjkh
99SjkhRCS is free software; you can redistribute it and/or modify
109Sjkhit under the terms of the GNU General Public License as published by
119Sjkhthe Free Software Foundation; either version 2, or (at your option)
129Sjkhany later version.
139Sjkh
149SjkhRCS is distributed in the hope that it will be useful,
159Sjkhbut WITHOUT ANY WARRANTY; without even the implied warranty of
169SjkhMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
179SjkhGNU General Public License for more details.
189Sjkh
199SjkhYou should have received a copy of the GNU General Public License
2011894Speteralong with RCS; see the file COPYING.
2111894SpeterIf not, write to the Free Software Foundation,
2211894Speter59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
239Sjkh
249SjkhReport problems and direct all questions to:
259Sjkh
269Sjkh    rcs-bugs@cs.purdue.edu
279Sjkh
289Sjkh*/
299Sjkh
3011894Speter/*
3111894Speter * Revision 5.16  1995/06/16 06:19:24  eggert
3211894Speter * Update FSF address.
338858Srgrimes *
3411894Speter * Revision 5.15  1995/06/01 16:23:43  eggert
3511894Speter * (putadmin): Open RCS file with FOPEN_WB.
3611894Speter *
3711894Speter * Revision 5.14  1994/03/17 14:05:48  eggert
3811894Speter * Work around SVR4 stdio performance bug.
3911894Speter * Flush stderr after prompt.  Remove lint.
4011894Speter *
4111894Speter * Revision 5.13  1993/11/03 17:42:27  eggert
4211894Speter * Don't discard ignored phrases.  Improve quality of diagnostics.
4311894Speter *
4411894Speter * Revision 5.12  1992/07/28  16:12:44  eggert
4511894Speter * Statement macro names now end in _.
4611894Speter * Be consistent about pathnames vs filenames.
4711894Speter *
4811894Speter * Revision 5.11  1992/01/24  18:44:19  eggert
4911894Speter * Move put routines here from rcssyn.c.
5011894Speter * Add support for bad_creat0.
5111894Speter *
529Sjkh * Revision 5.10  1991/10/07  17:32:46  eggert
539Sjkh * Fix log bugs, e.g. ci -t/dev/null when has_mmap.
549Sjkh *
559Sjkh * Revision 5.9  1991/09/10  22:15:46  eggert
569Sjkh * Fix test for redirected stdin.
579Sjkh *
589Sjkh * Revision 5.8  1991/08/19  03:13:55  eggert
599Sjkh * Add piece tables.  Tune.
609Sjkh *
619Sjkh * Revision 5.7  1991/04/21  11:58:24  eggert
629Sjkh * Add MS-DOS support.
639Sjkh *
649Sjkh * Revision 5.6  1990/12/27  19:54:26  eggert
659Sjkh * Fix bug: rcs -t inserted \n, making RCS file grow.
669Sjkh *
679Sjkh * Revision 5.5  1990/12/04  05:18:45  eggert
689Sjkh * Use -I for prompts and -q for diagnostics.
699Sjkh *
709Sjkh * Revision 5.4  1990/11/01  05:03:47  eggert
719Sjkh * Add -I and new -t behavior.  Permit arbitrary data in logs.
729Sjkh *
739Sjkh * Revision 5.3  1990/09/21  06:12:43  hammer
749Sjkh * made putdesc() treat stdin the same whether or not it was from a terminal
759Sjkh * by making it recognize that a single '.' was then end of the
769Sjkh * description always
779Sjkh *
789Sjkh * Revision 5.2  1990/09/04  08:02:25  eggert
799Sjkh * Fix `co -p1.1 -ko' bug.  Standardize yes-or-no procedure.
809Sjkh *
819Sjkh * Revision 5.1  1990/08/29  07:14:01  eggert
829Sjkh * Clean old log messages too.
839Sjkh *
849Sjkh * Revision 5.0  1990/08/22  08:12:52  eggert
859Sjkh * Remove compile-time limits; use malloc instead.
869Sjkh * Ansify and Posixate.
879Sjkh *
889Sjkh * Revision 4.7  89/05/01  15:12:49  narten
899Sjkh * changed copyright header to reflect current distribution rules
908858Srgrimes *
919Sjkh * Revision 4.6  88/08/28  14:59:10  eggert
929Sjkh * Shrink stdio code size; allow cc -R; remove lint; isatty() -> ttystdin()
938858Srgrimes *
949Sjkh * Revision 4.5  87/12/18  11:43:25  narten
959Sjkh * additional lint cleanups, and a bug fix from the 4.3BSD version that
969Sjkh * keeps "ci" from sticking a '\377' into the description if you run it
979Sjkh * with a zero-length file as the description. (Guy Harris)
988858Srgrimes *
999Sjkh * Revision 4.4  87/10/18  10:35:10  narten
1009Sjkh * Updating version numbers. Changes relative to 1.1 actually relative to
1019Sjkh * 4.2
1028858Srgrimes *
1039Sjkh * Revision 1.3  87/09/24  13:59:51  narten
1048858Srgrimes * Sources now pass through lint (if you ignore printf/sprintf/fprintf
1059Sjkh * warnings)
1068858Srgrimes *
1079Sjkh * Revision 1.2  87/03/27  14:22:27  jenkins
1089Sjkh * Port to suns
1098858Srgrimes *
1109Sjkh * Revision 4.2  83/12/02  23:01:39  wft
1119Sjkh * merged 4.1 and 3.3.1.1 (clearerr(stdin)).
1128858Srgrimes *
1139Sjkh * Revision 4.1  83/05/10  16:03:33  wft
1149Sjkh * Changed putamin() to abort if trying to reread redirected stdin.
1159Sjkh * Fixed getdesc() to output a prompt on initial newline.
1168858Srgrimes *
1179Sjkh * Revision 3.3.1.1  83/10/19  04:21:51  lepreau
1189Sjkh * Added clearerr(stdin) for re-reading description from stdin.
1198858Srgrimes *
1209Sjkh * Revision 3.3  82/11/28  21:36:49  wft
1219Sjkh * 4.2 prerelease
1228858Srgrimes *
1239Sjkh * Revision 3.3  82/11/28  21:36:49  wft
1249Sjkh * Replaced ferror() followed by fclose() with ffclose().
1259Sjkh * Putdesc() now suppresses the prompts if stdin
1269Sjkh * is not a terminal. A pointer to the current log message is now
1279Sjkh * inserted into the corresponding delta, rather than leaving it in a
1289Sjkh * global variable.
1299Sjkh *
1309Sjkh * Revision 3.2  82/10/18  21:11:26  wft
1319Sjkh * I added checks for write errors during editing, and improved
1329Sjkh * the prompt on putdesc().
1339Sjkh *
1349Sjkh * Revision 3.1  82/10/13  15:55:09  wft
1359Sjkh * corrected type of variables assigned to by getc (char --> int)
1369Sjkh */
1379Sjkh
1389Sjkh
1399Sjkh
1409Sjkh
1419Sjkh#include "rcsbase.h"
1429Sjkh
14350472SpeterlibId(genId, "$FreeBSD$")
1449Sjkh
1459Sjkhint interactiveflag;  /* Should we act as if stdin is a tty?  */
1469Sjkhstruct buf curlogbuf;  /* buffer for current log message */
1479Sjkh
1489Sjkhenum stringwork { enter, copy, edit, expand, edit_expand };
14911894Speter
15011894Speterstatic void putdelta P((struct hshentry const*,FILE*));
1519Sjkhstatic void scandeltatext P((struct hshentry*,enum stringwork,int));
1529Sjkh
1539Sjkh
1549Sjkh
1559Sjkh
1569Sjkh	char const *
1579Sjkhbuildrevision(deltas, target, outfile, expandflag)
1589Sjkh	struct hshentries const *deltas;
1599Sjkh	struct hshentry *target;
1609Sjkh	FILE *outfile;
1619Sjkh	int expandflag;
1629Sjkh/* Function: Generates the revision given by target
1639Sjkh * by retrieving all deltas given by parameter deltas and combining them.
1649Sjkh * If outfile is set, the revision is output to it,
1659Sjkh * otherwise written into a temporary file.
1669Sjkh * Temporary files are allocated by maketemp().
1679Sjkh * if expandflag is set, keyword expansion is performed.
16811894Speter * Return 0 if outfile is set, the name of the temporary file otherwise.
1699Sjkh *
1709Sjkh * Algorithm: Copy initial revision unchanged.  Then edit all revisions but
17111894Speter * the last one into it, alternating input and output files (resultname and
17211894Speter * editname). The last revision is then edited in, performing simultaneous
1739Sjkh * keyword substitution (this saves one extra pass).
1749Sjkh * All this simplifies if only one revision needs to be generated,
1759Sjkh * or no keyword expansion is necessary, or if output goes to stdout.
1769Sjkh */
1779Sjkh{
1789Sjkh	if (deltas->first == target) {
1799Sjkh                /* only latest revision to generate */
1809Sjkh		openfcopy(outfile);
1819Sjkh		scandeltatext(target, expandflag?expand:copy, true);
1829Sjkh		if (outfile)
1839Sjkh			return 0;
1849Sjkh		else {
1859Sjkh			Ozclose(&fcopy);
18611894Speter			return resultname;
1879Sjkh		}
1889Sjkh        } else {
1899Sjkh                /* several revisions to generate */
1909Sjkh		/* Get initial revision without keyword expansion.  */
1919Sjkh		scandeltatext(deltas->first, enter, false);
1929Sjkh		while ((deltas=deltas->rest)->rest) {
1939Sjkh                        /* do all deltas except last one */
1949Sjkh			scandeltatext(deltas->first, edit, false);
1959Sjkh                }
1969Sjkh		if (expandflag || outfile) {
1979Sjkh                        /* first, get to beginning of file*/
19811894Speter			finishedit((struct hshentry*)0, outfile, false);
1999Sjkh                }
20011894Speter		scandeltatext(target, expandflag?edit_expand:edit, true);
2019Sjkh		finishedit(
20211894Speter			expandflag ? target : (struct hshentry*)0,
2039Sjkh			outfile, true
2049Sjkh		);
2059Sjkh		if (outfile)
2069Sjkh			return 0;
2079Sjkh		Ozclose(&fcopy);
20811894Speter		return resultname;
2099Sjkh        }
2109Sjkh}
2119Sjkh
2129Sjkh
2139Sjkh
2149Sjkh	static void
2159Sjkhscandeltatext(delta, func, needlog)
21611894Speter	struct hshentry *delta;
2179Sjkh	enum stringwork func;
2189Sjkh	int needlog;
2199Sjkh/* Function: Scans delta text nodes up to and including the one given
2209Sjkh * by delta. For the one given by delta, the log message is saved into
2219Sjkh * delta->log if needlog is set; func specifies how to handle the text.
22211894Speter * Similarly, if needlog, delta->igtext is set to the ignored phrases.
2239Sjkh * Assumes the initial lexeme must be read in first.
2249Sjkh * Does not advance nexttok after it is finished.
2259Sjkh */
2269Sjkh{
2279Sjkh	struct hshentry const *nextdelta;
2289Sjkh	struct cbuf cb;
2299Sjkh
2309Sjkh        for (;;) {
2319Sjkh		if (eoflex())
2329Sjkh		    fatserror("can't find delta for revision %s", delta->num);
2339Sjkh                nextlex();
2349Sjkh                if (!(nextdelta=getnum())) {
2359Sjkh		    fatserror("delta number corrupted");
2369Sjkh                }
2379Sjkh		getkeystring(Klog);
2389Sjkh		if (needlog && delta==nextdelta) {
2399Sjkh			cb = savestring(&curlogbuf);
2409Sjkh			delta->log = cleanlogmsg(curlogbuf.string, cb.size);
24111894Speter			nextlex();
24211894Speter			delta->igtext = getphrases(Ktext);
2439Sjkh                } else {readstring();
24411894Speter			ignorephrases(Ktext);
2459Sjkh                }
2469Sjkh		getkeystring(Ktext);
2479Sjkh
2489Sjkh		if (delta==nextdelta)
2499Sjkh			break;
2509Sjkh		readstring(); /* skip over it */
2519Sjkh
2529Sjkh	}
2539Sjkh	switch (func) {
2549Sjkh		case enter: enterstring(); break;
2559Sjkh		case copy: copystring(); break;
2569Sjkh		case expand: xpandstring(delta); break;
25711894Speter		case edit: editstring((struct hshentry *)0); break;
2589Sjkh		case edit_expand: editstring(delta); break;
2599Sjkh	}
2609Sjkh}
2619Sjkh
2629Sjkh	struct cbuf
2639Sjkhcleanlogmsg(m, s)
2649Sjkh	char *m;
2659Sjkh	size_t s;
2669Sjkh{
2679Sjkh	register char *t = m;
2689Sjkh	register char const *f = t;
2699Sjkh	struct cbuf r;
2709Sjkh	while (s) {
2719Sjkh	    --s;
2729Sjkh	    if ((*t++ = *f++) == '\n')
2739Sjkh		while (m < --t)
2749Sjkh		    if (t[-1]!=' ' && t[-1]!='\t') {
2759Sjkh			*t++ = '\n';
2769Sjkh			break;
2779Sjkh		    }
2789Sjkh	}
2799Sjkh	while (m < t  &&  (t[-1]==' ' || t[-1]=='\t' || t[-1]=='\n'))
2809Sjkh	    --t;
2819Sjkh	r.string = m;
2829Sjkh	r.size = t - m;
2839Sjkh	return r;
2849Sjkh}
2859Sjkh
2869Sjkh
2879Sjkhint ttystdin()
2889Sjkh{
2899Sjkh	static int initialized;
2909Sjkh	if (!initialized) {
2919Sjkh		if (!interactiveflag)
2929Sjkh			interactiveflag = isatty(STDIN_FILENO);
2939Sjkh		initialized = true;
2949Sjkh	}
2959Sjkh	return interactiveflag;
2969Sjkh}
2979Sjkh
2989Sjkh	int
2999Sjkhgetcstdin()
3009Sjkh{
3019Sjkh	register FILE *in;
3029Sjkh	register int c;
3039Sjkh
3049Sjkh	in = stdin;
3059Sjkh	if (feof(in) && ttystdin())
3069Sjkh		clearerr(in);
3079Sjkh	c = getc(in);
30811894Speter	if (c == EOF) {
3099Sjkh		testIerror(in);
3109Sjkh		if (feof(in) && ttystdin())
3119Sjkh			afputc('\n',stderr);
3129Sjkh	}
3139Sjkh	return c;
3149Sjkh}
3159Sjkh
3169Sjkh#if has_prototypes
3179Sjkh	int
3189Sjkhyesorno(int default_answer, char const *question, ...)
3199Sjkh#else
3209Sjkh		/*VARARGS2*/ int
3219Sjkh	yesorno(default_answer, question, va_alist)
3229Sjkh		int default_answer; char const *question; va_dcl
3239Sjkh#endif
3249Sjkh{
3259Sjkh	va_list args;
3269Sjkh	register int c, r;
3279Sjkh	if (!quietflag && ttystdin()) {
3289Sjkh		oflush();
3299Sjkh		vararg_start(args, question);
3309Sjkh		fvfprintf(stderr, question, args);
3319Sjkh		va_end(args);
3329Sjkh		eflush();
3339Sjkh		r = c = getcstdin();
3349Sjkh		while (c!='\n' && !feof(stdin))
3359Sjkh			c = getcstdin();
3369Sjkh		if (r=='y' || r=='Y')
3379Sjkh			return true;
3389Sjkh		if (r=='n' || r=='N')
3399Sjkh			return false;
3409Sjkh	}
3419Sjkh	return default_answer;
3429Sjkh}
3439Sjkh
3449Sjkh
3459Sjkh	void
3469Sjkhputdesc(textflag, textfile)
3479Sjkh	int textflag;
3489Sjkh	char *textfile;
3499Sjkh/* Function: puts the descriptive text into file frewrite.
3509Sjkh * if finptr && !textflag, the text is copied from the old description.
35111894Speter * Otherwise, if textfile, the text is read from that
35211894Speter * file, or from stdin, if !textfile.
35311894Speter * A textfile with a leading '-' is treated as a string, not a pathname.
3549Sjkh * If finptr, the old descriptive text is discarded.
3559Sjkh * Always clears foutptr.
3569Sjkh */
3579Sjkh{
3589Sjkh	static struct buf desc;
3599Sjkh	static struct cbuf desclean;
3609Sjkh
3619Sjkh	register FILE *txt;
3629Sjkh	register int c;
3639Sjkh	register FILE * frew;
3649Sjkh	register char *p;
3659Sjkh	register size_t s;
3669Sjkh	char const *plim;
3679Sjkh
3689Sjkh	frew = frewrite;
3699Sjkh	if (finptr && !textflag) {
3709Sjkh                /* copy old description */
3719Sjkh		aprintf(frew, "\n\n%s%c", Kdesc, nextc);
3729Sjkh		foutptr = frewrite;
3739Sjkh		getdesc(false);
3749Sjkh		foutptr = 0;
3759Sjkh        } else {
3769Sjkh		foutptr = 0;
3779Sjkh                /* get new description */
3789Sjkh		if (finptr) {
3799Sjkh                        /*skip old description*/
3809Sjkh			getdesc(false);
3819Sjkh                }
3829Sjkh		aprintf(frew,"\n\n%s\n%c",Kdesc,SDELIM);
3839Sjkh		if (!textfile)
3849Sjkh			desclean = getsstdin(
3859Sjkh				"t-", "description",
3869Sjkh				"NOTE: This is NOT the log message!\n", &desc
3879Sjkh			);
3889Sjkh		else if (!desclean.string) {
3899Sjkh			if (*textfile == '-') {
3909Sjkh				p = textfile + 1;
3919Sjkh				s = strlen(p);
3929Sjkh			} else {
39311894Speter				if (!(txt = fopenSafer(textfile, "r")))
3949Sjkh					efaterror(textfile);
3959Sjkh				bufalloc(&desc, 1);
3969Sjkh				p = desc.string;
3979Sjkh				plim = p + desc.size;
3989Sjkh				for (;;) {
39911894Speter					if ((c=getc(txt)) == EOF) {
4009Sjkh						testIerror(txt);
4019Sjkh						if (feof(txt))
4029Sjkh							break;
4039Sjkh					}
4049Sjkh					if (plim <= p)
4059Sjkh					    p = bufenlarge(&desc, &plim);
4069Sjkh					*p++ = c;
4079Sjkh				}
4089Sjkh				if (fclose(txt) != 0)
4099Sjkh					Ierror();
4109Sjkh				s = p - desc.string;
4119Sjkh				p = desc.string;
4129Sjkh			}
4139Sjkh			desclean = cleanlogmsg(p, s);
4149Sjkh		}
4159Sjkh		putstring(frew, false, desclean, true);
41611894Speter		aputc_('\n', frew)
4179Sjkh        }
4189Sjkh}
4199Sjkh
4209Sjkh	struct cbuf
4219Sjkhgetsstdin(option, name, note, buf)
4229Sjkh	char const *option, *name, *note;
4239Sjkh	struct buf *buf;
4249Sjkh{
4259Sjkh	register int c;
4269Sjkh	register char *p;
4279Sjkh	register size_t i;
4289Sjkh	register int tty = ttystdin();
4299Sjkh
43011894Speter	if (tty) {
4319Sjkh	    aprintf(stderr,
4329Sjkh		"enter %s, terminated with single '.' or end of file:\n%s>> ",
4339Sjkh		name, note
4349Sjkh	    );
43511894Speter	    eflush();
43611894Speter	} else if (feof(stdin))
43711894Speter	    rcsfaterror("can't reread redirected stdin for %s; use -%s<%s>",
4389Sjkh		name, option, name
4399Sjkh	    );
4408858Srgrimes
4419Sjkh	for (
4429Sjkh	   i = 0,  p = 0;
4439Sjkh	   c = getcstdin(),  !feof(stdin);
4449Sjkh	   bufrealloc(buf, i+1),  p = buf->string,  p[i++] = c
4459Sjkh	)
4469Sjkh		if (c == '\n')
4479Sjkh			if (i  &&  p[i-1]=='.'  &&  (i==1 || p[i-2]=='\n')) {
4489Sjkh				/* Remove trailing '.'.  */
4499Sjkh				--i;
4509Sjkh				break;
45111894Speter			} else if (tty) {
4529Sjkh				aputs(">> ", stderr);
45311894Speter				eflush();
45411894Speter			}
4559Sjkh	return cleanlogmsg(p, i);
4569Sjkh}
45711894Speter
45811894Speter
45911894Speter	void
46011894Speterputadmin()
46111894Speter/* Output the admin node.  */
46211894Speter{
46311894Speter	register FILE *fout;
46411894Speter	struct assoc const *curassoc;
46511894Speter	struct rcslock const *curlock;
46611894Speter	struct access const *curaccess;
46711894Speter
46811894Speter	if (!(fout = frewrite)) {
46911894Speter#		if bad_creat0
47011894Speter			ORCSclose();
47111894Speter			fout = fopenSafer(makedirtemp(0), FOPEN_WB);
47211894Speter#		else
47311894Speter			int fo = fdlock;
47411894Speter			fdlock = -1;
47511894Speter			fout = fdopen(fo, FOPEN_WB);
47611894Speter#		endif
47711894Speter
47811894Speter		if (!(frewrite = fout))
47911894Speter			efaterror(RCSname);
48011894Speter	}
48111894Speter
48211894Speter	/*
48311894Speter	* Output the first character with putc, not printf.
48411894Speter	* Otherwise, an SVR4 stdio bug buffers output inefficiently.
48511894Speter	*/
48611894Speter	aputc_(*Khead, fout)
48711894Speter	aprintf(fout, "%s\t%s;\n", Khead + 1, Head?Head->num:"");
48811894Speter	if (Dbranch && VERSION(4)<=RCSversion)
48911894Speter		aprintf(fout, "%s\t%s;\n", Kbranch, Dbranch);
49011894Speter
49111894Speter	aputs(Kaccess, fout);
49211894Speter	curaccess = AccessList;
49311894Speter	while (curaccess) {
49411894Speter	       aprintf(fout, "\n\t%s", curaccess->login);
49511894Speter	       curaccess = curaccess->nextaccess;
49611894Speter	}
49711894Speter	aprintf(fout, ";\n%s", Ksymbols);
49811894Speter	curassoc = Symbols;
49911894Speter	while (curassoc) {
50011894Speter	       aprintf(fout, "\n\t%s:%s", curassoc->symbol, curassoc->num);
50111894Speter	       curassoc = curassoc->nextassoc;
50211894Speter	}
50311894Speter	aprintf(fout, ";\n%s", Klocks);
50411894Speter	curlock = Locks;
50511894Speter	while (curlock) {
50611894Speter	       aprintf(fout, "\n\t%s:%s", curlock->login, curlock->delta->num);
50711894Speter	       curlock = curlock->nextlock;
50811894Speter	}
50911894Speter	if (StrictLocks) aprintf(fout, "; %s", Kstrict);
51011894Speter	aprintf(fout, ";\n");
51111894Speter	if (Comment.size) {
51211894Speter		aprintf(fout, "%s\t", Kcomment);
51311894Speter		putstring(fout, true, Comment, false);
51411894Speter		aprintf(fout, ";\n");
51511894Speter	}
51611894Speter	if (Expand != KEYVAL_EXPAND)
51711894Speter		aprintf(fout, "%s\t%c%s%c;\n",
51811894Speter			Kexpand, SDELIM, expand_names[Expand], SDELIM
51911894Speter		);
52011894Speter	awrite(Ignored.string, Ignored.size, fout);
52111894Speter	aputc_('\n', fout)
52211894Speter}
52311894Speter
52411894Speter
52511894Speter	static void
52611894Speterputdelta(node, fout)
52711894Speter	register struct hshentry const *node;
52811894Speter	register FILE * fout;
52911894Speter/* Output the delta NODE to FOUT.  */
53011894Speter{
53111894Speter	struct branchhead const *nextbranch;
53211894Speter
53311894Speter	if (!node) return;
53411894Speter
53511894Speter	aprintf(fout, "\n%s\n%s\t%s;\t%s %s;\t%s %s;\nbranches",
53611894Speter		node->num,
53711894Speter		Kdate, node->date,
53811894Speter		Kauthor, node->author,
53911894Speter		Kstate, node->state?node->state:""
54011894Speter	);
54111894Speter	nextbranch = node->branches;
54211894Speter	while (nextbranch) {
54311894Speter	       aprintf(fout, "\n\t%s", nextbranch->hsh->num);
54411894Speter	       nextbranch = nextbranch->nextbranch;
54511894Speter	}
54611894Speter
54711894Speter	aprintf(fout, ";\n%s\t%s;\n", Knext, node->next?node->next->num:"");
54811894Speter	awrite(node->ig.string, node->ig.size, fout);
54911894Speter}
55011894Speter
55111894Speter
55211894Speter	void
55311894Speterputtree(root, fout)
55411894Speter	struct hshentry const *root;
55511894Speter	register FILE *fout;
55611894Speter/* Output the delta tree with base ROOT in preorder to FOUT.  */
55711894Speter{
55811894Speter	struct branchhead const *nextbranch;
55911894Speter
56011894Speter	if (!root) return;
56111894Speter
56211894Speter	if (root->selector)
56311894Speter		putdelta(root, fout);
56411894Speter
56511894Speter	puttree(root->next, fout);
56611894Speter
56711894Speter	nextbranch = root->branches;
56811894Speter	while (nextbranch) {
56911894Speter	     puttree(nextbranch->hsh, fout);
57011894Speter	     nextbranch = nextbranch->nextbranch;
57111894Speter	}
57211894Speter}
57311894Speter
57411894Speter
57511894Speter	int
57611894Speterputdtext(delta, srcname, fout, diffmt)
57711894Speter	struct hshentry const *delta;
57811894Speter	char const *srcname;
57911894Speter	FILE *fout;
58011894Speter	int diffmt;
58111894Speter/*
58211894Speter * Output a deltatext node with delta number DELTA->num, log message DELTA->log,
58311894Speter * ignored phrases DELTA->igtext and text SRCNAME to FOUT.
58411894Speter * Double up all SDELIMs in both the log and the text.
58511894Speter * Make sure the log message ends in \n.
58611894Speter * Return false on error.
58711894Speter * If DIFFMT, also check that the text is valid diff -n output.
58811894Speter */
58911894Speter{
59011894Speter	RILE *fin;
59111894Speter	if (!(fin = Iopen(srcname, "r", (struct stat*)0))) {
59211894Speter		eerror(srcname);
59311894Speter		return false;
59411894Speter	}
59511894Speter	putdftext(delta, fin, fout, diffmt);
59611894Speter	Ifclose(fin);
59711894Speter	return true;
59811894Speter}
59911894Speter
60011894Speter	void
60111894Speterputstring(out, delim, s, log)
60211894Speter	register FILE *out;
60311894Speter	struct cbuf s;
60411894Speter	int delim, log;
60511894Speter/*
60611894Speter * Output to OUT one SDELIM if DELIM, then the string S with SDELIMs doubled.
60711894Speter * If LOG is set then S is a log string; append a newline if S is nonempty.
60811894Speter */
60911894Speter{
61011894Speter	register char const *sp;
61111894Speter	register size_t ss;
61211894Speter
61311894Speter	if (delim)
61411894Speter		aputc_(SDELIM, out)
61511894Speter	sp = s.string;
61611894Speter	for (ss = s.size;  ss;  --ss) {
61711894Speter		if (*sp == SDELIM)
61811894Speter			aputc_(SDELIM, out)
61911894Speter		aputc_(*sp++, out)
62011894Speter	}
62111894Speter	if (s.size && log)
62211894Speter		aputc_('\n', out)
62311894Speter	aputc_(SDELIM, out)
62411894Speter}
62511894Speter
62611894Speter	void
62711894Speterputdftext(delta, finfile, foutfile, diffmt)
62811894Speter	struct hshentry const *delta;
62911894Speter	RILE *finfile;
63011894Speter	FILE *foutfile;
63111894Speter	int diffmt;
63211894Speter/* like putdtext(), except the source file is already open */
63311894Speter{
63411894Speter	declarecache;
63511894Speter	register FILE *fout;
63611894Speter	register int c;
63711894Speter	register RILE *fin;
63811894Speter	int ed;
63911894Speter	struct diffcmd dc;
64011894Speter
64111894Speter	fout = foutfile;
64211894Speter	aprintf(fout, DELNUMFORM, delta->num, Klog);
64311894Speter
64411894Speter	/* put log */
64511894Speter	putstring(fout, true, delta->log, true);
64611894Speter	aputc_('\n', fout)
64711894Speter
64811894Speter	/* put ignored phrases */
64911894Speter	awrite(delta->igtext.string, delta->igtext.size, fout);
65011894Speter
65111894Speter	/* put text */
65211894Speter	aprintf(fout, "%s\n%c", Ktext, SDELIM);
65311894Speter
65411894Speter	fin = finfile;
65511894Speter	setupcache(fin);
65611894Speter	if (!diffmt) {
65711894Speter	    /* Copy the file */
65811894Speter	    cache(fin);
65911894Speter	    for (;;) {
66011894Speter		cachegeteof_(c, break;)
66111894Speter		if (c==SDELIM) aputc_(SDELIM, fout) /*double up SDELIM*/
66211894Speter		aputc_(c, fout)
66311894Speter	    }
66411894Speter	} else {
66511894Speter	    initdiffcmd(&dc);
66611894Speter	    while (0  <=  (ed = getdiffcmd(fin, false, fout, &dc)))
66711894Speter		if (ed) {
66811894Speter		    cache(fin);
66911894Speter		    while (dc.nlines--)
67011894Speter			do {
67111894Speter			    cachegeteof_(c, { if (!dc.nlines) goto OK_EOF; unexpected_EOF(); })
67211894Speter			    if (c == SDELIM)
67311894Speter				aputc_(SDELIM, fout)
67411894Speter			    aputc_(c, fout)
67511894Speter			} while (c != '\n');
67611894Speter		    uncache(fin);
67711894Speter		}
67811894Speter	}
67911894Speter    OK_EOF:
68011894Speter	aprintf(fout, "%c\n", SDELIM);
68111894Speter}
682