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