Handle repos where commitid may be 15 instead of 16 characters.
[cvsclone.git] / cvsclone.l
blob2946e1f89675770fe1b15cf022e638d9cd1be148
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>
97 #include <signal.h>
99 #include <errno.h>
100 #include <ctype.h>
101 #include <assert.h>
102 #include <stdlib.h>
103 #include <time.h>
104 #include <stdio.h>
105 #include <string.h>
106 #ifdef _IO_getc_unlocked
107 #undef getc
108 #define getc _IO_getc_unlocked
109 #endif
111 %s HDR0 HDR1 DESC ACCL0 TAGL0 TAGL1 LCKL0 LCKL1 REV0 REV1 REV2 ATR0 ATR1
112 %s REV3 REV4 RLST XREV FEND SRV0 SRVA SRVB SRV1 SRV2 SRV3 SRV4 SRV5 SRV6 PWF0
113 %s REVSKIP
114 %option noyywrap nounput
115 num     [0-9.]+
116 special [$,.:;@]
117 idchar  [^$,.:;@ \b\t\v\f\r\n]
118 ws      [ \b\t\v\f\r]
119 id      {num}?{idchar}({idchar}|{num})*
120 sym     [0-9]?{idchar}({idchar}|[0-9])*
121 xid     ({idchar}|{special}|{ws})+
122 yid     ({idchar}|{special})+
123 nws     (ws|\n)
124 date1   [0-9]+(-[0-9]{2}){2}\ [0-9]{2}(:[0-9]{2}){2}\ [-+][0-9]{4}
125 date2   [0-9]+(\/[0-9]{2}){2}\ [0-9]{2}(:[0-9]{2}){2}
127         /*
128         RCS file: x,v
129         Working file: x
130         head: 1.2
131         branch: 1.1
132         locks: [strict]
133                 rtc: 1.1
134                 rtc: 1.1.2.1
135         access list:
136                 root
137                 rtc
138         symbolic names:
139                 test: 1.1
140         keyword substitution: kv
141         total revisions: ; selected\ revisions:
142         description: 
145         file()          : trunk(Head) tree(Head)
146                         ;
147         trunk(p)        :
148                         | adelta(p) trunk(p->next)
149                         ;
150         tree(root)      :
151                         | tree(root->next) forest(root->branches)
152                         ;
153         forest(broot)   :
154                         | forest(broot->nextbranch) abranch(broot) tree(broot)
155                         ;
156         abranch(root)   :
157                         | abranch(root->next) adelta(root)
158                         ;
160         adelta  :
161                 "----------------------------\n"
162                 "revision %s" [ "\tlocked by: %s;" ] "\n"
164                 "date: %s;  author: %s;  state: %s; lines: +%ld -%ld"
165                 [ "; kopt: %s" ]
166                 [ "; commitid: %s" ]
167                 [ "; filename: %s" ]
168                 [ "; mergepoint:" ("  %s;")+ ]
169                 "\n"
171                 [ "branches:" ("  %s;")+ "\n" ]
172                 [ "included:" ("  %s;")+ ]
173                 [ "excluded:" ("  %s;")+ ]
174                 [ "ignored:" ("  %s;")+ ] "\n"  // '\n' if one of them is there
175                 [ "source: %s" [ "\t%s" ] "\n" ]
177                 <log>*"\n"
178          */
180 char *getstr(unsigned long n, char *s)
182         s[n] = '\0';
183         return s;
185 union revref;
186 struct rev {
187         char *num;
188         time_t date;
189         char *author, *state, *comment, *commitid, *kopt;
190         int ladd, ldel;
191         /* mergepoint */
192         /* branches */
193         /* included */
194         /* excluded */
195         /* ignored */
196         char *server,  /* originating server */
197                 *onum; /* original number */
198         char *log;
199         struct rev *next, *branch, *sub;
201 union revref {
202         char *num;
203         struct rev *r;
205 struct rpair {
206         char *item;
207         union revref rev;
209 struct rcsfile {
210         char *source, *workfile;
211         union revref head, branch;
212         int strict;
213         char *ksub, *leader;
214         int tot, sel;
215         char *descr;
216         struct rpair *lckl, *lckt, *tagl, *tagt;
217         char **accl, **acct; /* access list, access top */
218         struct rev *revl, *revt;
219 } rfile;
221 void rcsinit(void)
223         rfile.source = rfile.workfile = rfile.head.num
224                 = rfile.branch.num = rfile.ksub = rfile.leader
225                 = rfile.descr = NULL;
226         rfile.strict = rfile.tot = rfile.sel = 0;
227         rfile.lckl = rfile.lckt = rfile.tagl = rfile.tagt = NULL;
228         rfile.revl = rfile.revt = NULL;
229         rfile.accl = rfile.acct = NULL;
232 void addaccl(char *s)
234         if (!rfile.accl)
235                 rfile.acct = rfile.accl = malloc(16 * sizeof *rfile.accl);
236         else if (!((rfile.acct - rfile.accl) % 16)) {
237                 unsigned long n = rfile.acct - rfile.accl;
238                 rfile.accl = realloc(rfile.accl, (n+16) * sizeof *rfile.accl);
239                 rfile.acct = rfile.accl + n;
240         }
241         
242         *rfile.acct++ = s;
244 void addtag(char *s)
246         if (!rfile.tagl)
247                 rfile.tagt = rfile.tagl = malloc(16 * sizeof *rfile.tagl);
248         else if (!((rfile.tagt - rfile.tagl) % 16)) {
249                 unsigned long n = rfile.tagt - rfile.tagl;
250                 rfile.tagl = realloc(rfile.tagl, (n+16) * sizeof *rfile.tagl);
251                 rfile.tagt = rfile.tagl + n;
252         }
253         
254         rfile.tagt++->item = s;
256 void addlck(char *s)
258         if (!rfile.lckl)
259                 rfile.lckt = rfile.lckl = malloc(16 * sizeof *rfile.lckl);
260         else if (!((rfile.lckt - rfile.lckl) % 16)) {
261                 unsigned long n = rfile.lckt - rfile.lckl;
262                 rfile.lckl = realloc(rfile.lckl, (n+16) * sizeof *rfile.lckl);
263                 rfile.lckt = rfile.lckl + n;
264         }
265         
266         rfile.lckt++->item = s;
268 void setdate(unsigned long n, char *s, char *t)
270         char d;
271         struct tm tm;
272         sscanf(s, "%d%c%d%c%d %d:%d:%d", &tm.tm_year, &d, &tm.tm_mon, &d,
273                 &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec);
274         tm.tm_year -= 1900;
275         tm.tm_mon--;
276         if (t) {
277                 int z = atoi(t);
278                 if (z < 0)
279                         tm.tm_sec += -z / 100 * 3600 + -z % 100 * 60;
280                 else
281                         tm.tm_sec -= z / 100 * 3600 + z % 100 * 60;
282         }
283         rfile.revt->date = timegm(&tm);
284 #if 0
285         printf("setdate: %.*s, %.5s --> %s", (int)n, s, t, 
286                 ctime(&rfile.revt->date));
287 #endif
288         
290 void addrev(char *s)
292         if (!rfile.revt)
293                 rfile.revt = rfile.revl;
294         else 
295                 rfile.revt++;
296         assert(rfile.revl && rfile.revt < rfile.revl + rfile.sel);
297         /* assert(rfile.revt - rfile.revl < rfile.n) */
298         rfile.revt->num = s;
299         rfile.revt->author = rfile.revt->state
300                 = rfile.revt->comment = rfile.revt->commitid 
301                 = rfile.revt->kopt = rfile.revt->server
302                 = rfile.revt->onum = rfile.revt->log = NULL;
303         rfile.revt->next = rfile.revt->branch = rfile.revt->sub = NULL;
304         rfile.revt->ladd = rfile.revt->ldel = 0;
306 void addrl(char *s)
309 void initrev(int n)
311         if ((rfile.sel = n))
312                 rfile.revl = malloc(n * sizeof *rfile.revl);
314 char *cvsroot, *cvspass, *cvsuser, *cvshost, *cvsdir;
315 int chkroot(char *s, size_t le, int s1)
317         size_t l = strlen(cvsroot);
318         
319         if (s1) {
320                 char *x, *y;
321 #if 0
322                 fprintf(stderr, "cvsroot=%s, s=%s, s1=%d\n", cvsroot, s, s1);
323 #endif
324                 if (*s != ':' || !(x = strchr(s + 1, ':'))
325                  || !(x = strchr(x + 1, ':'))
326                  || strncmp(cvsroot, s, x - s + 1)
327                  || !isdigit(*(x + 1)))
328                         return 0;
329 #if 0
330                 fprintf(stderr, "survived\n");
331 #endif
332                 y = cvsroot + (x - s) + 1;
333                 while (isdigit(*++x))
334                         continue;
335                 if (strncmp(y, x, l - (y - cvsroot)) 
336                  || x[l - (y - cvsroot)] != ' ')
337                         return 0;
338                 l += x - s - (y - cvsroot);
339 #if 0
340                 fprintf(stderr, "l=%u(%d, %u), s=%s, cvsroot=%s, x=%s, y=%s\n", 
341                         l, x - s - (y - cvsroot), le,
342                         s, cvsroot, x, y);
343 #endif
344         } else if (strncmp(cvsroot, s, l) || s[l] != ' ')
345                 return 0;
346         
347         cvsroot = strncpy(realloc(cvsroot, le + 1), s, le);
348         cvsroot[l] = cvsroot[le+1] = '\0';
349         cvspass = cvsroot + l + 1;
350         return 1;
352 FILE *rcsfiop;
353 void rcsfwrite(char *s, size_t l, FILE *stream)
355         for (; l--; putc(*s++, stream))
356                 if (*s == '@')
357                         putc(*s, stream);
359 void rcsrang(int d0, int d1, int a0, int a1)
361         fprintf(rcsfiop, "d%d %d\na%d %d\n", d0, d1-d0+1, d1, a1-a0+1);
363 void rcsrang2(int d0, int d1, int a)
365         fprintf(rcsfiop, "d%d %d\n", d0, d1-d0+1);
367 void rcsrang3(int d, int a0, int a1)
369         fprintf(rcsfiop, "a%d %d\n", d, a1-a0+1);
371 char *queue, *que;
372 size_t queuel;
373 char *getq(size_t l)
375         if (!queue)
376                 return queue = malloc(queuel = l);
377         queuel += l;
378         queue = realloc(queue, queuel);
379         return queue + queuel - l;
381 void begin(char *s, size_t l)
383         char *p = s + 10, *fi, *fi2;
384         size_t l0 = strlen(cvsdir);
385         s[l-1] = '\0';
386         if (strncmp(cvsdir, p, l0) || p[l0] != '/') {
387                 fprintf(stderr, "path mismatch.\n");
388                 return;
389         }
390         fi = p + l0 + 1;
391         while ((fi2 = strchr(fi, '/'))) {
392                 *fi2 = '\0';
393                 mkdir(p + l0 + 1, 0777);
394                 *fi2 = '/';
395                 fi = fi2 + 1;
396         }
397         strcpy(getq(strlen(p + l0 + 1) + 1), p + l0 + 1);
398         if (!access(p + l0 + 1, R_OK)) {
399                 char buffer[1024];
400                 snprintf(buffer, sizeof(buffer), "%s.old", p + l0 + 1);
401                 rename(p + l0 + 1, buffer);
402         }
403         if (!(rcsfiop = fopen(p + l0 + 1, "w")))
404                 return;
405         s[l-1] = '\n';
406         fputs("\n", rcsfiop);
407         fwrite(s, 1, l, rcsfiop);
413 <HDR0>RCS\ file:\ {xid}\n rfile.source = getstr(yyleng-11, yytext+10);
414 <HDR0>Working\ file:\ {xid}\n rfile.workfile = getstr(yyleng-15, yytext+14);
415 <HDR0>head:\ {num}\n    rfile.head.num = getstr(yyleng-7, yytext+6);
416 <HDR0>head:\n           rfile.head.num = NULL;
417 <HDR0>branch:\n         rfile.branch.num = NULL;
418 <HDR0>branch:\ {num}\n  rfile.branch.num = getstr(yyleng-9, yytext+8);
419 <HDR0>locks:\ strict\n  BEGIN LCKL0; rfile.strict = 1; 
420 <HDR0>locks:\n          BEGIN LCKL0; rfile.strict = 0; 
421 <HDR0>access\ list:\n   BEGIN ACCL0;
422 <HDR0>symbolic\ names:\n BEGIN TAGL0;
423 <HDR0>keyword\ substitution:\ .*\n rfile.ksub = getstr(yyleng-23, yytext+22); 
424 <HDR0>comment\ leader:\ \".*\"\n rfile.leader = getstr(yyleng-19, yytext+17);
425 <HDR0>total\ revisions:\ [0-9]+;\t BEGIN HDR1; rfile.tot = atoi(yytext+17);
426 <HDR1>selected\ revisions:\ [0-9]+\n BEGIN HDR0; initrev(atoi(yytext+20));
427 <HDR0>description:\n    BEGIN DESC;
428 <HDR0>\n                /* EMPTY */
429 <DESC>={77}\n           BEGIN FEND; *yytext = '\0'; if (!rfile.descr) rfile.descr = yytext;
430 <DESC>-{28}\n           BEGIN REV0; *yytext = '\0'; if (!rfile.descr) rfile.descr = yytext;
431 <DESC>.*\n              if (!rfile.descr) rfile.descr = yytext;
432 <ACCL0>\t{id}\n         addaccl(getstr(yyleng-2, yytext+1));
433 <ACCL0>""/[^\t]         BEGIN HDR0;
434 <TAGL0>\t{sym}:\        BEGIN TAGL1; addtag(getstr(yyleng-3, yytext+1)); 
435 <TAGL0>""/[^\t]         BEGIN HDR0;
436 <TAGL1>{num}\n          BEGIN TAGL0; rfile.tagt[-1].rev.num = getstr(yyleng-1, yytext); 
437 <LCKL0>\t{id}:\         BEGIN LCKL1; addlck(getstr(yyleng-3, yytext+1)); 
438 <LCKL0>""/[^\t]         BEGIN HDR0;
439 <LCKL1>{num}\n          BEGIN LCKL0; rfile.lckt[-1].rev.num = getstr(yyleng-1, yytext);
440 <REV0>revision\ {num}\n BEGIN REV2; addrev(getstr(yyleng-10, yytext+9));
441 <REV0>revision\ {num}\t BEGIN REV1; addrev(getstr(yyleng-10, yytext+9));
442 <REV1>locked\ by:\ {id};\n BEGIN REV2; /* getstr(yyleng-13, yytext+11); */
443 <REV2>date:\ {date1};\ \  BEGIN ATR0; setdate(yyleng-15, yytext+6, yytext+yyleng-8);
444 <REV2>date:\ {date2};\ \  BEGIN ATR0; setdate(yyleng-9, yytext+6, NULL);
445 <ATR0>author:\ {id};\ \  rfile.revt->author = getstr(yyleng-11, yytext+8);
446 <ATR0>state:\ {id};\ \  rfile.revt->state = getstr(yyleng-10, yytext+7);
447 <ATR0>state:\ {id};\n   BEGIN REV3; rfile.revt->state = getstr(yyleng-9, yytext+7);
448 <ATR0>lines:\ \+[0-9]+\  BEGIN ATR1; rfile.revt->ladd = atoi(yytext+8);
449 <ATR1>-[0-9]+;?\n         BEGIN REV3; rfile.revt->ldel = atoi(yytext+1);
450 <ATR1>-[0-9]+;\ \       BEGIN ATR0; rfile.revt->ldel = atoi(yytext+1);
451 <ATR0>kopt:\ [^;\n]+;\n BEGIN REV3; rfile.revt->kopt = getstr(yyleng-8, yytext+6);
452 <ATR0>kopt:\ [^;\n]+;\ \  rfile.revt->kopt = getstr(yyleng-9, yytext+6);
453 <ATR0>commitid:\ [0-9a-zA-Z]{15};\ \  rfile.revt->commitid = getstr(15, yytext+10);
454 <ATR0>commitid:\ [0-9a-zA-Z]{16};\ \  rfile.revt->commitid = getstr(16, yytext+10);
455 <ATR0>commitid:\ [0-9a-zA-Z]{15};\n BEGIN REV3; rfile.revt->commitid = getstr(15, yytext+10);
456 <ATR0>commitid:\ [0-9a-zA-Z]{16};\n BEGIN REV3; rfile.revt->commitid = getstr(16, yytext+10);
457 <ATR0>mergepoint:\ {num};\n BEGIN REV3; /* getstr(yyleng-14, yytext+12); */
458 <REV3>branches:/.*\n    BEGIN RLST;
459 <REV3,REV4>={77}\n      BEGIN FEND; *yytext = '\0'; if (!rfile.revt->log) rfile.revt->log = yytext; if (rfile.revt) rfile.revt++;
460 <REV3,REV4>-{28}\n      BEGIN REV0; *yytext = '\0'; if (!rfile.revt->log) rfile.revt->log = yytext;
461 <REV3>.*\n              BEGIN REV4; assert(!rfile.revt->log); rfile.revt->log = yytext;
462 <RLST>\ \ {num};        addrl(getstr(yyleng-3, yytext+2));
463 <RLST>\n                BEGIN REV3;
464 <REV4>The\ changelog\ prior\ to\ shifting.*\n BEGIN REVSKIP; *yytext = '\0'; if (!rfile.revt->log) rfile.revt->log = yytext;
465 <REVSKIP>={77}\n      BEGIN REV0; *yytext = '\0'; if (!rfile.revt->log) rfile.revt->log = yytext;
466 <REVSKIP>.*\n           *yytext = '\0'; /* do nothing */
467 <REV4>.*\n              if (!rfile.revt->log) rfile.revt->log = yytext;
468 <FEND><<EOF>>           return EOF;
470 <SRV0>M\ \n             BEGIN SRVA;
471 <SRVA>M\ RCS\ file:\ {xid}\n BEGIN SRVB; begin(yytext+2, yyleng-2);
472 <SRVB>M\ ={77}\n        BEGIN SRV0; fwrite(yytext+2, 1, yyleng-2, rcsfiop); fclose(rcsfiop);
473 <SRVB>M\ .*\n           fwrite(yytext+2, 1, yyleng-2, rcsfiop);
474 <SRV0,SRV1,SRV2,SRV3,SRV4,SRV5,SRV6>E\ .*\n fwrite(yytext+2, 1, yyleng-2, stderr);
475 <SRV0>I\ LOVE\ YOU\n    fprintf(stderr, "%s", yytext);
476 <SRV0>error\ [^ \n]*\ .*\n return 1;
477 <SRV0>ok\n              return 0;
478 <SRV0><<EOF>>           return EOF;
479 <SRV1>M\ <\ .*\n        BEGIN SRV2; rcsfwrite(yytext+4, yyleng-5, rcsfiop);
480 <SRV2>M\ <\ .*\n        yytext[3] = '\n'; rcsfwrite(yytext+3, yyleng-4, rcsfiop);
481 <SRV2>M\ \\\ .*\n       BEGIN SRV3;
482 <SRV2,SRV5>ok\n         putc('\n', rcsfiop); return 0;
483 <SRV2,SRV5>error\ [^ \n]*\ .*\n putc('\n', rcsfiop); return 1;
484 <SRV3,SRV1,SRV4,SRV6>ok\n return 0;
485 <SRV3,SRV1,SRV4,SRV6>error\ [^ \n]*\ .*\n return 1;
486 <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));
487 <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));
488 <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));
489 <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));
490 <SRV4>M\ [0-9]+,[0-9]+d[0-9]+\n rcsrang2(atoi(yytext+2), atoi(strchr(yytext, ',')+1), atoi(strchr(yytext, 'd')+1));
491 <SRV4>M\ [0-9]+d[0-9]+\n   rcsrang2(atoi(yytext+2), atoi(yytext+2), atoi(strchr(yytext, 'd')+1));
492 <SRV4>M\ [0-9]+a[0-9]+,[0-9]+\n rcsrang3(atoi(yytext+2), atoi(strchr(yytext, 'a')+1), atoi(strchr(yytext, ',')+1));
493 <SRV4>M\ [0-9]+a[0-9]+\n   rcsrang3(atoi(yytext+2), atoi(strrchr(yytext, 'a')+1), atoi(strchr(yytext, 'a')+1));
494 <SRV4>M\ >\ .*\n        BEGIN SRV5; rcsfwrite(yytext+4, yyleng-5, rcsfiop);
495 <SRV5>M\ >\ .*\n        yytext[3] = '\n'; rcsfwrite(yytext+3, yyleng-4, rcsfiop);
496 <SRV5>""/M\ [0-9]       BEGIN SRV4; putc('\n', rcsfiop);
497 <SRV5>M\ \\\ .*\n       BEGIN SRV6;
498 <SRV1,SRV4>M\ RCS\ file:.*\n |
499 <SRV1,SRV4>M\ ========.*\n |
500 <SRV1,SRV4>M\ retrieving.*\n |
501 <SRV1,SRV4>M\ diff.*\n  |
502 <SRV1,SRV4>M\ Index:.*\n |
503 <SRV1>M\ [0-9]+(,[0-9]+)?d[0-9]+\n |
504 <SRV4>M\ \\\ .*\n       |
505 <SRV4>M\ (<\ .*|---)\n  /* ignore */
506 <SRV5>([^M\n]|M([^ \n]|\ [^0-9\n])).*\n? |
507 <SRV5>M\ ?\n?           |
508 <SRV0,SRV1,SRV2,SRV3,SRV4,SRV6>.*\n? fwrite(yytext, 1, yyleng, stderr);
510 <PWF0>\/1\ [^ \n]+\ .*\n if (chkroot(yytext+3, yyleng-4, 1)) return 0;
511 <PWF0>[^ \n]+\ .*\n     if (chkroot(yytext, yyleng-1, 0)) return 0;
512 <PWF0>.*\n?             /* ignore unknown lines */
513 <PWF0><<EOF>>           return EOF;
515 <HDR0>[^t\n].*\n?       |
516 <HDR0>t[^\t\n]*         |
517 <TAGL0,LCKL0>\t[^:\n]*:?\n? |
518 <TAGL0,LCKL0>\t[^:\n]*:[^ ].*\n? |
519 <TAGL0,LCKL0>\t[^:\n]*:\  |
520 <ACCL0>\t.*\n?          |
521 <REV0>[^\t\n]*[\t\n]?   |
522 <REV2>[^;\n]*(""|;\ ?)  |
523 <ATR0>[^l\n][^;\n]*     |
524 <ATR0>[^l\n][^;\n]*;\ ? |
525 <ATR0>(\n|l[^ \n]*)     |
526 <ATR0>lines:\ [^ \n]*   |
527 <ATR1,RLST>[^;\n]*      |
528 <ATR1>[^;\n]*;\ ?       |
529 <HDR1,DESC,TAGL1>.*\n?  |
530 <LCKL1,REV1,REV3>.*\n?  |
531 <REV4,XREV,SRVA>.*\n?   |
532 <SRVB>.*\n?             |
533 (.|\n)                  printf("Unmached: <%.*s>\n", yyleng, yytext);
536 static YY_BUFFER_STATE yybuf;
537 /* Read rlog to buffer */
538 size_t rlread(char *ptr, size_t nmemb, FILE *stream)
540         char *top = ptr;
541         static int state;
542         while (nmemb--) {
543                 register int c = getc(stream);
545                 if (c == EOF) {
546                         state = 0;
547                         return ptr - top;
548                 }
549                 *ptr++ = c;
550                 if (c == '\n') {
551                         if (state == 77) {
552                                 state = 0;
553                                 return ptr - top;
554                         }
555                         state = 0;
556                 } else if (state < 78) {
557                         if (c == '=')
558                                 state++;
559                         else
560                                 state = 78;
561                 }
562         }
563         return ptr - top;
566 void *ftobuf(FILE *stream, unsigned long *s)
568         unsigned long siz = 0;
569         void *buf = NULL;
570         do {
571                 buf = realloc(buf, siz + BUFSIZ + 2);
572                 siz += fread(buf + siz, 1, BUFSIZ, stream);
573                 /*siz += rlread(buf + siz, BUFSIZ, stream);*/
574         } while (siz && !(siz % BUFSIZ));
575         *s = siz += 2;
577         if (*s == 2) {
578                 free(buf);
579                 return NULL;
580         }
581         return realloc(buf, siz);
584 char *strfix(char *s)
586         unsigned long l = strlen(s);
587         if (!l)
588                return s;
589         if (s[l-1] == '\n')
590                 s[l-1] = '\0';
591         return s;       
593 FILE *srv, *rlog_input;
595 FILE *srvopen(void)
597 #ifndef XTEST
598         struct hostent *host;
599         struct servent *serv;
600         struct sockaddr_in sin;
601         int fd;
602 #endif
603         char *cvsport = NULL;
604         if (strncmp(cvsroot, ":pserver:", 9)
605          || !(cvsdir = strchr(cvsuser = cvsroot + 9, ':'))
606          || !(cvshost = strchr(cvsuser, '@')) || cvshost > cvsdir)
607                 return NULL;
608         *cvshost++ = *cvsdir++ = '\0';
609         if (isdigit(*cvsdir)) {
610                 cvsport = cvsdir;
611                 while (isdigit(*++cvsdir))
612                         continue;
613         }
614 #ifndef XTEST
615         memset(&sin, '\0', sizeof sin);
616         if (cvsport)
617                 sin.sin_port = htons(atoi(cvsport));
618         else if (!(serv = getservbyname("cvspserver", "tcp")))
619                 return NULL;
620         else
621                 sin.sin_port = serv->s_port;
622         if (!(host = gethostbyname(cvshost))
623          || (fd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
624                 return NULL;
625         sin.sin_family = AF_INET;
626         memcpy(&sin.sin_addr, host->h_addr, host->h_length);
627         if (connect(fd, (struct sockaddr *)&sin, sizeof sin) == -1) {
628                 close(fd);
629                 return NULL;
630         }
631         return fdopen(fd, "w+");
632 #else
633         return NULL;
634 #endif
636 char *getpar(char *s)
638         char *p = NULL, *q = NULL;
639         while (*s)
640                 if (*s == '.')
641                         p = q, q = s++;
642                 else
643                         s++;
644         return p;
647 struct rev *range(struct rev *begin)
649         char *brb, *bre;
650         if (begin >= rfile.revt)
651                 return NULL;
652         brb = begin->num;
653         bre = strrchr(brb, '.');
654         if (!bre || bre == strchr(brb, '.'))
655                 bre = brb;
656         else
657                 bre++;
658         /*fprintf(stderr, "brb: %s, n: %u\n", brb, bre - brb);*/
659         while (begin < rfile.revt 
660             && ((bre - brb) ? !strncmp(begin->num, brb, bre - brb)
661                   && !strchr(begin->num + (bre - brb), '.')
662                 : strchr(begin->num, '.') == strrchr(begin->num, '.')))
663                   /* works fine even if no '.' present */
664                 begin++;
665         return begin;
671  |  
672 1.3 | next
673  |  v
674 1.2   branch
675  |   ------>      
676 1.1--1.1.1.1--1.1.1.1.1.1 - -
677         |   
678      1.1.2.1 | sub
679         |    v
680      1.1.3.1
681         :
684 next            branch          sub              T
685 v               -                                /1.2
686 *               | -                              |1.1*
687                 : :                              : F B
688         *       | |             ->               | / <1.1.2.1*
689                 : :             :                : : 
690         *       | |       -     |                | | /1.1.1.2
691         ^       | ->    - |     -                | | |1.1.1.1*
692                 :       : :                      : : : F B 
693         *       |       | |                      | | | / /1.1.1.2.1.2
694         ^       |       | ->                     | | | \ \1.1.1.2.1.1*
695                 :       :                        : : : F B
696         *       |       |                        | | | / /1.1.1.1.1.2
697         ^       |       ->                       | \ \ \ \1.1.1.1.1.1*
698                 :                                :   B
699         *       |                                | / /1.2.1.2
700         ^       ->                               \ \ \1.2.1.1*
703 int prefix(const char *rev, const char *subrev)
705         size_t l = strlen(rev);
706         return !strncmp(rev, subrev, l) && subrev[l] == '.';
709 /* parse forest belonging to revision x */
710 struct rev *forest(struct rev *x, struct rev **b)
712         struct rev *z = NULL;
713         do {
714                 struct rev *branch(struct rev **b);
715                 struct rev *y = branch(b);
716                 y->sub = z;
717                 z = y;
718         } while (*b < rfile.revt && prefix(x->num, (*b)->num));
719         return z;
721 /* parse branch */
722 struct rev *branch(struct rev **b)
724         struct rev *e = range(*b), *x, *y = *b;
725         (*b)->next = NULL;
726         for (x = *b; x < e - 1; x++)
727                 x[1].next = x;
728         /* As for the trunk, for each branch revision, a forest can follow.
729          * However here the forests in decreasing order match the order 
730          * of the branch revisions.
731          */
732         while (e < rfile.revt) {
733                 while (y <= x && !prefix(y->num, e->num))
734                         y++->branch = NULL;
735                 if (y > x)
736                         break;
737                 y->branch = forest(y, &e);
738                 y++;
739         }
740         *b = e;
741         return x;
744 /* parse trunk */
745 struct rev *trunk(struct rev *b)
747         struct rev *e = b ? range(b) : NULL, *x, *y = e;
748         if (!e)
749                 return NULL;
750         for (x = b; x < e - 1; x++)
751                 x->next = x + 1;
752         x->next = NULL;
753         /* For each trunk revision, a forest can follow.
754          * The forests are in increasing order, in contrast to the 
755          * trunk revisions, which are in decreasing order.
756          */
757         while (e < rfile.revt) {
758                 /* search the trunk revision this forest belongs to */
759                 while (y > b && !prefix((--y)->num, e->num))
760                         y->branch = NULL;
761                 assert(y > b || prefix(y->num, e->num));
762                 /* parse the forest, set branch of the trunk revision */
763                 y->branch = forest(y, &e);
764         }
765         return b;
768 struct {
769         int ndeltas, nfiles, nbranches;
770 } stats;
772 void rcsfputs(const char *s, FILE *stream)
774         for (putc('@', stream); *s; putc(*s++, stream))
775                 if (*s == '@')
776                         putc(*s, stream);
777         putc('@', stream);
779 void puttree(struct rev *x, FILE *stream)
781         struct rev *y, *z;
782         void putforest(struct rev *x, FILE *stream);
783         for (y = x; y; y = y->next) {
784                 struct tm *tm;
785                 tm = gmtime(&y->date);
786                 fprintf(stream, "%s\n", y->num);
787                 fprintf(stream, "date\t%d.%02d.%02d.%02d.%02d.%02d;"
788                         "\tauthor %s;\tstate %s;\n", 
789                         tm->tm_year > 99 ? tm->tm_year + 1900 : tm->tm_year,
790                         tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min,
791                         tm->tm_sec, y->author, y->state);
792                 fprintf(stream, "branches");
793                 for (z = y->branch; z; z = z->sub)
794                         fprintf(stream, "\n\t%s", z->num);
795                         
796                 fprintf(stream, ";\n");
797                 fprintf(stream, "next\t%s;\n", y->next ? y->next->num : "");
798                 fprintf(stream, "\n");
799         }
800         putforest(x, stream);
802 void putforest(struct rev *x, FILE *stream)
804         struct rev *z;
805         if (!x)
806                 return;
807         putforest(x->next, stream);
808         for (z = x->branch; z; z = z->sub)
809                 puttree(z, stream);
811 void unqueue(void)
813         ssize_t s;
814         if (que == queue + queuel)
815                 return;
816         fcntl(fileno(srv), F_SETFL, O_NONBLOCK);
817         if ((s = write(fileno(srv), que, queue + queuel - que)) != -1)
818                 que += s;
819         fcntl(fileno(srv), F_SETFL, 0L);
821 char *fi, *fi2;
822 void puttree2(struct rev *z, struct rev *x)
824         struct rev *y;
825         for (y = x; y; y = y->next) {
826                 void putfor2(struct rev *z, struct rev *x);
827                 
828                 if (!z) {
829                         for (z = y; z && !strcmp(z->state, "dead"); z = z->next)
830                                 continue;
831                         if (z) {
832                                 sprintf(getq(10*5+5+3+2+3+2+strlen(z->num)
833                                         + (fi2 - strrchr(fi, '/')) - 1), 
834                                         "Argument -kb\n"
835                                         "Argument -r%s\n"
836                                         "Argument -aN\n"
837                                         "Argument --\n"
838                                         "Argument %.*s\n"
839                                         "diff", z->num, 
840                                         (int)(fi2 - strrchr(fi, '/') - 1),
841                                         strrchr(fi, '/') + 1);
842                                 queue[queuel-1] = '\n';
843                         } else
844                                 /* file was initially added on branch */
845                                 z = y;
846                 } else if (strcmp(y->state, "dead") && z != y) {
847                         sprintf(getq(10*6+5+3+2+2+3+2+strlen(z->num)
848                                 + strlen(y->num)
849                                 + (fi2 - strrchr(fi, '/'))), 
850                                 "Argument -kb\n"
851                                 "Argument -r%s\n"
852                                 "Argument -r%s\n"
853                                 "Argument -aN\n"
854                                 "Argument --\n"
855                                 "Argument %.*s\n"
856                                 "diff", z->num, y->num, 
857                                 (int)(fi2 - strrchr(fi, '/') - 1),
858                                 strrchr(fi, '/') + 1);
859                         queue[queuel-1] = '\n';
860                         z = y;
861                 }
862                 putfor2(z, y->branch);
863         }
865 void putfor2(struct rev *z, struct rev *x)
867         if (!x)
868                 return;
869         putfor2(z, x->sub);
870         puttree2(z, x);
872 void puttree3(struct rev *z, struct rev *x)
874         struct rev *y;
875         for (y = x; y; y = y->next) {
876                 void putfor3(struct rev *z, struct rev *x);
877                 fprintf(stderr, "%s ", y->num);
878                 
879                 stats.ndeltas++;
880                 fprintf(rcsfiop, "\n\n%s\nlog\n", y->num);
881                 rcsfputs(y->log, rcsfiop);
882                 fprintf(rcsfiop, "\ntext\n@");
883                 
884                 if (!z) {
885                         for (z = y; z && !strcmp(z->state, "dead"); z = z->next)
886                                 continue;
887                         if (z) {
888                                 unqueue();
889                                 BEGIN SRV1;
890                                 yylex();
891                         } else
892                                 /* file was initially added on branch */
893                                 z = y;
894                 } else if (strcmp(y->state, "dead") && z != y) {
895                         unqueue();
896                         BEGIN SRV4;
897                         yylex();
898                         z = y;
899                 }
900                 fprintf(rcsfiop, "@\n");
901                 putfor3(z, y->branch);
902         }
904 void putfor3(struct rev *z, struct rev *x)
906         if (!x)
907                 return;
908         stats.nbranches++;
909         putfor3(z, x->sub);
910         puttree3(z, x);
913 /* table of comment leader pairs, merged from RCS and CVS */
914 static const struct clpair {
915         char *suffix, *comlead;
916 } cltbl[] = {
917         { "a"   , "-- " },      /* Ada */
918         { "ada" , "-- " },      
919         { "adb" , "-- " },      
920         { "ads" , "-- " },      
921         { "asm" , ";; " },      /* assembler (MS-DOS) */
922         { "bas" , "' "  },      /* Visual Basic code */
923         { "bat" , ":: " },      /* batch (MS-DOS) */
924         { "body", "-- " },      /* Ada */
925         { "c"   , " * " },      /* C */
926         { "c++" , "// " },      /* C++ in all its infinite guises */
927         { "cc"  , "// " },      
928         { "cpp" , "// " },      
929         { "cxx" , "// " },      
930         { "cl"  , ";;; "},      /* Common Lisp */
931         { "cmd" , ":: " },      /* command (OS/2) */
932         { "cmf" , "c "  },      /* CM Fortran */
933         { "cs"  , " * " },      /* C* */
934         { "csh" , "# "  },      /* shell */
935         { "dlg" , " * " },      /* MS Windows dialog file */
936         { "e"   , "# "  },      /* efl */
937         { "epsf", "% "  },      /* encapsulated postscript */
938         { "epsi", "% "  },      /* encapsulated postscript */
939         { "el"  , "; "  },      /* Emacs Lisp */
940         { "f"   , "c "  },      /* Fortran */
941         { "for" , "c "  },      
942         { "frm" , "' "  },      /* Visual Basic form */
943         { "h"   , " * " },      /* C-header */
944         { "hh"  , "// " },      
945         { "hpp" , "// " },      /* C++ header */
946         { "hxx" , "// " },      
947         { "in"  , "# "  },      /* for Makefile.in */
948         { "l"   , " * " },      /* lex (NOTE: franzlisp disagrees) */
949         { "lisp", ";;; "},      /* Lucid Lisp */
950         { "lsp" , ";; " },      /* Microsoft Lisp */
951         { "m"   , "// " },      /* Objective C */
952         { "mac" , ";; " },      /* macro (DEC-10, MS-DOS, PDP-11, VMS, etc) */
953         { "mak" , "# "  },      /* makefile, e.g. Visual C++ */
954         { "me"  , ".\\\" "},    /* troff -me */
955         { "ml"  , "; "  },      /* mocklisp */
956         { "mm"  , ".\\\" "},    /* troff -mm */
957         { "ms"  , ".\\\" "},    /* troff -ms */
958         { "man" , ".\\\" "},    /* man-macros   t/nroff */
959         { "1"   , ".\\\" "},    /* feeble attempt at man pages... */
960         { "2"   , ".\\\" "},    
961         { "3"   , ".\\\" "},    
962         { "4"   , ".\\\" "},    
963         { "5"   , ".\\\" "},    
964         { "6"   , ".\\\" "},    
965         { "7"   , ".\\\" "},    
966         { "8"   , ".\\\" "},    
967         { "9"   , ".\\\" "},    
968         { "p"   , " * " },      /* Pascal */
969         { "pas" , " * " },      
970         { "pl"  , "# "  },      /* perl (conflict with Prolog) */
971         { "ps"  , "% "  },      /* PostScript */
972         { "psw" , "% "  },      /* postscript wrap */
973         { "pswm", "% "  },      /* postscript wrap */
974         { "r"   , "# "  },      /* ratfor */
975         { "rc"  , " * " },      /* Microsoft Windows resource file */
976         { "red" , "% "  },      /* psl/rlisp */
977 #ifdef sparc
978         { "s"   , "! "  },      /* assembler */
979 #endif
980 #ifdef mc68000
981         { "s"   , "| "  },      /* assembler */
982 #endif
983 #ifdef pdp11
984         { "s"   , "/ "  },      /* assembler */
985 #endif
986 #ifdef vax
987         { "s"   , "# "  },      /* assembler */
988 #endif
989 #ifdef __ksr__
990         { "s"   , "# "  },      /* assembler */
991         { "S"   , "# "  },      /* Macro assembler */
992 #endif
993         { "sh"  , "# "  },      /* shell */
994         { "sl"  , "% "  },      /* psl */
995         { "spec", "-- " },      /* Ada */
996         { "sty" , "% "  },      /* LaTeX style */
997         { "tex" , "% "  },      /* TeX */
998         { "y"   , " * " },      /* yacc */
999         { "ye"  , " * " },      /* yacc-efl */
1000         { "yr"  , " * " },      /* yacc-ratfor */
1001         { ""    , "# "  },      /* default for empty suffix */
1002         { 0     , "# "  }       /* default for unknown suffix; must be last */
1005 static int is_version_number(const char *line)
1007         if (*line < '0' || *line > '9')
1008                 return 0;
1009         for (; *line; line++)
1010                 if (*line != '\n' && *line != '.' &&
1011                                 (*line < '0' || *line > '9'))
1012                         return 0;
1013         return 1;
1016 static int is_done(const char *filename)
1018         char buffer[1024], line[1024];
1019         FILE *orig;
1020         int remaining = rfile.sel;
1022         snprintf(buffer, sizeof(buffer), "%s.old", filename);
1023         orig = fopen(buffer, "rb");
1024         if (!orig)
1025                 return 0;
1027         if (!fgets(line, sizeof(line), orig) || strncmp(line, "head\t", 5)) {
1028                 fclose(orig);
1029                 return 0;
1030         }
1032         while (!feof(orig) && fgets(line, sizeof(line), orig)) {
1033                 if (!strcmp(line, "desc\n"))
1034                         break;
1035                 if (is_version_number(line))
1036                         remaining--;
1037         }
1039         fclose(orig);
1040         if (!remaining)
1041                 rename(buffer, filename);
1042         else
1043                 unlink(buffer);
1044         return !remaining;
1047 static const char *current_file;
1048 static void unlink_current_file(int dummy)
1050         if (current_file)
1051                 unlink(current_file);
1052         exit(1);
1055 void gen()
1057         size_t l = strlen(cvsdir);
1058         char **i, suffix[6] = "";
1059         struct rpair *j;
1060         fprintf(stderr, "%s\n", rfile.source);
1061         fi = strrchr(rfile.source, '/');
1062         sprintf(getq(12 + (fi - rfile.source) + 1), 
1063                 "Directory .\n%.*s", 
1064                 (int)(fi - rfile.source), rfile.source);
1065         queue[queuel-1] = '\n';
1066         fi2 = strrchr(fi, ',');
1067         fi = strrchr(fi, '.');
1068         assert(fi2 && (!fi || fi < fi2));
1069         if (fi) {
1070                 fi++;
1071                 /* If the suffix length is greater than four characters,
1072                  * it cannot match, since it copies five of them.
1073                  */
1074                 strncpy(suffix, fi, fi2 - fi < sizeof suffix - 1 
1075                         ? fi2 - fi : sizeof suffix - 1);
1076         }
1077         fi = rfile.source + l + 1;
1078         if (is_done(fi))
1079                 return;
1080         current_file = fi;
1081         rcsfiop = fopen(fi, "w");
1083         fprintf(rcsfiop, "head\t%s;\n", rfile.head.num ? rfile.head.num : "");
1084         if (rfile.branch.num)
1085                 fprintf(rcsfiop, "branch\t%s;\n", rfile.branch.num);
1086         fprintf(rcsfiop, "access");
1087         for (i = rfile.accl; i < rfile.acct; i++)
1088                 fprintf(rcsfiop, "\n\t%s", *i);
1089         fprintf(rcsfiop, ";\n");
1090         fprintf(rcsfiop, "symbols");
1091         for (j = rfile.tagl; j < rfile.tagt; j++)
1092                 fprintf(rcsfiop, "\n\t%s:%s", j->item, j->rev.num);
1093         fprintf(rcsfiop, ";\n");
1094         fprintf(rcsfiop, "locks");
1095         for (j = rfile.lckl; j < rfile.lckt; j++)
1096                 fprintf(rcsfiop, "\n\t%s:%s", j->item, j->rev.num);
1097         fprintf(rcsfiop, ";%s\n", rfile.strict ? " strict;" : "");
1098         fprintf(rcsfiop, "comment\t");
1099         if (rfile.leader)
1100                 rcsfputs(rfile.leader, rcsfiop);
1101         else {
1102                 const struct clpair *curr;
1103                 for (curr = cltbl; curr->suffix; curr++)
1104                         if (!strcmp(curr->suffix, suffix))
1105                                 break;
1106                 rcsfputs(curr->comlead, rcsfiop);
1107         }
1108         fprintf(rcsfiop, ";\n");
1109         if (rfile.ksub && strcmp(rfile.ksub, "kv")) {
1110                 fprintf(rcsfiop, "expand\t");
1111                 rcsfputs(rfile.ksub, rcsfiop);
1112                 fprintf(rcsfiop, ";\n");
1113         }
1114         fprintf(rcsfiop, "\n\n");
1115         trunk(rfile.revl);
1116         stats.nfiles++;
1117         puttree(rfile.revl, rcsfiop);
1118         fprintf(rcsfiop, "\ndesc\n");
1119         rcsfputs(rfile.descr, rcsfiop);
1120         fprintf(rcsfiop, "\n");
1121         puttree2(NULL, rfile.revl);
1122         que = queue;
1123         puttree3(NULL, rfile.revl);
1124         if (queue)
1125                 free(queue);
1126         queue = NULL;
1127         fprintf(stderr, "\n");
1128         fclose(rcsfiop);
1129         current_file = NULL;
1131 char *home;
1132 int main(int argc, char *argv[])
1134         unsigned long siz;
1135         char *buf, *passfile, *fn0, *fn1, *fn2;
1137         if (argc == 5 || argc == 3)
1138                 if (!(rlog_input = fopen(argv[--argc], "rb"))) {
1139                         fprintf(stderr, "Invalid rlog file\n");
1140                         return EXIT_FAILURE;
1141                 }
1143         if (argc == 4 && !strcmp(argv[1], "-d")) {
1144                 argc -= 2;
1145                 cvsroot = argv[2];
1146                 argv[1] = argv[3];
1147         } else if (argc != 2) {
1148                 fprintf(stderr, "Argument count.\n");
1149                 return EXIT_FAILURE;
1150         } else
1151                 cvsroot = getenv("CVSROOT");
1153         if (!cvsroot) {
1154                 fprintf(stderr, "No CVSROOT.\n");
1155                 return EXIT_FAILURE;
1156         }
1157         cvsroot = strdup(cvsroot);
1158         passfile = (home = getenv("HOME")) 
1159                 ? strcat(strcpy(malloc(strlen(home) 
1160                         + sizeof "/.cvspass"), home), "/.cvspass")
1161                 : strcpy(malloc(sizeof ".cvspass"), ".cvspass");
1162         BEGIN PWF0;
1163         if (!(yyin = fopen(passfile, "r")) || yylex() == EOF) {
1164                 size_t l = strlen(cvsroot);
1165                 cvsroot = realloc(cvsroot, l + 2);
1166                 cvspass = strcpy(cvsroot + l + 1, "A");
1167         }
1168         if (yyin)
1169                 fclose(yyin);
1170         fprintf(stderr, "password file: %s\n"
1171                 "cvsroot: %s\n"
1172                 "pass: %.1s\n", passfile,
1173                 cvsroot, cvspass);
1175         signal(SIGINT, unlink_current_file);
1176         if (!(srv = srvopen())) {
1177                 fprintf(stderr, "server connection failed or bad cvsroot\n");
1178 #ifndef XTEST
1179                 return EXIT_FAILURE;
1180 #endif
1181         }
1182 #ifndef XTEST
1183         fprintf(srv, "BEGIN AUTH REQUEST\n"
1184                 "%s\n"
1185                 "%s\n"
1186                 "%s\n"
1187                 "END AUTH REQUEST\n", cvsdir, cvsuser, cvspass);
1188         fprintf(srv, "Root %s\n", cvsdir);
1189         /*fprintf(srv, "Argument micq/m4\n");*/
1190         fprintf(srv, "Argument %s\n", argv[1]);
1191         if (rlog_input)
1192                 yyrestart(rlog_input);
1193         else
1194         {
1195                 yyrestart(srv);
1196                 fprintf(srv, "rlog\n");
1197                 fflush(srv);
1198         }
1199         BEGIN SRV0;
1200         yy_set_interactive(1);
1201         fprintf(stderr, "exit: %d\n", yylex());
1202         fn2 = fn0 = queue;
1203         fn1 = queue + queuel;
1204         queue = NULL;
1205 #else
1206         fn2 = fn0 = malloc(strlen(argv[1]) + 3);
1207         fn1 = strlen(argv[1]) + 3 + strcpy(fn0, argv[1]);
1208         strcat(fn0, ",v");
1209 #endif
1210         while (fn0 < fn1 && (buf = ftobuf(rcsfiop = fopen(fn0, "r"), &siz))) {
1211                 fclose(rcsfiop);
1212                 fn0 += strlen(fn0) + 1;
1213                 buf[siz - 2] = YY_END_OF_BUFFER_CHAR;
1214                 buf[siz - 1] = YY_END_OF_BUFFER_CHAR;
1215                 /* fprintf(stderr, "<<%s>>\n", buf); */
1216                 yybuf = YY_CURRENT_BUFFER;
1217                 yy_scan_buffer(buf, siz);
1218                 yy_set_interactive(0);
1219                 BEGIN HDR0;
1220                 rcsinit();
1221                 yylex();
1222                 assert(YY_CURRENT_BUFFER);
1223                 yy_delete_buffer(YY_CURRENT_BUFFER);
1224                 yy_switch_to_buffer(yybuf);
1225                 gen();
1226                 if (rfile.lckl)
1227                         free(rfile.lckl);
1228                 if (rfile.tagl)
1229                         free(rfile.tagl);
1230                 if (rfile.accl)
1231                         free(rfile.accl);
1232                 if (rfile.revl)
1233                         free(rfile.revl);
1234                 
1235                 free(buf);
1236         }
1237         if (fn2)
1238                 free(fn2);
1239         fprintf(stderr, "%s clone successful: %d files, "
1240                 "%d branches, %d deltas\n",
1241                 argv[0], stats.nfiles, stats.nbranches, stats.ndeltas);
1242 #ifndef XTEST
1243         fclose(srv);
1244 #endif
1245         return EXIT_SUCCESS;