cvsclone.l
[cvsclone.git] / cvsclone.l
blob4b80079481aded9a9441968b2f6fd0ac29351e05
1 /* cvsclone.l */
3 /*
4  * Copyright (C) 2006  Peter Backes <rtc@gmx.de>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  * 
20  * BUILDING
21  *
22  * flex cvsclone.l && gcc -Wall -O2 lex.yy.c -o cvsclone
23  *
24  * EXAMPLE
25  *
26  * *-------------------------------------------------------------------*
27  * | Applying this tool to sourceforge.net or savannah.gnu.org is      |
28  * | neither necessary nor recommended: With $1 being the project, you |
29  * | can simply                                                        |
30  * |      rsync -av rsync://$1.cvs.sourceforge.net/cvsroot/$1/ $1      |
31  * | or                                                                |
32  * |           rsync -av rsync://cvs.sv.gnu.org/sources/$1 $1          |
33  * | respectively (try also 'web' instead of 'sources').               |
34  * *-------------------------------------------------------------------*
35  *
36  * cvsclone -d :pserver:anonymous@cvs.example.org:/var/lib/cvs module
37  *
38  * DESCRIPTION
39  * 
40  * Utility to clone cvs repositories over the cvspserver interface.  Works
41  * for anonymous access.
42  * 
43  * FEATURES
44  *
45  * - reads $HOME/.cvspass
46  *
47  * - can clone corrupt repositories: writes ,v files directly, does not
48  *   need rcs.  (For example, ccvs module has archives that go backwards
49  *   in time.)
50  *
51  * PROBLEMS
52  *
53  * - can't enable compression.
54  *
55  * - reading cvs password from $HOME/.cvspass uses CVSROOT in a 
56  *   case sensitive way.
57  *
58  * - rlog format is ambiguous.  If the separators it uses are found inside 
59  *   log messages, possibly followed by lines similar to what rlog
60  *   outputs, things can go wrong horribly.
61  *
62  * - rcs 5.x rlog format does not contain the comment leader.  It is 
63  *   guessed according to the extension as rcs and CVS do.
64  *
65  * - uses normal diff format since this is the easiest one that works.
66  *   diff --rcs is problematic, since files without newline at the
67  *   last line are not output correctly.  The major drawback about this
68  *   is that deleted lines are transfered while they don't need to be.
69  *   even rdiff has major problems with lines that contain \0, because
70  *   of a bug in cvs.
71  * 
72  * - does not work incrementally.  That would be much more work if
73  *   updating the trunk since the most recent revision had to be 
74  *   reconstructed.  Also, the whole history probably had to be transfered 
75  *   again, with all log messages.
76  * 
77  * - Horrible complexity.  A file with n deltas takes O(n^2) to transfer.
78  *
79  * - Makes the cvs server really work hard, taking up all processor time.
80  *   It should really not be used on public cvs servers, especially
81  *   not on a regular basis.  Perhaps it is useful for salvaging 
82  *   archive files from projects where only access to anonymous cvs 
83  *   is available.
84  *
85  *
86  * Patches and comments are welcome.
87  *
88  */
90 #include <sys/stat.h>
91 #include <sys/types.h>
92 #include <sys/socket.h>
93 #include <netdb.h>
94 #include <resolv.h>
95 #include <unistd.h>
96 #include <fcntl.h>
98 #include <errno.h>
99 #include <ctype.h>
100 #include <assert.h>
101 #include <stdlib.h>
102 #include <time.h>
103 #include <stdio.h>
104 #include <string.h>
105 #ifdef _IO_getc_unlocked
106 #undef getc
107 #define getc _IO_getc_unlocked
108 #endif
110 %s HDR0 HDR1 DESC ACCL0 TAGL0 TAGL1 LCKL0 LCKL1 REV0 REV1 REV2 ATR0 ATR1
111 %s REV3 REV4 RLST XREV FEND SRV0 SRVA SRVB SRV1 SRV2 SRV3 SRV4 SRV5 SRV6 PWF0
112 %option noyywrap nounput
113 num     [0-9.]+
114 special [$,.:;@]
115 idchar  [^$,.:;@ \b\t\v\f\r\n]
116 ws      [ \b\t\v\f\r]
117 id      {num}?{idchar}({idchar}|{num})*
118 sym     [0-9]?{idchar}({idchar}|[0-9])*
119 xid     ({idchar}|{special}|{ws})+
120 yid     ({idchar}|{special})+
121 nws     (ws|\n)
122 date1   [0-9]+(-[0-9]{2}){2}\ [0-9]{2}(:[0-9]{2}){2}\ [-+][0-9]{4}
123 date2   [0-9]+(\/[0-9]{2}){2}\ [0-9]{2}(:[0-9]{2}){2}
125         /*
126         RCS file: x,v
127         Working file: x
128         head: 1.2
129         branch: 1.1
130         locks: [strict]
131                 rtc: 1.1
132                 rtc: 1.1.2.1
133         access list:
134                 root
135                 rtc
136         symbolic names:
137                 test: 1.1
138         keyword substitution: kv
139         total revisions: ; selected\ revisions:
140         description: 
143         file()          : trunk(Head) tree(Head)
144                         ;
145         trunk(p)        :
146                         | adelta(p) trunk(p->next)
147                         ;
148         tree(root)      :
149                         | tree(root->next) forest(root->branches)
150                         ;
151         forest(broot)   :
152                         | forest(broot->nextbranch) abranch(broot) tree(broot)
153                         ;
154         abranch(root)   :
155                         | abranch(root->next) adelta(root)
156                         ;
158         adelta  :
159                 "----------------------------\n"
160                 "revision %s" [ "\tlocked by: %s;" ] "\n"
162                 "date: %s;  author: %s;  state: %s; lines: +%ld -%ld"
163                 [ "; kopt: %s" ]
164                 [ "; commitid: %s" ]
165                 [ "; filename: %s" ]
166                 [ "; mergepoint:" ("  %s;")+ ]
167                 "\n"
169                 [ "branches:" ("  %s;")+ "\n" ]
170                 [ "included:" ("  %s;")+ ]
171                 [ "excluded:" ("  %s;")+ ]
172                 [ "ignored:" ("  %s;")+ ] "\n"  // '\n' if one of them is there
173                 [ "source: %s" [ "\t%s" ] "\n" ]
175                 <log>*"\n"
176          */
178 char *getstr(unsigned long n, char *s)
180         s[n] = '\0';
181         return s;
183 union revref;
184 struct rev {
185         char *num;
186         time_t date;
187         char *author, *state, *comment, *commitid, *kopt;
188         int ladd, ldel;
189         /* mergepoint */
190         /* branches */
191         /* included */
192         /* excluded */
193         /* ignored */
194         char *server,  /* originating server */
195                 *onum; /* original number */
196         char *log;
197         struct rev *next, *branch, *sub;
199 union revref {
200         char *num;
201         struct rev *r;
203 struct rpair {
204         char *item;
205         union revref rev;
207 struct rcsfile {
208         char *source, *workfile;
209         union revref head, branch;
210         int strict;
211         char *ksub, *leader;
212         int tot, sel;
213         char *descr;
214         struct rpair *lckl, *lckt, *tagl, *tagt;
215         char **accl, **acct; /* access list, access top */
216         struct rev *revl, *revt;
217 } rfile;
219 void rcsinit(void)
221         rfile.source = rfile.workfile = rfile.head.num
222                 = rfile.branch.num = rfile.ksub = rfile.leader
223                 = rfile.descr = NULL;
224         rfile.strict = rfile.tot = rfile.sel = 0;
225         rfile.lckl = rfile.lckt = rfile.tagl = rfile.tagt = NULL;
226         rfile.revl = rfile.revt = NULL;
227         rfile.accl = rfile.acct = NULL;
230 void addaccl(char *s)
232         if (!rfile.accl)
233                 rfile.acct = rfile.accl = malloc(16 * sizeof *rfile.accl);
234         else if (!((rfile.acct - rfile.accl) % 16)) {
235                 unsigned long n = rfile.acct - rfile.accl;
236                 rfile.accl = realloc(rfile.accl, (n+16) * sizeof *rfile.accl);
237                 rfile.acct = rfile.accl + n;
238         }
239         
240         *rfile.acct++ = s;
242 void addtag(char *s)
244         if (!rfile.tagl)
245                 rfile.tagt = rfile.tagl = malloc(16 * sizeof *rfile.tagl);
246         else if (!((rfile.tagt - rfile.tagl) % 16)) {
247                 unsigned long n = rfile.tagt - rfile.tagl;
248                 rfile.tagl = realloc(rfile.tagl, (n+16) * sizeof *rfile.tagl);
249                 rfile.tagt = rfile.tagl + n;
250         }
251         
252         rfile.tagt++->item = s;
254 void addlck(char *s)
256         if (!rfile.lckl)
257                 rfile.lckt = rfile.lckl = malloc(16 * sizeof *rfile.lckl);
258         else if (!((rfile.lckt - rfile.lckl) % 16)) {
259                 unsigned long n = rfile.lckt - rfile.lckl;
260                 rfile.lckl = realloc(rfile.lckl, (n+16) * sizeof *rfile.lckl);
261                 rfile.lckt = rfile.lckl + n;
262         }
263         
264         rfile.lckt++->item = s;
266 void setdate(unsigned long n, char *s, char *t)
268         char d;
269         struct tm tm;
270         sscanf(s, "%d%c%d%c%d %d:%d:%d", &tm.tm_year, &d, &tm.tm_mon, &d,
271                 &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec);
272         tm.tm_year -= 1900;
273         tm.tm_mon--;
274         if (t) {
275                 int z = atoi(t);
276                 if (z < 0)
277                         tm.tm_sec += -z / 100 * 3600 + -z % 100 * 60;
278                 else
279                         tm.tm_sec -= z / 100 * 3600 + z % 100 * 60;
280         }
281         rfile.revt->date = timegm(&tm);
282 #if 0
283         printf("setdate: %.*s, %.5s --> %s", (int)n, s, t, 
284                 ctime(&rfile.revt->date));
285 #endif
286         
288 void addrev(char *s)
290         if (!rfile.revt)
291                 rfile.revt = rfile.revl;
292         else 
293                 rfile.revt++;
294         assert(rfile.revl && rfile.revt < rfile.revl + rfile.sel);
295         /* assert(rfile.revt - rfile.revl < rfile.n) */
296         rfile.revt->num = s;
297         rfile.revt->author = rfile.revt->state
298                 = rfile.revt->comment = rfile.revt->commitid 
299                 = rfile.revt->kopt = rfile.revt->server
300                 = rfile.revt->onum = rfile.revt->log = NULL;
301         rfile.revt->next = rfile.revt->branch = rfile.revt->sub = NULL;
302         rfile.revt->ladd = rfile.revt->ldel = 0;
304 void addrl(char *s)
307 void initrev(int n)
309         if ((rfile.sel = n))
310                 rfile.revl = malloc(n * sizeof *rfile.revl);
312 char *cvsroot, *cvspass, *cvsuser, *cvshost, *cvsdir;
313 int chkroot(char *s, size_t le, int s1)
315         size_t l = strlen(cvsroot);
316         
317         if (s1) {
318                 char *x, *y;
319 #if 0
320                 fprintf(stderr, "cvsroot=%s, s=%s, s1=%d\n", cvsroot, s, s1);
321 #endif
322                 if (*s != ':' || !(x = strchr(s + 1, ':'))
323                  || !(x = strchr(x + 1, ':'))
324                  || strncmp(cvsroot, s, x - s + 1)
325                  || !isdigit(*(x + 1)))
326                         return 0;
327 #if 0
328                 fprintf(stderr, "survived\n");
329 #endif
330                 y = cvsroot + (x - s) + 1;
331                 while (isdigit(*++x))
332                         continue;
333                 if (strncmp(y, x, l - (y - cvsroot)) 
334                  || x[l - (y - cvsroot)] != ' ')
335                         return 0;
336                 l += x - s - (y - cvsroot);
337 #if 0
338                 fprintf(stderr, "l=%u(%d, %u), s=%s, cvsroot=%s, x=%s, y=%s\n", 
339                         l, x - s - (y - cvsroot), le,
340                         s, cvsroot, x, y);
341 #endif
342         } else if (strncmp(cvsroot, s, l) || s[l] != ' ')
343                 return 0;
344         
345         cvsroot = strncpy(realloc(cvsroot, le + 1), s, le);
346         cvsroot[l] = cvsroot[le+1] = '\0';
347         cvspass = cvsroot + l + 1;
348         return 1;
350 FILE *rcsfiop;
351 void rcsfwrite(char *s, size_t l, FILE *stream)
353         for (; l--; putc(*s++, stream))
354                 if (*s == '@')
355                         putc(*s, stream);
357 void rcsrang(int d0, int d1, int a0, int a1)
359         fprintf(rcsfiop, "d%d %d\na%d %d\n", d0, d1-d0+1, d1, a1-a0+1);
361 void rcsrang2(int d0, int d1, int a)
363         fprintf(rcsfiop, "d%d %d\n", d0, d1-d0+1);
365 void rcsrang3(int d, int a0, int a1)
367         fprintf(rcsfiop, "a%d %d\n", d, a1-a0+1);
369 char *queue, *que;
370 size_t queuel;
371 char *getq(size_t l)
373         if (!queue)
374                 return queue = malloc(queuel = l);
375         queuel += l;
376         queue = realloc(queue, queuel);
377         return queue + queuel - l;
379 void begin(char *s, size_t l)
381         char *p = s + 10, *fi, *fi2;
382         size_t l0 = strlen(cvsdir);
383         s[l-1] = '\0';
384         if (strncmp(cvsdir, p, l0) || p[l0] != '/') {
385                 fprintf(stderr, "path mismatch.\n");
386                 return;
387         }
388         fi = p + l0 + 1;
389         while ((fi2 = strchr(fi, '/'))) {
390                 *fi2 = '\0';
391                 mkdir(p + l0 + 1, 0777);
392                 *fi2 = '/';
393                 fi = fi2 + 1;
394         }
395         strcpy(getq(strlen(p + l0 + 1) + 1), p + l0 + 1);
396         if (!(rcsfiop = fopen(p + l0 + 1, "w")))
397                 return;
398         s[l-1] = '\n';
399         fputs("\n", rcsfiop);
400         fwrite(s, 1, l, rcsfiop);
406 <HDR0>RCS\ file:\ {xid}\n rfile.source = getstr(yyleng-11, yytext+10);
407 <HDR0>Working\ file:\ {xid}\n rfile.workfile = getstr(yyleng-15, yytext+14);
408 <HDR0>head:\ {num}\n    rfile.head.num = getstr(yyleng-7, yytext+6);
409 <HDR0>head:\n           rfile.head.num = NULL;
410 <HDR0>branch:\n         rfile.branch.num = NULL;
411 <HDR0>branch:\ {num}\n  rfile.branch.num = getstr(yyleng-9, yytext+8);
412 <HDR0>locks:\ strict\n  BEGIN LCKL0; rfile.strict = 1; 
413 <HDR0>locks:\n          BEGIN LCKL0; rfile.strict = 0; 
414 <HDR0>access\ list:\n   BEGIN ACCL0;
415 <HDR0>symbolic\ names:\n BEGIN TAGL0;
416 <HDR0>keyword\ substitution:\ .*\n rfile.ksub = getstr(yyleng-23, yytext+22); 
417 <HDR0>comment\ leader:\ \".*\"\n rfile.leader = getstr(yyleng-19, yytext+17);
418 <HDR0>total\ revisions:\ [0-9]+;\t BEGIN HDR1; rfile.tot = atoi(yytext+17);
419 <HDR1>selected\ revisions:\ [0-9]+\n BEGIN HDR0; initrev(atoi(yytext+20));
420 <HDR0>description:\n    BEGIN DESC;
421 <HDR0>\n                /* EMPTY */
422 <DESC>={77}\n           BEGIN FEND; *yytext = '\0'; if (!rfile.descr) rfile.descr = yytext;
423 <DESC>-{28}\n           BEGIN REV0; *yytext = '\0'; if (!rfile.descr) rfile.descr = yytext;
424 <DESC>.*\n              if (!rfile.descr) rfile.descr = yytext;
425 <ACCL0>\t{id}\n         addaccl(getstr(yyleng-2, yytext+1));
426 <ACCL0>""/[^\t]         BEGIN HDR0;
427 <TAGL0>\t{sym}:\        BEGIN TAGL1; addtag(getstr(yyleng-3, yytext+1)); 
428 <TAGL0>""/[^\t]         BEGIN HDR0;
429 <TAGL1>{num}\n          BEGIN TAGL0; rfile.tagt[-1].rev.num = getstr(yyleng-1, yytext); 
430 <LCKL0>\t{id}:\         BEGIN LCKL1; addlck(getstr(yyleng-3, yytext+1)); 
431 <LCKL0>""/[^\t]         BEGIN HDR0;
432 <LCKL1>{num}\n          BEGIN LCKL0; rfile.lckt[-1].rev.num = getstr(yyleng-1, yytext);
433 <REV0>revision\ {num}\n BEGIN REV2; addrev(getstr(yyleng-10, yytext+9));
434 <REV0>revision\ {num}\t BEGIN REV1; addrev(getstr(yyleng-10, yytext+9));
435 <REV1>locked\ by:\ {id};\n BEGIN REV2; /* getstr(yyleng-13, yytext+11); */
436 <REV2>date:\ {date1};\ \  BEGIN ATR0; setdate(yyleng-15, yytext+6, yytext+yyleng-8);
437 <REV2>date:\ {date2};\ \  BEGIN ATR0; setdate(yyleng-9, yytext+6, NULL);
438 <ATR0>author:\ {id};\ \  rfile.revt->author = getstr(yyleng-11, yytext+8);
439 <ATR0>state:\ {id};\ \  rfile.revt->state = getstr(yyleng-10, yytext+7);
440 <ATR0>state:\ {id};\n   BEGIN REV3; rfile.revt->state = getstr(yyleng-9, yytext+7);
441 <ATR0>lines:\ \+[0-9]+\  BEGIN ATR1; rfile.revt->ladd = atoi(yytext+8);
442 <ATR1>-[0-9]+\n         BEGIN REV3; rfile.revt->ldel = atoi(yytext+1);
443 <ATR1>-[0-9]+;\ \       BEGIN ATR0; rfile.revt->ldel = atoi(yytext+1);
444 <ATR0>kopt:\ [^;\n]+;\n BEGIN REV3; rfile.revt->kopt = getstr(yyleng-8, yytext+6);
445 <ATR0>kopt:\ [^;\n]+;\ \  rfile.revt->kopt = getstr(yyleng-9, yytext+6);
446 <ATR0>commitid:\ [0-9a-f]{16};\ \  rfile.revt->commitid = getstr(16, yytext+10);
447 <ATR0>commitid:\ [0-9a-f]{16};\n BEGIN REV3; rfile.revt->commitid = getstr(16, yytext+10);
448 <ATR0>mergepoint:\ {num};\n BEGIN REV3; /* getstr(yyleng-14, yytext+12); */
449 <REV3>branches:/.*\n    BEGIN RLST;
450 <REV3,REV4>={77}\n      BEGIN FEND; *yytext = '\0'; if (!rfile.revt->log) rfile.revt->log = yytext; if (rfile.revt) rfile.revt++;
451 <REV3,REV4>-{28}\n      BEGIN REV0; *yytext = '\0'; if (!rfile.revt->log) rfile.revt->log = yytext;
452 <REV3>.*\n              BEGIN REV4; assert(!rfile.revt->log); rfile.revt->log = yytext;
453 <RLST>\ \ {num};        addrl(getstr(yyleng-3, yytext+2));
454 <RLST>\n                BEGIN REV3;
455 <REV4>.*\n              if (!rfile.revt->log) rfile.revt->log = yytext;
456 <FEND><<EOF>>           return EOF;
458 <SRV0>M\ \n             BEGIN SRVA;
459 <SRVA>M\ RCS\ file:\ {xid}\n BEGIN SRVB; begin(yytext+2, yyleng-2);
460 <SRVB>M\ ={77}\n        BEGIN SRV0; fwrite(yytext+2, 1, yyleng-2, rcsfiop); fclose(rcsfiop);
461 <SRVB>M\ .*\n           fwrite(yytext+2, 1, yyleng-2, rcsfiop);
462 <SRV0,SRV1,SRV2,SRV3,SRV4,SRV5,SRV6>E\ .*\n fwrite(yytext+2, 1, yyleng-2, stderr);
463 <SRV0>I\ LOVE\ YOU\n    fprintf(stderr, "%s", yytext);
464 <SRV0>error\ [^ \n]*\ .*\n return 1;
465 <SRV0>ok\n              return 0;
466 <SRV0><<EOF>>           return EOF;
467 <SRV1>M\ <\ .*\n        BEGIN SRV2; rcsfwrite(yytext+4, yyleng-5, rcsfiop);
468 <SRV2>M\ <\ .*\n        yytext[3] = '\n'; rcsfwrite(yytext+3, yyleng-4, rcsfiop);
469 <SRV2>M\ \\\ .*\n       BEGIN SRV3;
470 <SRV2,SRV5>ok\n         putc('\n', rcsfiop); return 0;
471 <SRV2,SRV5>error\ [^ \n]*\ .*\n putc('\n', rcsfiop); return 1;
472 <SRV3,SRV1,SRV4,SRV6>ok\n return 0;
473 <SRV3,SRV1,SRV4,SRV6>error\ [^ \n]*\ .*\n return 1;
474 <SRV4>M\ [0-9]+,[0-9]+c[0-9]+,[0-9]+\n rcsrang(atoi(yytext+2), atoi(strchr(yytext, ',')+1), atoi(strchr(yytext, 'c')+1), atoi(strrchr(yytext, ',')+1));
475 <SRV4>M\ [0-9]+c[0-9]+,[0-9]+\n rcsrang(atoi(yytext+2), atoi(yytext+2), atoi(strchr(yytext, 'c')+1), atoi(strrchr(yytext, ',')+1));
476 <SRV4>M\ [0-9]+,[0-9]+c[0-9]+\n rcsrang(atoi(yytext+2), atoi(strchr(yytext, ',')+1), atoi(strchr(yytext, 'c')+1), atoi(strchr(yytext, 'c')+1));
477 <SRV4>M\ [0-9]+c[0-9]+\n   rcsrang(atoi(yytext+2), atoi(yytext+2), atoi(strchr(yytext, 'c')+1), atoi(strchr(yytext, 'c')+1));
478 <SRV4>M\ [0-9]+,[0-9]+d[0-9]+\n rcsrang2(atoi(yytext+2), atoi(strchr(yytext, ',')+1), atoi(strchr(yytext, 'd')+1));
479 <SRV4>M\ [0-9]+d[0-9]+\n   rcsrang2(atoi(yytext+2), atoi(yytext+2), atoi(strchr(yytext, 'd')+1));
480 <SRV4>M\ [0-9]+a[0-9]+,[0-9]+\n rcsrang3(atoi(yytext+2), atoi(strchr(yytext, 'a')+1), atoi(strchr(yytext, ',')+1));
481 <SRV4>M\ [0-9]+a[0-9]+\n   rcsrang3(atoi(yytext+2), atoi(strrchr(yytext, 'a')+1), atoi(strchr(yytext, 'a')+1));
482 <SRV4>M\ >\ .*\n        BEGIN SRV5; rcsfwrite(yytext+4, yyleng-5, rcsfiop);
483 <SRV5>M\ >\ .*\n        yytext[3] = '\n'; rcsfwrite(yytext+3, yyleng-4, rcsfiop);
484 <SRV5>""/M\ [0-9]       BEGIN SRV4; putc('\n', rcsfiop);
485 <SRV5>M\ \\\ .*\n       BEGIN SRV6;
486 <SRV1,SRV4>M\ RCS\ file:.*\n |
487 <SRV1,SRV4>M\ ========.*\n |
488 <SRV1,SRV4>M\ retrieving.*\n |
489 <SRV1,SRV4>M\ diff.*\n  |
490 <SRV1,SRV4>M\ Index:.*\n |
491 <SRV1>M\ [0-9]+(,[0-9]+)?d[0-9]+\n |
492 <SRV4>M\ \\\ .*\n       |
493 <SRV4>M\ (<\ .*|---)\n  /* ignore */
494 <SRV5>([^M\n]|M([^ \n]|\ [^0-9\n])).*\n? |
495 <SRV5>M\ ?\n?           |
496 <SRV0,SRV1,SRV2,SRV3,SRV4,SRV6>.*\n? fwrite(yytext, 1, yyleng, stderr);
498 <PWF0>\/1\ [^ \n]+\ .*\n if (chkroot(yytext+3, yyleng-4, 1)) return 0;
499 <PWF0>[^ \n]+\ .*\n     if (chkroot(yytext, yyleng-1, 0)) return 0;
500 <PWF0>.*\n?             /* ignore unknown lines */
501 <PWF0><<EOF>>           return EOF;
503 <HDR0>[^t\n].*\n?       |
504 <HDR0>t[^\t\n]*         |
505 <TAGL0,LCKL0>\t[^:\n]*:?\n? |
506 <TAGL0,LCKL0>\t[^:\n]*:[^ ].*\n? |
507 <TAGL0,LCKL0>\t[^:\n]*:\  |
508 <ACCL0>\t.*\n?          |
509 <REV0>[^\t\n]*[\t\n]?   |
510 <REV2>[^;\n]*(""|;\ ?)  |
511 <ATR0>[^l\n][^;\n]*     |
512 <ATR0>[^l\n][^;\n]*;\ ? |
513 <ATR0>(\n|l[^ \n]*)     |
514 <ATR0>lines:\ [^ \n]*   |
515 <ATR1,RLST>[^;\n]*      |
516 <ATR1>[^;\n]*;\ ?       |
517 <HDR1,DESC,TAGL1>.*\n?  |
518 <LCKL1,REV1,REV3>.*\n?  |
519 <REV4,XREV,SRVA>.*\n?   |
520 <SRVB>.*\n?             |
521 (.|\n)                  printf("Unmached: <%.*s>\n", yyleng, yytext);
524 static YY_BUFFER_STATE yybuf;
525 /* Read rlog to buffer */
526 size_t rlread(char *ptr, size_t nmemb, FILE *stream)
528         char *top = ptr;
529         static int state;
530         while (nmemb--) {
531                 register int c = getc(stream);
533                 if (c == EOF) {
534                         state = 0;
535                         return ptr - top;
536                 }
537                 *ptr++ = c;
538                 if (c == '\n') {
539                         if (state == 77) {
540                                 state = 0;
541                                 return ptr - top;
542                         }
543                         state = 0;
544                 } else if (state < 78) {
545                         if (c == '=')
546                                 state++;
547                         else
548                                 state = 78;
549                 }
550         }
551         return ptr - top;
554 void *ftobuf(FILE *stream, unsigned long *s)
556         unsigned long siz = 0;
557         void *buf = NULL;
558         do {
559                 buf = realloc(buf, siz + BUFSIZ + 2);
560                 siz += fread(buf + siz, 1, BUFSIZ, stream);
561                 /*siz += rlread(buf + siz, BUFSIZ, stream);*/
562         } while (siz && !(siz % BUFSIZ));
563         *s = siz += 2;
565         if (*s == 2) {
566                 free(buf);
567                 return NULL;
568         }
569         return realloc(buf, siz);
572 char *strfix(char *s)
574         unsigned long l = strlen(s);
575         if (!l)
576                return s;
577         if (s[l-1] == '\n')
578                 s[l-1] = '\0';
579         return s;       
581 FILE *srv;
583 FILE *srvopen(void)
585 #ifndef XTEST
586         struct hostent *host;
587         struct servent *serv;
588         struct sockaddr_in sin;
589         int fd;
590 #endif
591         char *cvsport = NULL;
592         if (strncmp(cvsroot, ":pserver:", 9)
593          || !(cvsdir = strchr(cvsuser = cvsroot + 9, ':'))
594          || !(cvshost = strchr(cvsuser, '@')) || cvshost > cvsdir)
595                 return NULL;
596         *cvshost++ = *cvsdir++ = '\0';
597         if (isdigit(*cvsdir)) {
598                 cvsport = cvsdir;
599                 while (isdigit(*++cvsdir))
600                         continue;
601         }
602 #ifndef XTEST
603         memset(&sin, '\0', sizeof sin);
604         if (cvsport)
605                 sin.sin_port = htons(atoi(cvsport));
606         else if (!(serv = getservbyname("cvspserver", "tcp")))
607                 return NULL;
608         else
609                 sin.sin_port = serv->s_port;
610         if (!(host = gethostbyname(cvshost))
611          || (fd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
612                 return NULL;
613         sin.sin_family = AF_INET;
614         memcpy(&sin.sin_addr, host->h_addr, host->h_length);
615         if (connect(fd, (struct sockaddr *)&sin, sizeof sin) == -1) {
616                 close(fd);
617                 return NULL;
618         }
619         return fdopen(fd, "w+");
620 #else
621         return NULL;
622 #endif
624 char *getpar(char *s)
626         char *p = NULL, *q = NULL;
627         while (*s)
628                 if (*s == '.')
629                         p = q, q = s++;
630                 else
631                         s++;
632         return p;
635 struct rev *range(struct rev *begin)
637         char *brb, *bre;
638         if (begin >= rfile.revt)
639                 return NULL;
640         brb = begin->num;
641         bre = strrchr(brb, '.');
642         if (!bre || bre == strchr(brb, '.'))
643                 bre = brb;
644         else
645                 bre++;
646         /*fprintf(stderr, "brb: %s, n: %u\n", brb, bre - brb);*/
647         while (begin < rfile.revt 
648             && (bre - brb ? !strncmp(begin->num, brb, bre - brb)
649                   && !strchr(begin->num + (bre - brb), '.')
650                 : strchr(begin->num, '.') == strrchr(begin->num, '.')))
651                   /* works fine even if no '.' present */
652                 begin++;
653         return begin;
659  |  
660 1.3 | next
661  |  v
662 1.2   branch
663  |   ------>      
664 1.1--1.1.1.1--1.1.1.1.1.1 - -
665         |   
666      1.1.2.1 | sub
667         |    v
668      1.1.3.1
669         :
672 next            branch          sub              T
673 v               -                                /1.2
674 *               | -                              |1.1*
675                 : :                              : F B
676         *       | |             ->               | / <1.1.2.1*
677                 : :             :                : : 
678         *       | |       -     |                | | /1.1.1.2
679         ^       | ->    - |     -                | | |1.1.1.1*
680                 :       : :                      : : : F B 
681         *       |       | |                      | | | / /1.1.1.2.1.2
682         ^       |       | ->                     | | | \ \1.1.1.2.1.1*
683                 :       :                        : : : F B
684         *       |       |                        | | | / /1.1.1.1.1.2
685         ^       |       ->                       | \ \ \ \1.1.1.1.1.1*
686                 :                                :   B
687         *       |                                | / /1.2.1.2
688         ^       ->                               \ \ \1.2.1.1*
691 int prefix(const char *rev, const char *subrev)
693         size_t l = strlen(rev);
694         return !strncmp(rev, subrev, l) && subrev[l] == '.';
697 /* parse forest belonging to revision x */
698 struct rev *forest(struct rev *x, struct rev **b)
700         struct rev *z = NULL;
701         do {
702                 struct rev *branch(struct rev **b);
703                 struct rev *y = branch(b);
704                 y->sub = z;
705                 z = y;
706         } while (*b < rfile.revt && prefix(x->num, (*b)->num));
707         return z;
709 /* parse branch */
710 struct rev *branch(struct rev **b)
712         struct rev *e = range(*b), *x, *y = *b;
713         (*b)->next = NULL;
714         for (x = *b; x < e - 1; x++)
715                 x[1].next = x;
716         /* As for the trunk, for each branch revision, a forest can follow.
717          * However here the forests in decreasing order match the order 
718          * of the branch revisions.
719          */
720         while (e < rfile.revt) {
721                 while (y <= x && !prefix(y->num, e->num))
722                         y++->branch = NULL;
723                 if (y > x)
724                         break;
725                 y->branch = forest(y, &e);
726                 y++;
727         }
728         *b = e;
729         return x;
732 /* parse trunk */
733 struct rev *trunk(struct rev *b)
735         struct rev *e = b ? range(b) : NULL, *x, *y = e;
736         if (!e)
737                 return NULL;
738         for (x = b; x < e - 1; x++)
739                 x->next = x + 1;
740         x->next = NULL;
741         /* For each trunk revision, a forest can follow.
742          * The forests are in increasing order, in contrast to the 
743          * trunk revisions, which are in decreasing order.
744          */
745         while (e < rfile.revt) {
746                 /* search the trunk revision this forest belongs to */
747                 while (y > b && !prefix((--y)->num, e->num))
748                         y->branch = NULL;
749                 assert(y > b || prefix(y->num, e->num));
750                 /* parse the forest, set branch of the trunk revision */
751                 y->branch = forest(y, &e);
752         }
753         return b;
756 struct {
757         int ndeltas, nfiles, nbranches;
758 } stats;
760 void rcsfputs(const char *s, FILE *stream)
762         for (putc('@', stream); *s; putc(*s++, stream))
763                 if (*s == '@')
764                         putc(*s, stream);
765         putc('@', stream);
767 void puttree(struct rev *x, FILE *stream)
769         struct rev *y, *z;
770         void putforest(struct rev *x, FILE *stream);
771         for (y = x; y; y = y->next) {
772                 struct tm *tm;
773                 tm = gmtime(&y->date);
774                 fprintf(stream, "%s\n", y->num);
775                 fprintf(stream, "date\t%d.%02d.%02d.%02d.%02d.%02d;"
776                         "\tauthor %s;\tstate %s;\n", 
777                         tm->tm_year > 99 ? tm->tm_year + 1900 : tm->tm_year,
778                         tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min,
779                         tm->tm_sec, y->author, y->state);
780                 fprintf(stream, "branches");
781                 for (z = y->branch; z; z = z->sub)
782                         fprintf(stream, "\n\t%s", z->num);
783                         
784                 fprintf(stream, ";\n");
785                 fprintf(stream, "next\t%s;\n", y->next ? y->next->num : "");
786                 fprintf(stream, "\n");
787         }
788         putforest(x, stream);
790 void putforest(struct rev *x, FILE *stream)
792         struct rev *z;
793         if (!x)
794                 return;
795         putforest(x->next, stream);
796         for (z = x->branch; z; z = z->sub)
797                 puttree(z, stream);
799 void unqueue(void)
801         ssize_t s;
802         if (que == queue + queuel)
803                 return;
804         fcntl(fileno(srv), F_SETFL, O_NONBLOCK);
805         if ((s = write(fileno(srv), que, queue + queuel - que)) != -1)
806                 que += s;
807         fcntl(fileno(srv), F_SETFL, 0L);
809 char *fi, *fi2;
810 void puttree2(struct rev *z, struct rev *x)
812         struct rev *y;
813         for (y = x; y; y = y->next) {
814                 void putfor2(struct rev *z, struct rev *x);
815                 
816                 if (!z) {
817                         for (z = y; z && !strcmp(z->state, "dead"); z = z->next)
818                                 continue;
819                         if (z) {
820                                 sprintf(getq(10*4+5+2+3+2+strlen(z->num)
821                                         + (fi2 - strrchr(fi, '/')) - 1), 
822                                         "Argument -r%s\n"
823                                         "Argument -aN\n"
824                                         "Argument --\n"
825                                         "Argument %.*s\n"
826                                         "diff", z->num, 
827                                         fi2 - strrchr(fi, '/') - 1,
828                                         strrchr(fi, '/') + 1);
829                                 queue[queuel-1] = '\n';
830                         } else
831                                 /* file was initially added on branch */
832                                 z = y;
833                 } else if (strcmp(y->state, "dead") && z != y) {
834                         sprintf(getq(10*5+5+2+2+2+2+strlen(z->num)
835                                 + strlen(y->num)
836                                 + (fi2 - strrchr(fi, '/'))), 
837                                 "Argument -r%s\n"
838                                 "Argument -r%s\n"
839                                 "Argument -aN\n"
840                                 "Argument --\n"
841                                 "Argument %.*s\n"
842                                 "diff", z->num, y->num, 
843                                 fi2 - strrchr(fi, '/') - 1,
844                                 strrchr(fi, '/') + 1);
845                         queue[queuel-1] = '\n';
846                         z = y;
847                 }
848                 putfor2(z, y->branch);
849         }
851 void putfor2(struct rev *z, struct rev *x)
853         if (!x)
854                 return;
855         putfor2(z, x->sub);
856         puttree2(z, x);
858 void puttree3(struct rev *z, struct rev *x)
860         struct rev *y;
861         for (y = x; y; y = y->next) {
862                 void putfor3(struct rev *z, struct rev *x);
863                 fprintf(stderr, "%s ", y->num);
864                 
865                 stats.ndeltas++;
866                 fprintf(rcsfiop, "\n\n%s\nlog\n", y->num);
867                 rcsfputs(y->log, rcsfiop);
868                 fprintf(rcsfiop, "\ntext\n@");
869                 
870                 if (!z) {
871                         for (z = y; z && !strcmp(z->state, "dead"); z = z->next)
872                                 continue;
873                         if (z) {
874                                 unqueue();
875                                 BEGIN SRV1;
876                                 yylex();
877                         } else
878                                 /* file was initially added on branch */
879                                 z = y;
880                 } else if (strcmp(y->state, "dead") && z != y) {
881                         unqueue();
882                         BEGIN SRV4;
883                         yylex();
884                         z = y;
885                 }
886                 fprintf(rcsfiop, "@\n");
887                 putfor3(z, y->branch);
888         }
890 void putfor3(struct rev *z, struct rev *x)
892         if (!x)
893                 return;
894         stats.nbranches++;
895         putfor3(z, x->sub);
896         puttree3(z, x);
899 /* table of comment leader pairs, merged from RCS and CVS */
900 static const struct clpair {
901         char *suffix, *comlead;
902 } cltbl[] = {
903         { "a"   , "-- " },      /* Ada */
904         { "ada" , "-- " },      
905         { "adb" , "-- " },      
906         { "ads" , "-- " },      
907         { "asm" , ";; " },      /* assembler (MS-DOS) */
908         { "bas" , "' "  },      /* Visual Basic code */
909         { "bat" , ":: " },      /* batch (MS-DOS) */
910         { "body", "-- " },      /* Ada */
911         { "c"   , " * " },      /* C */
912         { "c++" , "// " },      /* C++ in all its infinite guises */
913         { "cc"  , "// " },      
914         { "cpp" , "// " },      
915         { "cxx" , "// " },      
916         { "cl"  , ";;; "},      /* Common Lisp */
917         { "cmd" , ":: " },      /* command (OS/2) */
918         { "cmf" , "c "  },      /* CM Fortran */
919         { "cs"  , " * " },      /* C* */
920         { "csh" , "# "  },      /* shell */
921         { "dlg" , " * " },      /* MS Windows dialog file */
922         { "e"   , "# "  },      /* efl */
923         { "epsf", "% "  },      /* encapsulated postscript */
924         { "epsi", "% "  },      /* encapsulated postscript */
925         { "el"  , "; "  },      /* Emacs Lisp */
926         { "f"   , "c "  },      /* Fortran */
927         { "for" , "c "  },      
928         { "frm" , "' "  },      /* Visual Basic form */
929         { "h"   , " * " },      /* C-header */
930         { "hh"  , "// " },      
931         { "hpp" , "// " },      /* C++ header */
932         { "hxx" , "// " },      
933         { "in"  , "# "  },      /* for Makefile.in */
934         { "l"   , " * " },      /* lex (NOTE: franzlisp disagrees) */
935         { "lisp", ";;; "},      /* Lucid Lisp */
936         { "lsp" , ";; " },      /* Microsoft Lisp */
937         { "m"   , "// " },      /* Objective C */
938         { "mac" , ";; " },      /* macro (DEC-10, MS-DOS, PDP-11, VMS, etc) */
939         { "mak" , "# "  },      /* makefile, e.g. Visual C++ */
940         { "me"  , ".\\\" "},    /* troff -me */
941         { "ml"  , "; "  },      /* mocklisp */
942         { "mm"  , ".\\\" "},    /* troff -mm */
943         { "ms"  , ".\\\" "},    /* troff -ms */
944         { "man" , ".\\\" "},    /* man-macros   t/nroff */
945         { "1"   , ".\\\" "},    /* feeble attempt at man pages... */
946         { "2"   , ".\\\" "},    
947         { "3"   , ".\\\" "},    
948         { "4"   , ".\\\" "},    
949         { "5"   , ".\\\" "},    
950         { "6"   , ".\\\" "},    
951         { "7"   , ".\\\" "},    
952         { "8"   , ".\\\" "},    
953         { "9"   , ".\\\" "},    
954         { "p"   , " * " },      /* Pascal */
955         { "pas" , " * " },      
956         { "pl"  , "# "  },      /* perl (conflict with Prolog) */
957         { "ps"  , "% "  },      /* PostScript */
958         { "psw" , "% "  },      /* postscript wrap */
959         { "pswm", "% "  },      /* postscript wrap */
960         { "r"   , "# "  },      /* ratfor */
961         { "rc"  , " * " },      /* Microsoft Windows resource file */
962         { "red" , "% "  },      /* psl/rlisp */
963 #ifdef sparc
964         { "s"   , "! "  },      /* assembler */
965 #endif
966 #ifdef mc68000
967         { "s"   , "| "  },      /* assembler */
968 #endif
969 #ifdef pdp11
970         { "s"   , "/ "  },      /* assembler */
971 #endif
972 #ifdef vax
973         { "s"   , "# "  },      /* assembler */
974 #endif
975 #ifdef __ksr__
976         { "s"   , "# "  },      /* assembler */
977         { "S"   , "# "  },      /* Macro assembler */
978 #endif
979         { "sh"  , "# "  },      /* shell */
980         { "sl"  , "% "  },      /* psl */
981         { "spec", "-- " },      /* Ada */
982         { "sty" , "% "  },      /* LaTeX style */
983         { "tex" , "% "  },      /* TeX */
984         { "y"   , " * " },      /* yacc */
985         { "ye"  , " * " },      /* yacc-efl */
986         { "yr"  , " * " },      /* yacc-ratfor */
987         { ""    , "# "  },      /* default for empty suffix */
988         { 0     , "# "  }       /* default for unknown suffix; must be last */
991 void gen()
993         size_t l = strlen(cvsdir);
994         char **i, suffix[6] = "";
995         struct rpair *j;
996         fprintf(stderr, "%s\n", rfile.source);
997         fi = strrchr(rfile.source, '/');
998         sprintf(getq(12 + (fi - rfile.source) + 1), 
999                 "Directory .\n%.*s", 
1000                 fi - rfile.source, rfile.source);
1001         queue[queuel-1] = '\n';
1002         fi2 = strrchr(fi, ',');
1003         fi = strrchr(fi, '.');
1004         assert(fi2 && (!fi || fi < fi2));
1005         if (fi) {
1006                 fi++;
1007                 /* If the suffix length is greater than four characters,
1008                  * it cannot match, since it copies five of them.
1009                  */
1010                 strncpy(suffix, fi, fi2 - fi < sizeof suffix - 1 
1011                         ? fi2 - fi : sizeof suffix - 1);
1012         }
1013         fi = rfile.source + l + 1;
1014         rcsfiop = fopen(fi, "w");
1016         fprintf(rcsfiop, "head\t%s;\n", rfile.head.num ? rfile.head.num : "");
1017         if (rfile.branch.num)
1018                 fprintf(rcsfiop, "branch\t%s;\n", rfile.branch.num);
1019         fprintf(rcsfiop, "access");
1020         for (i = rfile.accl; i < rfile.acct; i++)
1021                 fprintf(rcsfiop, "\n\t%s", *i);
1022         fprintf(rcsfiop, ";\n");
1023         fprintf(rcsfiop, "symbols");
1024         for (j = rfile.tagl; j < rfile.tagt; j++)
1025                 fprintf(rcsfiop, "\n\t%s:%s", j->item, j->rev.num);
1026         fprintf(rcsfiop, ";\n");
1027         fprintf(rcsfiop, "locks");
1028         for (j = rfile.lckl; j < rfile.lckt; j++)
1029                 fprintf(rcsfiop, "\n\t%s:%s", j->item, j->rev.num);
1030         fprintf(rcsfiop, ";%s\n", rfile.strict ? " strict;" : "");
1031         fprintf(rcsfiop, "comment\t");
1032         if (rfile.leader)
1033                 rcsfputs(rfile.leader, rcsfiop);
1034         else {
1035                 const struct clpair *curr;
1036                 for (curr = cltbl; curr->suffix; curr++)
1037                         if (!strcmp(curr->suffix, suffix))
1038                                 break;
1039                 rcsfputs(curr->comlead, rcsfiop);
1040         }
1041         fprintf(rcsfiop, ";\n");
1042         if (rfile.ksub && strcmp(rfile.ksub, "kv")) {
1043                 fprintf(rcsfiop, "expand\t");
1044                 rcsfputs(rfile.ksub, rcsfiop);
1045                 fprintf(rcsfiop, ";\n");
1046         }
1047         fprintf(rcsfiop, "\n\n");
1048         trunk(rfile.revl);
1049         stats.nfiles++;
1050         puttree(rfile.revl, rcsfiop);
1051         fprintf(rcsfiop, "\ndesc\n");
1052         rcsfputs(rfile.descr, rcsfiop);
1053         fprintf(rcsfiop, "\n");
1054         puttree2(NULL, rfile.revl);
1055         que = queue;
1056         puttree3(NULL, rfile.revl);
1057         if (queue)
1058                 free(queue);
1059         queue = NULL;
1060         fprintf(stderr, "\n");
1061         fclose(rcsfiop);
1063 char *home;
1064 int main(int argc, char *argv[])
1066         unsigned long siz;
1067         char *buf, *passfile, *fn0, *fn1, *fn2;
1068         
1069         if (argc == 4 && !strcmp(argv[1], "-d")) {
1070                 argc -= 2;
1071                 cvsroot = argv[2];
1072                 argv[1] = argv[3];
1073         } else if (argc != 2) {
1074                 fprintf(stderr, "Argument count.\n");
1075                 return EXIT_FAILURE;
1076         } else
1077                 cvsroot = getenv("CVSROOT");
1079         if (!cvsroot) {
1080                 fprintf(stderr, "No CVSROOT.\n");
1081                 return EXIT_FAILURE;
1082         }
1083         cvsroot = strdup(cvsroot);
1084         passfile = (home = getenv("HOME")) 
1085                 ? strcat(strcpy(malloc(strlen(home) 
1086                         + sizeof "/.cvspass"), home), "/.cvspass")
1087                 : strcpy(malloc(sizeof ".cvspass"), ".cvspass");
1088         BEGIN PWF0;
1089         if (!(yyin = fopen(passfile, "r")) || yylex() == EOF) {
1090                 size_t l = strlen(cvsroot);
1091                 cvsroot = realloc(cvsroot, l + 2);
1092                 cvspass = strcpy(cvsroot + l + 1, "A");
1093         }
1094         if (yyin)
1095                 fclose(yyin);
1096         fprintf(stderr, "password file: %s\n"
1097                 "cvsroot: %s\n"
1098                 "pass: %.1s\n", passfile,
1099                 cvsroot, cvspass);
1101         if (!(srv = srvopen())) {
1102                 fprintf(stderr, "server connection failed or bad cvsroot\n");
1103 #ifndef XTEST
1104                 return EXIT_FAILURE;
1105 #endif
1106         }
1107 #ifndef XTEST
1108         yyrestart(srv);
1109         fprintf(srv, "BEGIN AUTH REQUEST\n"
1110                 "%s\n"
1111                 "%s\n"
1112                 "%s\n"
1113                 "END AUTH REQUEST\n", cvsdir, cvsuser, cvspass);
1114         fprintf(srv, "Root %s\n", cvsdir);
1115         /*fprintf(srv, "Argument micq/m4\n");*/
1116         fprintf(srv, "Argument %s\n", argv[1]);
1117         fprintf(srv, "rlog\n");
1118         fflush(srv);
1119         BEGIN SRV0;
1120         yy_set_interactive(1);
1121         fprintf(stderr, "exit: %d\n", yylex());
1122         fn2 = fn0 = queue;
1123         fn1 = queue + queuel;
1124         queue = NULL;
1125 #else
1126         fn2 = fn0 = malloc(strlen(argv[1]) + 3);
1127         fn1 = strlen(argv[1]) + 3 + strcpy(fn0, argv[1]);
1128         strcat(fn0, ",v");
1129 #endif
1130         while (fn0 < fn1 && (buf = ftobuf(rcsfiop = fopen(fn0, "r"), &siz))) {
1131                 fclose(rcsfiop);
1132                 fn0 += strlen(fn0) + 1;
1133                 buf[siz - 2] = YY_END_OF_BUFFER_CHAR;
1134                 buf[siz - 1] = YY_END_OF_BUFFER_CHAR;
1135                 /* fprintf(stderr, "<<%s>>\n", buf); */
1136                 yybuf = YY_CURRENT_BUFFER;
1137                 yy_scan_buffer(buf, siz);
1138                 yy_set_interactive(0);
1139                 BEGIN HDR0;
1140                 rcsinit();
1141                 yylex();
1142                 assert(YY_CURRENT_BUFFER);
1143                 yy_delete_buffer(YY_CURRENT_BUFFER);
1144                 yy_switch_to_buffer(yybuf);
1145                 gen();
1146                 if (rfile.lckl)
1147                         free(rfile.lckl);
1148                 if (rfile.tagl)
1149                         free(rfile.tagl);
1150                 if (rfile.accl)
1151                         free(rfile.accl);
1152                 if (rfile.revl)
1153                         free(rfile.revl);
1154                 
1155                 free(buf);
1156         }
1157         if (fn2)
1158                 free(fn2);
1159         fprintf(stderr, "%s clone successful: %d files, "
1160                 "%d branches, %d deltas\n",
1161                 argv[0], stats.nfiles, stats.nbranches, stats.ndeltas);
1162 #ifndef XTEST
1163         fclose(srv);
1164 #endif
1165         return EXIT_SUCCESS;