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