no-fork.p
[cvsps/4msysgit.git] / cvs_direct.c
blob1e582b80366f5308b21659ecf8d347308ec7ea73
1 /*
2 * Copyright 2001, 2002, 2003 David Mansfield and Cobite, Inc.
3 * See COPYING file for license information
4 */
6 #include <string.h>
7 #ifdef __MINGW32__
8 char * strsep(char **str, const char *delim);
9 #endif
10 #include <unistd.h>
11 #include <stdlib.h>
12 #include <limits.h>
13 #include <stdarg.h>
14 #include <zlib.h>
15 #include <sys/socket.h>
16 #include <cbtcommon/debug.h>
17 #include <cbtcommon/text_util.h>
18 #include <cbtcommon/tcpsocket.h>
19 #include <cbtcommon/sio.h>
21 #include "cvs_direct.h"
22 #include "util.h"
24 #define RD_BUFF_SIZE 4096
26 struct _CvsServerCtx
28 int read_fd;
29 int write_fd;
30 char root[PATH_MAX];
32 int is_pserver;
34 /* buffered reads from descriptor */
35 char read_buff[RD_BUFF_SIZE];
36 char * head;
37 char * tail;
39 int compressed;
40 z_stream zout;
41 z_stream zin;
43 /* when reading compressed data, the compressed data buffer */
44 char zread_buff[RD_BUFF_SIZE];
47 static void get_cvspass(char *, const char *);
48 static void send_string(CvsServerCtx *, const char *, ...);
49 static int read_response(CvsServerCtx *, const char *);
50 static void ctx_to_fp(CvsServerCtx * ctx, FILE * fp);
51 static int read_line(CvsServerCtx * ctx, char * p);
53 static CvsServerCtx * open_ctx_pserver(CvsServerCtx *, const char *);
54 static CvsServerCtx * open_ctx_forked(CvsServerCtx *, const char *);
56 CvsServerCtx * open_cvs_server(char * p_root, int compress)
58 CvsServerCtx * ctx = (CvsServerCtx*)malloc(sizeof(*ctx));
59 char root[PATH_MAX];
60 char * p = root, *tok;
62 if (!ctx)
63 return NULL;
65 ctx->head = ctx->tail = ctx->read_buff;
66 ctx->read_fd = ctx->write_fd = -1;
67 ctx->compressed = 0;
68 ctx->is_pserver = 0;
70 if (compress)
72 memset(&ctx->zout, 0, sizeof(z_stream));
73 memset(&ctx->zin, 0, sizeof(z_stream));
75 /*
76 * to 'prime' the reads, make it look like there was output
77 * room available (i.e. we have processed all pending compressed
78 * data
80 ctx->zin.avail_out = 1;
82 if (deflateInit(&ctx->zout, compress) != Z_OK)
84 free(ctx);
85 return NULL;
88 if (inflateInit(&ctx->zin) != Z_OK)
90 deflateEnd(&ctx->zout);
91 free(ctx);
92 return NULL;
96 strcpy(root, p_root);
98 tok = strsep(&p, ":");
100 /* if root string looks like :pserver:... then the first token will be empty */
101 if (strlen(tok) == 0)
103 char * method = strsep(&p, ":");
104 if (strcmp(method, "pserver") == 0)
106 ctx = open_ctx_pserver(ctx, p);
108 else if (strstr("local:ext:fork:server", method))
110 /* handle all of these via fork, even local */
111 ctx = open_ctx_forked(ctx, p);
113 else
115 debug(DEBUG_APPERROR, "cvs_direct: unsupported cvs access method: %s", method);
116 free(ctx);
117 ctx = NULL;
120 else
122 ctx = open_ctx_forked(ctx, p_root);
125 if (ctx)
127 char buff[BUFSIZ];
129 send_string(ctx, "Root %s\n", ctx->root);
131 /* this is taken from 1.11.1p1 trace - but with Mbinary removed. we can't handle it (yet!) */
132 send_string(ctx, "Valid-responses ok error Valid-requests Checked-in New-entry Checksum Copy-file Updated Created Update-existing Merged Patched Rcs-diff Mode Mod-time Removed Remove-entry Set-static-directory Clear-static-directory Set-sticky Clear-sticky Template Set-checkin-prog Set-update-prog Notified Module-expansion Wrapper-rcsOption M E F\n", ctx->root);
134 send_string(ctx, "valid-requests\n");
136 /* check for the commands we will issue */
137 read_line(ctx, buff);
138 if (strncmp(buff, "Valid-requests", 14) != 0)
140 debug(DEBUG_APPERROR, "cvs_direct: bad response to valid-requests command");
141 close_cvs_server(ctx);
142 return NULL;
145 if (!strstr(buff, " version") ||
146 !strstr(buff, " rlog") ||
147 !strstr(buff, " rdiff") ||
148 !strstr(buff, " diff") ||
149 !strstr(buff, " co"))
151 debug(DEBUG_APPERROR, "cvs_direct: cvs server too old for cvs_direct");
152 close_cvs_server(ctx);
153 return NULL;
156 read_line(ctx, buff);
157 if (strcmp(buff, "ok") != 0)
159 debug(DEBUG_APPERROR, "cvs_direct: bad ok trailer to valid-requests command");
160 close_cvs_server(ctx);
161 return NULL;
164 /* this is myterious but 'mandatory' */
165 send_string(ctx, "UseUnchanged\n");
167 if (compress)
169 send_string(ctx, "Gzip-stream %d\n", compress);
170 ctx->compressed = 1;
173 debug(DEBUG_APPMSG1, "cvs_direct initialized to CVSROOT %s", ctx->root);
176 return ctx;
179 static CvsServerCtx * open_ctx_pserver(CvsServerCtx * ctx, const char * p_root)
181 char root[PATH_MAX];
182 char full_root[PATH_MAX];
183 char * p = root, *tok, *tok2;
184 char user[BUFSIZ];
185 char server[BUFSIZ];
186 char pass[BUFSIZ];
187 char port[8];
189 strcpy(root, p_root);
191 tok = strsep(&p, ":");
192 if (strlen(tok) == 0 || !p)
194 debug(DEBUG_APPERROR, "parse error on third token");
195 goto out_free_err;
198 tok2 = strsep(&tok, "@");
199 if (!strlen(tok2) || (!tok || !strlen(tok)))
201 debug(DEBUG_APPERROR, "parse error on user@server in pserver");
202 goto out_free_err;
205 strcpy(user, tok2);
206 strcpy(server, tok);
208 if (*p != '/')
210 tok = strchr(p, '/');
211 if (!tok)
213 debug(DEBUG_APPERROR, "parse error: expecting / in root");
214 goto out_free_err;
217 memset(port, 0, sizeof(port));
218 memcpy(port, p, tok - p);
220 p = tok;
222 else
224 strcpy(port, "2401");
227 /* the line from .cvspass is fully qualified, so rebuild */
228 snprintf(full_root, PATH_MAX, ":pserver:%s@%s:%s%s", user, server, port, p);
229 get_cvspass(pass, full_root);
231 debug(DEBUG_TCP, "user:%s server:%s port:%s pass:%s full_root:%s", user, server, port, pass, full_root);
233 if ((ctx->read_fd = tcp_create_socket(REUSE_ADDR)) < 0)
234 goto out_free_err;
236 ctx->write_fd = dup(ctx->read_fd);
238 if (tcp_connect(ctx->read_fd, server, atoi(port)) < 0)
239 goto out_close_err;
241 send_string(ctx, "BEGIN AUTH REQUEST\n");
242 send_string(ctx, "%s\n", p);
243 send_string(ctx, "%s\n", user);
244 send_string(ctx, "%s\n", pass);
245 send_string(ctx, "END AUTH REQUEST\n");
247 if (!read_response(ctx, "I LOVE YOU"))
248 goto out_close_err;
250 strcpy(ctx->root, p);
251 ctx->is_pserver = 1;
253 return ctx;
255 out_close_err:
256 close(ctx->read_fd);
257 out_free_err:
258 free(ctx);
259 return NULL;
262 static CvsServerCtx * open_ctx_forked(CvsServerCtx * ctx, const char * p_root)
264 #ifdef __MINGW32__
265 debug(DEBUG_SYSERROR, "cvs_direct: fork not supported on MinGW");
266 #else
267 char root[PATH_MAX];
268 char * p = root, *tok, *tok2, *rep;
269 char execcmd[PATH_MAX];
270 int to_cvs[2];
271 int from_cvs[2];
272 pid_t pid;
273 const char * cvs_server = getenv("CVS_SERVER");
275 if (!cvs_server)
276 cvs_server = "cvs";
278 strcpy(root, p_root);
280 /* if there's a ':', it's remote */
281 tok = strsep(&p, ":");
283 if (p)
285 const char * cvs_rsh = getenv("CVS_RSH");
287 if (!cvs_rsh)
288 cvs_rsh = "rsh";
290 tok2 = strsep(&tok, "@");
292 if (tok)
293 snprintf(execcmd, PATH_MAX, "%s -l %s %s %s server", cvs_rsh, tok2, tok, cvs_server);
294 else
295 snprintf(execcmd, PATH_MAX, "%s %s %s server", cvs_rsh, tok2, cvs_server);
297 rep = p;
299 else
301 snprintf(execcmd, PATH_MAX, "%s server", cvs_server);
302 rep = tok;
305 if (pipe(to_cvs) < 0)
307 debug(DEBUG_SYSERROR, "cvs_direct: failed to create pipe to_cvs");
308 goto out_free_err;
311 if (pipe(from_cvs) < 0)
313 debug(DEBUG_SYSERROR, "cvs_direct: failed to create pipe from_cvs");
314 goto out_close_err;
317 debug(DEBUG_TCP, "forked cmdline: %s", execcmd);
319 if ((pid = fork()) < 0)
321 debug(DEBUG_SYSERROR, "cvs_direct: can't fork");
322 goto out_close2_err;
324 else if (pid == 0) /* child */
326 char * argp[4];
327 argp[0] = "sh";
328 argp[1] = "-c";
329 argp[2] = execcmd;
330 argp[3] = NULL;
332 close(to_cvs[1]);
333 close(from_cvs[0]);
335 close(0);
336 dup(to_cvs[0]);
337 close(1);
338 dup(from_cvs[1]);
340 execv("/bin/sh",argp);
342 debug(DEBUG_APPERROR, "cvs_direct: fatal: shouldn't be reached");
343 exit(1);
346 close(to_cvs[0]);
347 close(from_cvs[1]);
348 ctx->read_fd = from_cvs[0];
349 ctx->write_fd = to_cvs[1];
351 strcpy(ctx->root, rep);
353 return ctx;
355 out_close2_err:
356 close(from_cvs[0]);
357 close(from_cvs[1]);
358 out_close_err:
359 close(to_cvs[0]);
360 close(to_cvs[1]);
361 out_free_err:
362 free(ctx);
363 #endif
364 return NULL;
367 void close_cvs_server(CvsServerCtx * ctx)
369 /* FIXME: some sort of flushing should be done for non-compressed case */
371 if (ctx->compressed)
373 int ret, len;
374 char buff[BUFSIZ];
377 * there shouldn't be anything left, but we do want
378 * to send an 'end of stream' marker, (if such a thing
379 * actually exists..)
383 ctx->zout.next_out = buff;
384 ctx->zout.avail_out = BUFSIZ;
385 ret = deflate(&ctx->zout, Z_FINISH);
387 if ((ret == Z_OK || ret == Z_STREAM_END) && ctx->zout.avail_out != BUFSIZ)
389 len = BUFSIZ - ctx->zout.avail_out;
390 if (writen(ctx->write_fd, buff, len) != len)
391 debug(DEBUG_APPERROR, "cvs_direct: zout: error writing final state");
393 //hexdump(buff, len, "cvs_direct: zout: sending unsent data");
395 } while (ret == Z_OK);
397 if ((ret = deflateEnd(&ctx->zout)) != Z_OK)
398 debug(DEBUG_APPERROR, "cvs_direct: zout: deflateEnd error: %s: %s",
399 (ret == Z_STREAM_ERROR) ? "Z_STREAM_ERROR":"Z_DATA_ERROR", ctx->zout.msg);
402 /* we're done writing now */
403 debug(DEBUG_TCP, "cvs_direct: closing cvs server write connection %d", ctx->write_fd);
404 close(ctx->write_fd);
407 * if this is pserver, then read_fd is a bi-directional socket.
408 * we want to shutdown the write side, just to make sure the
409 * server get's eof
411 if (ctx->is_pserver)
413 debug(DEBUG_TCP, "cvs_direct: shutdown on read socket");
414 if (shutdown(ctx->read_fd, SHUT_WR) < 0)
415 debug(DEBUG_SYSERROR, "cvs_direct: error with shutdown on pserver socket");
418 if (ctx->compressed)
420 int ret = Z_OK, len, eof = 0;
421 char buff[BUFSIZ];
423 /* read to the 'eof'/'eos' marker. there are two states we
424 * track, looking for Z_STREAM_END (application level EOS)
425 * and EOF on socket. Both should happen at the same time,
426 * but we need to do the read first, the first time through
427 * the loop, but we want to do one read after getting Z_STREAM_END
428 * too. so this loop has really ugly exit conditions.
430 for(;;)
433 * if there's nothing in the avail_in, and we
434 * inflated everything last pass (avail_out != 0)
435 * then slurp some more from the descriptor,
436 * if we get EOF, exit the loop
438 if (ctx->zin.avail_in == 0 && ctx->zin.avail_out != 0)
440 debug(DEBUG_TCP, "cvs_direct: doing final slurp");
441 len = read(ctx->read_fd, ctx->zread_buff, RD_BUFF_SIZE);
442 debug(DEBUG_TCP, "cvs_direct: did final slurp: %d", len);
444 if (len <= 0)
446 eof = 1;
447 break;
450 /* put the data into the inflate input stream */
451 ctx->zin.next_in = ctx->zread_buff;
452 ctx->zin.avail_in = len;
456 * if the last time through we got Z_STREAM_END, and we
457 * get back here, it means we should've gotten EOF but
458 * didn't
460 if (ret == Z_STREAM_END)
461 break;
463 ctx->zin.next_out = buff;
464 ctx->zin.avail_out = BUFSIZ;
466 ret = inflate(&ctx->zin, Z_SYNC_FLUSH);
467 len = BUFSIZ - ctx->zin.avail_out;
469 if (ret == Z_BUF_ERROR)
470 debug(DEBUG_APPERROR, "Z_BUF_ERROR");
472 if (ret == Z_OK && len == 0)
473 debug(DEBUG_TCP, "cvs_direct: no data out of inflate");
475 if (ret == Z_STREAM_END)
476 debug(DEBUG_TCP, "cvs_direct: got Z_STREAM_END");
478 if ((ret == Z_OK || ret == Z_STREAM_END) && len > 0)
479 hexdump(buff, BUFSIZ - ctx->zin.avail_out, "cvs_direct: zin: unread data at close");
482 if (ret != Z_STREAM_END)
483 debug(DEBUG_APPERROR, "cvs_direct: zin: Z_STREAM_END not encountered (premature EOF?)");
485 if (eof == 0)
486 debug(DEBUG_APPERROR, "cvs_direct: zin: EOF not encountered (premature Z_STREAM_END?)");
488 if ((ret = inflateEnd(&ctx->zin)) != Z_OK)
489 debug(DEBUG_APPERROR, "cvs_direct: zin: inflateEnd error: %s: %s",
490 (ret == Z_STREAM_ERROR) ? "Z_STREAM_ERROR":"Z_DATA_ERROR", ctx->zin.msg ? ctx->zin.msg : "");
493 debug(DEBUG_TCP, "cvs_direct: closing cvs server read connection %d", ctx->read_fd);
494 close(ctx->read_fd);
496 free(ctx);
499 static void get_cvspass(char * pass, const char * root)
501 char cvspass[PATH_MAX];
502 const char * home;
503 FILE * fp;
505 pass[0] = 0;
507 if (!(home = getenv("HOME")))
509 debug(DEBUG_APPERROR, "HOME environment variable not set");
510 exit(1);
513 if (snprintf(cvspass, PATH_MAX, "%s/.cvspass", home) >= PATH_MAX)
515 debug(DEBUG_APPERROR, "prefix buffer overflow");
516 exit(1);
519 if ((fp = fopen(cvspass, "r")))
521 char buff[BUFSIZ];
522 int len = strlen(root);
524 while (fgets(buff, BUFSIZ, fp))
526 /* FIXME: what does /1 mean? */
527 if (strncmp(buff, "/1 ", 3) != 0)
528 continue;
530 if (strncmp(buff + 3, root, len) == 0)
532 strcpy(pass, buff + 3 + len + 1);
533 chop(pass);
534 break;
538 fclose(fp);
541 if (!pass[0])
542 pass[0] = 'A';
545 static void send_string(CvsServerCtx * ctx, const char * str, ...)
547 int len;
548 char buff[BUFSIZ];
549 va_list ap;
551 va_start(ap, str);
553 len = vsnprintf(buff, BUFSIZ, str, ap);
554 if (len >= BUFSIZ)
556 debug(DEBUG_APPERROR, "cvs_direct: command send string overflow");
557 exit(1);
560 if (ctx->compressed)
562 char zbuff[BUFSIZ];
564 if (ctx->zout.avail_in != 0)
566 debug(DEBUG_APPERROR, "cvs_direct: zout: last output command not flushed");
567 exit(1);
570 ctx->zout.next_in = buff;
571 ctx->zout.avail_in = len;
572 ctx->zout.avail_out = 0;
574 while (ctx->zout.avail_in > 0 || ctx->zout.avail_out == 0)
576 int ret;
578 ctx->zout.next_out = zbuff;
579 ctx->zout.avail_out = BUFSIZ;
581 /* FIXME: for the arguments before a command, flushing is counterproductive */
582 ret = deflate(&ctx->zout, Z_SYNC_FLUSH);
584 if (ret == Z_OK)
586 len = BUFSIZ - ctx->zout.avail_out;
588 if (writen(ctx->write_fd, zbuff, len) != len)
590 debug(DEBUG_SYSERROR, "cvs_direct: zout: can't write");
591 exit(1);
594 else
596 debug(DEBUG_APPERROR, "cvs_direct: zout: error %d %s", ret, ctx->zout.msg);
600 else
602 if (writen(ctx->write_fd, buff, len) != len)
604 debug(DEBUG_SYSERROR, "cvs_direct: can't send command");
605 exit(1);
609 debug(DEBUG_TCP, "string: '%s' sent", buff);
612 static int refill_buffer(CvsServerCtx * ctx)
614 int len;
616 if (ctx->head != ctx->tail)
618 debug(DEBUG_APPERROR, "cvs_direct: refill_buffer called on non-empty buffer");
619 exit(1);
622 ctx->head = ctx->read_buff;
623 len = RD_BUFF_SIZE;
625 if (ctx->compressed)
627 int zlen, ret;
629 /* if there was leftover buffer room, it's time to slurp more data */
632 if (ctx->zin.avail_out > 0)
634 if (ctx->zin.avail_in != 0)
636 debug(DEBUG_APPERROR, "cvs_direct: zin: expect 0 avail_in");
637 exit(1);
639 zlen = read(ctx->read_fd, ctx->zread_buff, RD_BUFF_SIZE);
640 ctx->zin.next_in = ctx->zread_buff;
641 ctx->zin.avail_in = zlen;
644 ctx->zin.next_out = ctx->head;
645 ctx->zin.avail_out = len;
647 /* FIXME: we don't always need Z_SYNC_FLUSH, do we? */
648 ret = inflate(&ctx->zin, Z_SYNC_FLUSH);
650 while (ctx->zin.avail_out == len);
652 if (ret == Z_OK)
654 ctx->tail = ctx->head + (len - ctx->zin.avail_out);
656 else
658 debug(DEBUG_APPERROR, "cvs_direct: zin: error %d %s", ret, ctx->zin.msg);
659 exit(1);
662 else
664 len = read(ctx->read_fd, ctx->head, len);
665 ctx->tail = (len <= 0) ? ctx->head : ctx->head + len;
668 return len;
671 static int read_line(CvsServerCtx * ctx, char * p)
673 int len = 0;
674 while (1)
676 if (ctx->head == ctx->tail)
677 if (refill_buffer(ctx) <= 0)
678 return -1;
680 *p = *ctx->head++;
682 if (*p == '\n')
684 *p = 0;
685 break;
687 p++;
688 len++;
691 return len;
694 static int read_response(CvsServerCtx * ctx, const char * str)
696 /* FIXME: more than 1 char at a time */
697 char resp[BUFSIZ];
699 if (read_line(ctx, resp) < 0)
700 return 0;
702 debug(DEBUG_TCP, "response '%s' read", resp);
704 return (strcmp(resp, str) == 0);
707 static void ctx_to_fp(CvsServerCtx * ctx, FILE * fp)
709 char line[BUFSIZ];
711 while (1)
713 read_line(ctx, line);
714 debug(DEBUG_TCP, "ctx_to_fp: %s", line);
715 if (memcmp(line, "M ", 2) == 0)
717 if (fp)
718 fprintf(fp, "%s\n", line + 2);
720 else if (memcmp(line, "E ", 2) == 0)
722 debug(DEBUG_APPMSG1, "%s", line + 2);
724 else if (strncmp(line, "ok", 2) == 0 || strncmp(line, "error", 5) == 0)
726 break;
730 if (fp)
731 fflush(fp);
734 void cvs_rdiff(CvsServerCtx * ctx,
735 const char * rep, const char * file,
736 const char * rev1, const char * rev2)
738 /* NOTE: opts are ignored for rdiff, '-u' is always used */
740 send_string(ctx, "Argument -u\n");
741 send_string(ctx, "Argument -r\n");
742 send_string(ctx, "Argument %s\n", rev1);
743 send_string(ctx, "Argument -r\n");
744 send_string(ctx, "Argument %s\n", rev2);
745 send_string(ctx, "Argument %s%s\n", rep, file);
746 send_string(ctx, "rdiff\n");
748 ctx_to_fp(ctx, stdout);
751 void cvs_rupdate(CvsServerCtx * ctx, const char * rep, const char * file, const char * rev, int create, const char * opts)
753 FILE * fp;
754 char cmdbuff[BUFSIZ];
756 snprintf(cmdbuff, BUFSIZ, "diff %s %s /dev/null %s | sed -e '%s s|^\\([+-][+-][+-]\\) -|\\1 %s/%s|g'",
757 opts, create?"":"-", create?"-":"", create?"2":"1", rep, file);
759 debug(DEBUG_TCP, "cmdbuff: %s", cmdbuff);
761 if (!(fp = popen(cmdbuff, "w")))
763 debug(DEBUG_APPERROR, "cvs_direct: popen for diff failed: %s", cmdbuff);
764 exit(1);
767 send_string(ctx, "Argument -p\n");
768 send_string(ctx, "Argument -r\n");
769 send_string(ctx, "Argument %s\n", rev);
770 send_string(ctx, "Argument %s/%s\n", rep, file);
771 send_string(ctx, "co\n");
773 ctx_to_fp(ctx, fp);
775 pclose(fp);
778 static int parse_patch_arg(char * arg, char ** str)
780 char *tok, *tok2 = "";
781 tok = strsep(str, " ");
782 if (!tok)
783 return 0;
785 if (!*tok == '-')
787 debug(DEBUG_APPERROR, "diff_opts parse error: no '-' starting argument: %s", *str);
788 return 0;
791 /* if it's not 'long format' argument, we can process it efficiently */
792 if (tok[1] == '-')
794 debug(DEBUG_APPERROR, "diff_opts parse_error: long format args not supported");
795 return 0;
798 /* see if command wants two args and they're separated by ' ' */
799 if (tok[2] == 0 && strchr("BdDFgiorVxYz", tok[1]))
801 tok2 = strsep(str, " ");
802 if (!tok2)
804 debug(DEBUG_APPERROR, "diff_opts parse_error: argument %s requires two arguments", tok);
805 return 0;
809 snprintf(arg, 32, "%s%s", tok, tok2);
810 return 1;
813 void cvs_diff(CvsServerCtx * ctx,
814 const char * rep, const char * file,
815 const char * rev1, const char * rev2, const char * opts)
817 char argstr[BUFSIZ], *p = argstr;
818 char arg[32];
819 char file_buff[PATH_MAX], *basename;
821 strzncpy(argstr, opts, BUFSIZ);
822 while (parse_patch_arg(arg, &p))
823 send_string(ctx, "Argument %s\n", arg);
825 send_string(ctx, "Argument -r\n");
826 send_string(ctx, "Argument %s\n", rev1);
827 send_string(ctx, "Argument -r\n");
828 send_string(ctx, "Argument %s\n", rev2);
831 * we need to separate the 'basename' of file in order to
832 * generate the Directory directive(s)
834 strzncpy(file_buff, file, PATH_MAX);
835 if ((basename = strrchr(file_buff, '/')))
837 *basename = 0;
838 send_string(ctx, "Directory %s/%s\n", rep, file_buff);
839 send_string(ctx, "%s/%s/%s\n", ctx->root, rep, file_buff);
841 else
843 send_string(ctx, "Directory %s\n", rep, file_buff);
844 send_string(ctx, "%s/%s\n", ctx->root, rep);
847 send_string(ctx, "Directory .\n");
848 send_string(ctx, "%s\n", ctx->root);
849 send_string(ctx, "Argument %s/%s\n", rep, file);
850 send_string(ctx, "diff\n");
852 ctx_to_fp(ctx, stdout);
856 * FIXME: the design of this sucks. It was originally designed to fork a subprocess
857 * which read the cvs response and send it back through a pipe the main process,
858 * which fdopen(3)ed the other end, and juts used regular fgets. This however
859 * didn't work because the reads of compressed data in the child process altered
860 * the compression state, and there was no way to resynchronize that state with
861 * the parent process. We could use threads...
863 FILE * cvs_rlog_open(CvsServerCtx * ctx, const char * rep, const char * date_str)
865 /* note: use of the date_str is handled in a non-standard, cvsps specific way */
866 if (date_str && date_str[0])
868 send_string(ctx, "Argument -d\n", rep);
869 send_string(ctx, "Argument %s<1 Jan 2038 05:00:00 -0000\n", date_str);
870 send_string(ctx, "Argument -d\n", rep);
871 send_string(ctx, "Argument %s\n", date_str);
874 send_string(ctx, "Argument %s\n", rep);
875 send_string(ctx, "rlog\n");
878 * FIXME: is it possible to create a 'fake' FILE * whose 'refill'
879 * function is below?
881 return (FILE*)ctx;
884 char * cvs_rlog_fgets(char * buff, int buflen, CvsServerCtx * ctx)
886 char lbuff[BUFSIZ];
887 int len;
889 len = read_line(ctx, lbuff);
890 debug(DEBUG_TCP, "cvs_direct: rlog: read %s", lbuff);
892 if (memcmp(lbuff, "M ", 2) == 0)
894 memcpy(buff, lbuff + 2, len - 2);
895 buff[len - 2 ] = '\n';
896 buff[len - 1 ] = 0;
898 else if (memcmp(lbuff, "E ", 2) == 0)
900 debug(DEBUG_APPMSG1, "%s", lbuff + 2);
902 else if (strcmp(lbuff, "ok") == 0 ||strcmp(lbuff, "error") == 0)
904 debug(DEBUG_TCP, "cvs_direct: rlog: got command completion");
905 return NULL;
908 return buff;
911 void cvs_rlog_close(CvsServerCtx * ctx)
915 void cvs_version(CvsServerCtx * ctx, char * client_version, char * server_version)
917 char lbuff[BUFSIZ];
918 strcpy(client_version, "Client: Concurrent Versions System (CVS) 99.99.99 (client/server) cvs-direct");
919 send_string(ctx, "version\n");
920 read_line(ctx, lbuff);
921 if (memcmp(lbuff, "M ", 2) == 0)
922 sprintf(server_version, "Server: %s", lbuff + 2);
923 else
924 debug(DEBUG_APPERROR, "cvs_direct: didn't read version: %s", lbuff);
926 read_line(ctx, lbuff);
927 if (strcmp(lbuff, "ok") != 0)
928 debug(DEBUG_APPERROR, "cvs_direct: protocol error reading version");
930 debug(DEBUG_TCP, "cvs_direct: client version %s", client_version);
931 debug(DEBUG_TCP, "cvs_direct: server version %s", server_version);