4 * Copyright (C) 2006 Peter Backes <rtc@gmx.de>
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.
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.
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
22 * flex cvsclone.l && gcc -Wall -O2 lex.yy.c -o cvsclone
26 * *-------------------------------------------------------------------*
27 * | Applying this tool to sourceforge.net or savannah.gnu.org is |
28 * | neither necessary nor recommended: With $1 being the project, you |
30 * | rsync -av rsync://$1.cvs.sourceforge.net/cvsroot/$1/ $1 |
32 * | rsync -av rsync://cvs.sv.gnu.org/sources/$1 $1 |
33 * | respectively (try also 'web' instead of 'sources'). |
34 * *-------------------------------------------------------------------*
36 * cvsclone -d :pserver:anonymous@cvs.example.org:/var/lib/cvs module
40 * Utility to clone cvs repositories over the cvspserver interface. Works
41 * for anonymous access.
45 * - reads $HOME/.cvspass
47 * - can clone corrupt repositories: writes ,v files directly, does not
48 * need rcs. (For example, ccvs module has archives that go backwards
53 * - can't enable compression.
55 * - reading cvs password from $HOME/.cvspass uses CVSROOT in a
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.
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.
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
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.
77 * - Horrible complexity. A file with n deltas takes O(n^2) to transfer.
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
86 * Patches and comments are welcome.
91 #include <sys/types.h>
92 #include <sys/socket.h>
105 #ifdef _IO_getc_unlocked
107 #define getc _IO_getc_unlocked
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
115 idchar [^$,.:;@ \b\t\v\f\r\n]
117 id {num}?{idchar}({idchar}|{num})*
118 sym [0-9]?{idchar}({idchar}|[0-9])*
119 xid ({idchar}|{special}|{ws})+
120 yid ({idchar}|{special})+
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}
138 keyword substitution: kv
139 total revisions: ; selected\ revisions:
143 file() : trunk(Head) tree(Head)
146 | adelta(p) trunk(p->next)
149 | tree(root->next) forest(root->branches)
152 | forest(broot->nextbranch) abranch(broot) tree(broot)
155 | abranch(root->next) adelta(root)
159 "----------------------------\n"
160 "revision %s" [ "\tlocked by: %s;" ] "\n"
162 "date: %s; author: %s; state: %s; lines: +%ld -%ld"
166 [ "; mergepoint:" (" %s;")+ ]
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" ]
178 char *getstr(unsigned long n, char *s)
187 char *author, *state, *comment, *commitid, *kopt;
194 char *server, /* originating server */
195 *onum; /* original number */
197 struct rev *next, *branch, *sub;
208 char *source, *workfile;
209 union revref head, branch;
214 struct rpair *lckl, *lckt, *tagl, *tagt;
215 char **accl, **acct; /* access list, access top */
216 struct rev *revl, *revt;
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)
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;
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;
252 rfile.tagt++->item = s;
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;
264 rfile.lckt++->item = s;
266 void setdate(unsigned long n, char *s, char *t)
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);
277 tm.tm_sec += -z / 100 * 3600 + -z % 100 * 60;
279 tm.tm_sec -= z / 100 * 3600 + z % 100 * 60;
281 rfile.revt->date = timegm(&tm);
283 printf("setdate: %.*s, %.5s --> %s", (int)n, s, t,
284 ctime(&rfile.revt->date));
291 rfile.revt = rfile.revl;
294 assert(rfile.revl && rfile.revt < rfile.revl + rfile.sel);
295 /* assert(rfile.revt - rfile.revl < rfile.n) */
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;
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);
320 fprintf(stderr, "cvsroot=%s, s=%s, s1=%d\n", cvsroot, s, s1);
322 if (*s != ':' || !(x = strchr(s + 1, ':'))
323 || !(x = strchr(x + 1, ':'))
324 || strncmp(cvsroot, s, x - s + 1)
325 || !isdigit(*(x + 1)))
328 fprintf(stderr, "survived\n");
330 y = cvsroot + (x - s) + 1;
331 while (isdigit(*++x))
333 if (strncmp(y, x, l - (y - cvsroot))
334 || x[l - (y - cvsroot)] != ' ')
336 l += x - s - (y - cvsroot);
338 fprintf(stderr, "l=%u(%d, %u), s=%s, cvsroot=%s, x=%s, y=%s\n",
339 l, x - s - (y - cvsroot), le,
342 } else if (strncmp(cvsroot, s, l) || s[l] != ' ')
345 cvsroot = strncpy(realloc(cvsroot, le + 1), s, le);
346 cvsroot[l] = cvsroot[le+1] = '\0';
347 cvspass = cvsroot + l + 1;
351 void rcsfwrite(char *s, size_t l, FILE *stream)
353 for (; l--; 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);
374 return queue = malloc(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);
384 if (strncmp(cvsdir, p, l0) || p[l0] != '/') {
385 fprintf(stderr, "path mismatch.\n");
389 while ((fi2 = strchr(fi, '/'))) {
391 mkdir(p + l0 + 1, 0777);
395 strcpy(getq(strlen(p + l0 + 1) + 1), p + l0 + 1);
396 if (!(rcsfiop = fopen(p + l0 + 1, "w")))
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;
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));
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;
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 |
493 <SRV4>M\ (<\ .*|---)\n /* ignore */
494 <SRV5>([^M\n]|M([^ \n]|\ [^0-9\n])).*\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;
505 <TAGL0,LCKL0>\t[^:\n]*:?\n? |
506 <TAGL0,LCKL0>\t[^:\n]*:[^ ].*\n? |
507 <TAGL0,LCKL0>\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]* |
517 <HDR1,DESC,TAGL1>.*\n? |
518 <LCKL1,REV1,REV3>.*\n? |
519 <REV4,XREV,SRVA>.*\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)
531 register int c = getc(stream);
544 } else if (state < 78) {
554 void *ftobuf(FILE *stream, unsigned long *s)
556 unsigned long siz = 0;
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));
569 return realloc(buf, siz);
572 char *strfix(char *s)
574 unsigned long l = strlen(s);
586 struct hostent *host;
587 struct servent *serv;
588 struct sockaddr_in sin;
591 char *cvsport = NULL;
592 if (strncmp(cvsroot, ":pserver:", 9)
593 || !(cvsdir = strchr(cvsuser = cvsroot + 9, ':'))
594 || !(cvshost = strchr(cvsuser, '@')) || cvshost > cvsdir)
596 *cvshost++ = *cvsdir++ = '\0';
597 if (isdigit(*cvsdir)) {
599 while (isdigit(*++cvsdir))
603 memset(&sin, '\0', sizeof sin);
605 sin.sin_port = htons(atoi(cvsport));
606 else if (!(serv = getservbyname("cvspserver", "tcp")))
609 sin.sin_port = serv->s_port;
610 if (!(host = gethostbyname(cvshost))
611 || (fd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
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) {
619 return fdopen(fd, "w+");
624 char *getpar(char *s)
626 char *p = NULL, *q = NULL;
635 struct rev *range(struct rev *begin)
638 if (begin >= rfile.revt)
641 bre = strrchr(brb, '.');
642 if (!bre || bre == strchr(brb, '.'))
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 */
664 1.1--1.1.1.1--1.1.1.1.1.1 - -
676 * | | -> | / <1.1.2.1*
678 * | | - | | | /1.1.1.2
679 ^ | -> - | - | | |1.1.1.1*
681 * | | | | | | / /1.1.1.2.1.2
682 ^ | | -> | | | \ \1.1.1.2.1.1*
684 * | | | | | / /1.1.1.1.1.2
685 ^ | -> | \ \ \ \1.1.1.1.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;
702 struct rev *branch(struct rev **b);
703 struct rev *y = branch(b);
706 } while (*b < rfile.revt && prefix(x->num, (*b)->num));
710 struct rev *branch(struct rev **b)
712 struct rev *e = range(*b), *x, *y = *b;
714 for (x = *b; x < e - 1; 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.
720 while (e < rfile.revt) {
721 while (y <= x && !prefix(y->num, e->num))
725 y->branch = forest(y, &e);
733 struct rev *trunk(struct rev *b)
735 struct rev *e = b ? range(b) : NULL, *x, *y = e;
738 for (x = b; x < e - 1; x++)
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.
745 while (e < rfile.revt) {
746 /* search the trunk revision this forest belongs to */
747 while (y > b && !prefix((--y)->num, e->num))
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);
757 int ndeltas, nfiles, nbranches;
760 void rcsfputs(const char *s, FILE *stream)
762 for (putc('@', stream); *s; putc(*s++, stream))
767 void puttree(struct rev *x, FILE *stream)
770 void putforest(struct rev *x, FILE *stream);
771 for (y = x; y; y = y->next) {
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);
784 fprintf(stream, ";\n");
785 fprintf(stream, "next\t%s;\n", y->next ? y->next->num : "");
786 fprintf(stream, "\n");
788 putforest(x, stream);
790 void putforest(struct rev *x, FILE *stream)
795 putforest(x->next, stream);
796 for (z = x->branch; z; z = z->sub)
802 if (que == queue + queuel)
804 fcntl(fileno(srv), F_SETFL, O_NONBLOCK);
805 if ((s = write(fileno(srv), que, queue + queuel - que)) != -1)
807 fcntl(fileno(srv), F_SETFL, 0L);
810 void puttree2(struct rev *z, struct rev *x)
813 for (y = x; y; y = y->next) {
814 void putfor2(struct rev *z, struct rev *x);
817 for (z = y; z && !strcmp(z->state, "dead"); z = z->next)
820 sprintf(getq(10*4+5+2+3+2+strlen(z->num)
821 + (fi2 - strrchr(fi, '/')) - 1),
827 fi2 - strrchr(fi, '/') - 1,
828 strrchr(fi, '/') + 1);
829 queue[queuel-1] = '\n';
831 /* file was initially added on branch */
833 } else if (strcmp(y->state, "dead") && z != y) {
834 sprintf(getq(10*5+5+2+2+2+2+strlen(z->num)
836 + (fi2 - strrchr(fi, '/'))),
842 "diff", z->num, y->num,
843 fi2 - strrchr(fi, '/') - 1,
844 strrchr(fi, '/') + 1);
845 queue[queuel-1] = '\n';
848 putfor2(z, y->branch);
851 void putfor2(struct rev *z, struct rev *x)
858 void puttree3(struct rev *z, struct rev *x)
861 for (y = x; y; y = y->next) {
862 void putfor3(struct rev *z, struct rev *x);
863 fprintf(stderr, "%s ", y->num);
866 fprintf(rcsfiop, "\n\n%s\nlog\n", y->num);
867 rcsfputs(y->log, rcsfiop);
868 fprintf(rcsfiop, "\ntext\n@");
871 for (z = y; z && !strcmp(z->state, "dead"); z = z->next)
878 /* file was initially added on branch */
880 } else if (strcmp(y->state, "dead") && z != y) {
886 fprintf(rcsfiop, "@\n");
887 putfor3(z, y->branch);
890 void putfor3(struct rev *z, struct rev *x)
899 /* table of comment leader pairs, merged from RCS and CVS */
900 static const struct clpair {
901 char *suffix, *comlead;
903 { "a" , "-- " }, /* Ada */
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 */
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 */
928 { "frm" , "' " }, /* Visual Basic form */
929 { "h" , " * " }, /* C-header */
931 { "hpp" , "// " }, /* C++ header */
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... */
954 { "p" , " * " }, /* Pascal */
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 */
964 { "s" , "! " }, /* assembler */
967 { "s" , "| " }, /* assembler */
970 { "s" , "/ " }, /* assembler */
973 { "s" , "# " }, /* assembler */
976 { "s" , "# " }, /* assembler */
977 { "S" , "# " }, /* Macro assembler */
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 */
993 size_t l = strlen(cvsdir);
994 char **i, suffix[6] = "";
996 fprintf(stderr, "%s\n", rfile.source);
997 fi = strrchr(rfile.source, '/');
998 sprintf(getq(12 + (fi - rfile.source) + 1),
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));
1007 /* If the suffix length is greater than four characters,
1008 * it cannot match, since it copies five of them.
1010 strncpy(suffix, fi, fi2 - fi < sizeof suffix - 1
1011 ? fi2 - fi : sizeof suffix - 1);
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");
1033 rcsfputs(rfile.leader, rcsfiop);
1035 const struct clpair *curr;
1036 for (curr = cltbl; curr->suffix; curr++)
1037 if (!strcmp(curr->suffix, suffix))
1039 rcsfputs(curr->comlead, rcsfiop);
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");
1047 fprintf(rcsfiop, "\n\n");
1050 puttree(rfile.revl, rcsfiop);
1051 fprintf(rcsfiop, "\ndesc\n");
1052 rcsfputs(rfile.descr, rcsfiop);
1053 fprintf(rcsfiop, "\n");
1054 puttree2(NULL, rfile.revl);
1056 puttree3(NULL, rfile.revl);
1060 fprintf(stderr, "\n");
1064 int main(int argc, char *argv[])
1067 char *buf, *passfile, *fn0, *fn1, *fn2;
1069 if (argc == 4 && !strcmp(argv[1], "-d")) {
1073 } else if (argc != 2) {
1074 fprintf(stderr, "Argument count.\n");
1075 return EXIT_FAILURE;
1077 cvsroot = getenv("CVSROOT");
1080 fprintf(stderr, "No CVSROOT.\n");
1081 return EXIT_FAILURE;
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");
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");
1096 fprintf(stderr, "password file: %s\n"
1098 "pass: %.1s\n", passfile,
1101 if (!(srv = srvopen())) {
1102 fprintf(stderr, "server connection failed or bad cvsroot\n");
1104 return EXIT_FAILURE;
1109 fprintf(srv, "BEGIN AUTH REQUEST\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");
1120 yy_set_interactive(1);
1121 fprintf(stderr, "exit: %d\n", yylex());
1123 fn1 = queue + queuel;
1126 fn2 = fn0 = malloc(strlen(argv[1]) + 3);
1127 fn1 = strlen(argv[1]) + 3 + strcpy(fn0, argv[1]);
1130 while (fn0 < fn1 && (buf = ftobuf(rcsfiop = fopen(fn0, "r"), &siz))) {
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);
1142 assert(YY_CURRENT_BUFFER);
1143 yy_delete_buffer(YY_CURRENT_BUFFER);
1144 yy_switch_to_buffer(yybuf);
1159 fprintf(stderr, "%s clone successful: %d files, "
1160 "%d branches, %d deltas\n",
1161 argv[0], stats.nfiles, stats.nbranches, stats.ndeltas);
1165 return EXIT_SUCCESS;