Use regex.[ch] from msysGit's /git
[cvsps/4msysgit.git] / cvs_direct.c
blob38e3c7dce40d176961f365dcd0ba667fe0079fe5
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 #ifdef __MINGW32__
16 #include <winsock2.h>
17 #define SHUT_WR SD_SEND
18 #define DEV_NULL "nul"
19 #else
20 #include <sys/socket.h>
21 #define DEV_NULL "/dev/null"
22 #endif
23 #include <cbtcommon/debug.h>
24 #include <cbtcommon/text_util.h>
25 #include <cbtcommon/tcpsocket.h>
26 #include <cbtcommon/sio.h>
28 #include "cvs_direct.h"
29 #include "util.h"
31 #define RD_BUFF_SIZE 4096
33 struct _CvsServerCtx
35 int read_fd;
36 int write_fd;
37 char root[PATH_MAX];
39 int is_pserver;
41 /* buffered reads from descriptor */
42 char read_buff[RD_BUFF_SIZE];
43 char * head;
44 char * tail;
46 int compressed;
47 z_stream zout;
48 z_stream zin;
50 /* when reading compressed data, the compressed data buffer */
51 char zread_buff[RD_BUFF_SIZE];
54 static void get_cvspass(char *, const char *);
55 static void send_string(CvsServerCtx *, const char *, ...);
56 static int read_response(CvsServerCtx *, const char *);
57 static void ctx_to_fp(CvsServerCtx * ctx, FILE * fp);
58 static int read_line(CvsServerCtx * ctx, char * p);
60 static CvsServerCtx * open_ctx_pserver(CvsServerCtx *, const char *);
61 static CvsServerCtx * open_ctx_forked(CvsServerCtx *, const char *);
63 CvsServerCtx * open_cvs_server(char * p_root, int compress)
65 CvsServerCtx * ctx = (CvsServerCtx*)malloc(sizeof(*ctx));
66 char root[PATH_MAX];
67 char * p = root, *tok;
69 if (!ctx)
70 return NULL;
72 ctx->head = ctx->tail = ctx->read_buff;
73 ctx->read_fd = ctx->write_fd = -1;
74 ctx->compressed = 0;
75 ctx->is_pserver = 0;
77 if (compress)
79 memset(&ctx->zout, 0, sizeof(z_stream));
80 memset(&ctx->zin, 0, sizeof(z_stream));
82 /*
83 * to 'prime' the reads, make it look like there was output
84 * room available (i.e. we have processed all pending compressed
85 * data
87 ctx->zin.avail_out = 1;
89 if (deflateInit(&ctx->zout, compress) != Z_OK)
91 free(ctx);
92 return NULL;
95 if (inflateInit(&ctx->zin) != Z_OK)
97 deflateEnd(&ctx->zout);
98 free(ctx);
99 return NULL;
103 strcpy(root, p_root);
105 tok = strsep(&p, ":");
107 /* if root string looks like :pserver:... then the first token will be empty */
108 if (strlen(tok) == 0)
110 char * method = strsep(&p, ":");
111 if (strcmp(method, "pserver") == 0)
113 ctx = open_ctx_pserver(ctx, p);
115 else if (strstr("local:ext:fork:server", method))
117 /* handle all of these via fork, even local */
118 ctx = open_ctx_forked(ctx, p);
120 else
122 debug(DEBUG_APPERROR, "cvs_direct: unsupported cvs access method: %s", method);
123 free(ctx);
124 ctx = NULL;
127 else
129 ctx = open_ctx_forked(ctx, p_root);
132 if (ctx)
134 char buff[BUFSIZ];
136 send_string(ctx, "Root %s\n", ctx->root);
138 /* this is taken from 1.11.1p1 trace - but with Mbinary removed. we can't handle it (yet!) */
139 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);
141 send_string(ctx, "valid-requests\n");
143 /* check for the commands we will issue */
144 read_line(ctx, buff);
145 if (strncmp(buff, "Valid-requests", 14) != 0)
147 debug(DEBUG_APPERROR, "cvs_direct: bad response to valid-requests command");
148 close_cvs_server(ctx);
149 return NULL;
152 if (!strstr(buff, " version") ||
153 !strstr(buff, " rlog") ||
154 !strstr(buff, " rdiff") ||
155 !strstr(buff, " diff") ||
156 !strstr(buff, " co"))
158 debug(DEBUG_APPERROR, "cvs_direct: cvs server too old for cvs_direct");
159 close_cvs_server(ctx);
160 return NULL;
163 read_line(ctx, buff);
164 if (strcmp(buff, "ok") != 0)
166 debug(DEBUG_APPERROR, "cvs_direct: bad ok trailer to valid-requests command");
167 close_cvs_server(ctx);
168 return NULL;
171 /* this is myterious but 'mandatory' */
172 send_string(ctx, "UseUnchanged\n");
174 if (compress)
176 send_string(ctx, "Gzip-stream %d\n", compress);
177 ctx->compressed = 1;
180 debug(DEBUG_APPMSG1, "cvs_direct initialized to CVSROOT %s", ctx->root);
183 return ctx;
186 static CvsServerCtx * open_ctx_pserver(CvsServerCtx * ctx, const char * p_root)
188 char root[PATH_MAX];
189 char full_root[PATH_MAX];
190 char * p = root, *tok, *tok2;
191 char user[BUFSIZ];
192 char server[BUFSIZ];
193 char pass[BUFSIZ];
194 char port[8];
196 strcpy(root, p_root);
198 tok = strsep(&p, ":");
199 if (strlen(tok) == 0 || !p)
201 debug(DEBUG_APPERROR, "parse error on third token");
202 goto out_free_err;
205 tok2 = strsep(&tok, "@");
206 if (!strlen(tok2) || (!tok || !strlen(tok)))
208 debug(DEBUG_APPERROR, "parse error on user@server in pserver");
209 goto out_free_err;
212 strcpy(user, tok2);
213 strcpy(server, tok);
215 if (*p != '/')
217 tok = strchr(p, '/');
218 if (!tok)
220 debug(DEBUG_APPERROR, "parse error: expecting / in root");
221 goto out_free_err;
224 memset(port, 0, sizeof(port));
225 memcpy(port, p, tok - p);
227 p = tok;
229 else
231 strcpy(port, "2401");
234 #ifdef __MINGW32__
235 /* the line from registry does not contain port, so rebuild */
236 snprintf(full_root, PATH_MAX, ":pserver:%s@%s:%s", user, server, p);
237 #else
238 /* the line from .cvspass is fully qualified, so rebuild */
239 snprintf(full_root, PATH_MAX, ":pserver:%s@%s:%s%s", user, server, port, p);
240 #endif
241 get_cvspass(pass, full_root);
243 debug(DEBUG_TCP, "user:%s server:%s port:%s pass:%s full_root:%s", user, server, port, pass, full_root);
245 if ((ctx->read_fd = tcp_create_socket(REUSE_ADDR)) < 0)
246 goto out_free_err;
248 ctx->write_fd = dup(ctx->read_fd);
250 if (tcp_connect(ctx->read_fd, server, atoi(port)) < 0)
251 goto out_close_err;
253 send_string(ctx, "BEGIN AUTH REQUEST\n");
254 send_string(ctx, "%s\n", p);
255 send_string(ctx, "%s\n", user);
256 send_string(ctx, "%s\n", pass);
257 send_string(ctx, "END AUTH REQUEST\n");
259 if (!read_response(ctx, "I LOVE YOU"))
260 goto out_close_err;
262 strcpy(ctx->root, p);
263 ctx->is_pserver = 1;
265 return ctx;
267 out_close_err:
268 close(ctx->read_fd);
269 out_free_err:
270 free(ctx);
271 return NULL;
274 static CvsServerCtx * open_ctx_forked(CvsServerCtx * ctx, const char * p_root)
276 #ifdef __MINGW32__
277 debug(DEBUG_SYSERROR, "cvs_direct: fork not supported on MinGW");
278 #else
279 char root[PATH_MAX];
280 char * p = root, *tok, *tok2, *rep;
281 char execcmd[PATH_MAX];
282 int to_cvs[2];
283 int from_cvs[2];
284 pid_t pid;
285 const char * cvs_server = getenv("CVS_SERVER");
287 if (!cvs_server)
288 cvs_server = "cvs";
290 strcpy(root, p_root);
292 /* if there's a ':', it's remote */
293 tok = strsep(&p, ":");
295 if (p)
297 const char * cvs_rsh = getenv("CVS_RSH");
299 if (!cvs_rsh)
300 cvs_rsh = "rsh";
302 tok2 = strsep(&tok, "@");
304 if (tok)
305 snprintf(execcmd, PATH_MAX, "%s -l %s %s %s server", cvs_rsh, tok2, tok, cvs_server);
306 else
307 snprintf(execcmd, PATH_MAX, "%s %s %s server", cvs_rsh, tok2, cvs_server);
309 rep = p;
311 else
313 snprintf(execcmd, PATH_MAX, "%s server", cvs_server);
314 rep = tok;
317 if (pipe(to_cvs) < 0)
319 debug(DEBUG_SYSERROR, "cvs_direct: failed to create pipe to_cvs");
320 goto out_free_err;
323 if (pipe(from_cvs) < 0)
325 debug(DEBUG_SYSERROR, "cvs_direct: failed to create pipe from_cvs");
326 goto out_close_err;
329 debug(DEBUG_TCP, "forked cmdline: %s", execcmd);
331 if ((pid = fork()) < 0)
333 debug(DEBUG_SYSERROR, "cvs_direct: can't fork");
334 goto out_close2_err;
336 else if (pid == 0) /* child */
338 char * argp[4];
339 argp[0] = "sh";
340 argp[1] = "-c";
341 argp[2] = execcmd;
342 argp[3] = NULL;
344 close(to_cvs[1]);
345 close(from_cvs[0]);
347 close(0);
348 dup(to_cvs[0]);
349 close(1);
350 dup(from_cvs[1]);
352 execv("/bin/sh",argp);
354 debug(DEBUG_APPERROR, "cvs_direct: fatal: shouldn't be reached");
355 exit(1);
358 close(to_cvs[0]);
359 close(from_cvs[1]);
360 ctx->read_fd = from_cvs[0];
361 ctx->write_fd = to_cvs[1];
363 strcpy(ctx->root, rep);
365 return ctx;
367 out_close2_err:
368 close(from_cvs[0]);
369 close(from_cvs[1]);
370 out_close_err:
371 close(to_cvs[0]);
372 close(to_cvs[1]);
373 out_free_err:
374 free(ctx);
375 #endif
376 return NULL;
379 void close_cvs_server(CvsServerCtx * ctx)
381 /* FIXME: some sort of flushing should be done for non-compressed case */
383 if (ctx->compressed)
385 int ret, len;
386 char buff[BUFSIZ];
389 * there shouldn't be anything left, but we do want
390 * to send an 'end of stream' marker, (if such a thing
391 * actually exists..)
395 ctx->zout.next_out = buff;
396 ctx->zout.avail_out = BUFSIZ;
397 ret = deflate(&ctx->zout, Z_FINISH);
399 if ((ret == Z_OK || ret == Z_STREAM_END) && ctx->zout.avail_out != BUFSIZ)
401 len = BUFSIZ - ctx->zout.avail_out;
402 if (writen(ctx->write_fd, buff, len) != len)
403 debug(DEBUG_APPERROR, "cvs_direct: zout: error writing final state");
405 //hexdump(buff, len, "cvs_direct: zout: sending unsent data");
407 } while (ret == Z_OK);
409 if ((ret = deflateEnd(&ctx->zout)) != Z_OK)
410 debug(DEBUG_APPERROR, "cvs_direct: zout: deflateEnd error: %s: %s",
411 (ret == Z_STREAM_ERROR) ? "Z_STREAM_ERROR":"Z_DATA_ERROR", ctx->zout.msg);
414 /* we're done writing now */
415 debug(DEBUG_TCP, "cvs_direct: closing cvs server write connection %d", ctx->write_fd);
416 close(ctx->write_fd);
419 * if this is pserver, then read_fd is a bi-directional socket.
420 * we want to shutdown the write side, just to make sure the
421 * server get's eof
423 if (ctx->is_pserver)
425 debug(DEBUG_TCP, "cvs_direct: shutdown on read socket");
426 if (shutdown(ctx->read_fd, SHUT_WR) < 0)
427 debug(DEBUG_SYSERROR, "cvs_direct: error with shutdown on pserver socket");
430 if (ctx->compressed)
432 int ret = Z_OK, len, eof = 0;
433 char buff[BUFSIZ];
435 /* read to the 'eof'/'eos' marker. there are two states we
436 * track, looking for Z_STREAM_END (application level EOS)
437 * and EOF on socket. Both should happen at the same time,
438 * but we need to do the read first, the first time through
439 * the loop, but we want to do one read after getting Z_STREAM_END
440 * too. so this loop has really ugly exit conditions.
442 for(;;)
445 * if there's nothing in the avail_in, and we
446 * inflated everything last pass (avail_out != 0)
447 * then slurp some more from the descriptor,
448 * if we get EOF, exit the loop
450 if (ctx->zin.avail_in == 0 && ctx->zin.avail_out != 0)
452 debug(DEBUG_TCP, "cvs_direct: doing final slurp");
453 len = read(ctx->read_fd, ctx->zread_buff, RD_BUFF_SIZE);
454 debug(DEBUG_TCP, "cvs_direct: did final slurp: %d", len);
456 if (len <= 0)
458 eof = 1;
459 break;
462 /* put the data into the inflate input stream */
463 ctx->zin.next_in = ctx->zread_buff;
464 ctx->zin.avail_in = len;
468 * if the last time through we got Z_STREAM_END, and we
469 * get back here, it means we should've gotten EOF but
470 * didn't
472 if (ret == Z_STREAM_END)
473 break;
475 ctx->zin.next_out = buff;
476 ctx->zin.avail_out = BUFSIZ;
478 ret = inflate(&ctx->zin, Z_SYNC_FLUSH);
479 len = BUFSIZ - ctx->zin.avail_out;
481 if (ret == Z_BUF_ERROR)
482 debug(DEBUG_APPERROR, "Z_BUF_ERROR");
484 if (ret == Z_OK && len == 0)
485 debug(DEBUG_TCP, "cvs_direct: no data out of inflate");
487 if (ret == Z_STREAM_END)
488 debug(DEBUG_TCP, "cvs_direct: got Z_STREAM_END");
490 if ((ret == Z_OK || ret == Z_STREAM_END) && len > 0)
491 hexdump(buff, BUFSIZ - ctx->zin.avail_out, "cvs_direct: zin: unread data at close");
494 if (ret != Z_STREAM_END)
495 debug(DEBUG_APPERROR, "cvs_direct: zin: Z_STREAM_END not encountered (premature EOF?)");
497 if (eof == 0)
498 debug(DEBUG_APPERROR, "cvs_direct: zin: EOF not encountered (premature Z_STREAM_END?)");
500 if ((ret = inflateEnd(&ctx->zin)) != Z_OK)
501 debug(DEBUG_APPERROR, "cvs_direct: zin: inflateEnd error: %s: %s",
502 (ret == Z_STREAM_ERROR) ? "Z_STREAM_ERROR":"Z_DATA_ERROR", ctx->zin.msg ? ctx->zin.msg : "");
505 debug(DEBUG_TCP, "cvs_direct: closing cvs server read connection %d", ctx->read_fd);
506 close(ctx->read_fd);
508 free(ctx);
511 static void get_cvspass(char * pass, const char * root)
513 #ifdef __MINGW32__
514 /* CVSNT stores password in registry HKCU\Software\Cvsnt\cvspass
515 * See http://issues.apache.org/bugzilla/show_bug.cgi?id=21657#c5
516 * See http://www.adp-gmbh.ch/misc/tools/cvs/cvspass.html
518 HKEY hKey;
519 if (RegOpenKeyExA(HKEY_CURRENT_USER,"Software\\Cvsnt\\cvspass",0,KEY_READ,&hKey))
521 debug(DEBUG_APPERROR, "Cannot open HKCU\\Software\\Cvsnt\\cvspass registry key");
522 exit(1);
524 /* "root" is connection string that require a password */
525 static char buf[4096];
526 DWORD dwType,dwLen;
527 LONG ret;
528 dwLen = sizeof(buf);
529 ret = RegQueryValueExA(hKey,root,NULL,&dwType,(LPBYTE)buf,&dwLen);
530 RegCloseKey(hKey);
531 if (ret != ERROR_SUCCESS)
533 debug(DEBUG_APPERROR, "HKCU\\Software\\Cvsnt\\cvspass registry key opened, but could not read value '%s'", root);
534 debug(DEBUG_APPERROR, "HKCU\\Software\\Cvsnt\\cvspass registry key not readable");
535 exit(1);
537 strcpy(pass,buf); /* If this function knew the size of pass, we could
538 use strncpy and avoid potential buffer-overflow. */
539 #else
540 char cvspass[PATH_MAX];
541 const char * home;
542 FILE * fp;
544 pass[0] = 0;
546 if (!(home = getenv("HOME")))
548 debug(DEBUG_APPERROR, "HOME environment variable not set");
549 exit(1);
552 if (snprintf(cvspass, PATH_MAX, "%s/.cvspass", home) >= PATH_MAX)
554 debug(DEBUG_APPERROR, "prefix buffer overflow");
555 exit(1);
558 if ((fp = fopen(cvspass, "r")))
560 char buff[BUFSIZ];
561 int len = strlen(root);
563 while (fgets(buff, BUFSIZ, fp))
565 /* FIXME: what does /1 mean? */
566 if (strncmp(buff, "/1 ", 3) != 0)
567 continue;
569 if (strncmp(buff + 3, root, len) == 0)
571 strcpy(pass, buff + 3 + len + 1);
572 chop(pass);
573 break;
577 fclose(fp);
580 if (!pass[0])
581 pass[0] = 'A';
582 #endif
585 static void send_string(CvsServerCtx * ctx, const char * str, ...)
587 int len;
588 char buff[BUFSIZ];
589 va_list ap;
591 va_start(ap, str);
593 len = vsnprintf(buff, BUFSIZ, str, ap);
594 if (len >= BUFSIZ)
596 debug(DEBUG_APPERROR, "cvs_direct: command send string overflow");
597 exit(1);
600 if (ctx->compressed)
602 char zbuff[BUFSIZ];
604 if (ctx->zout.avail_in != 0)
606 debug(DEBUG_APPERROR, "cvs_direct: zout: last output command not flushed");
607 exit(1);
610 ctx->zout.next_in = buff;
611 ctx->zout.avail_in = len;
612 ctx->zout.avail_out = 0;
614 while (ctx->zout.avail_in > 0 || ctx->zout.avail_out == 0)
616 int ret;
618 ctx->zout.next_out = zbuff;
619 ctx->zout.avail_out = BUFSIZ;
621 /* FIXME: for the arguments before a command, flushing is counterproductive */
622 ret = deflate(&ctx->zout, Z_SYNC_FLUSH);
624 if (ret == Z_OK)
626 len = BUFSIZ - ctx->zout.avail_out;
628 if (writen(ctx->write_fd, zbuff, len) != len)
630 debug(DEBUG_SYSERROR, "cvs_direct: zout: can't write");
631 exit(1);
634 else
636 debug(DEBUG_APPERROR, "cvs_direct: zout: error %d %s", ret, ctx->zout.msg);
640 else
642 if (writen(ctx->write_fd, buff, len) != len)
644 debug(DEBUG_SYSERROR, "cvs_direct: can't send command");
645 exit(1);
649 debug(DEBUG_TCP, "string: '%s' sent", buff);
652 static int refill_buffer(CvsServerCtx * ctx)
654 int len;
656 if (ctx->head != ctx->tail)
658 debug(DEBUG_APPERROR, "cvs_direct: refill_buffer called on non-empty buffer");
659 exit(1);
662 ctx->head = ctx->read_buff;
663 len = RD_BUFF_SIZE;
665 if (ctx->compressed)
667 int zlen, ret;
669 /* if there was leftover buffer room, it's time to slurp more data */
672 if (ctx->zin.avail_out > 0)
674 if (ctx->zin.avail_in != 0)
676 debug(DEBUG_APPERROR, "cvs_direct: zin: expect 0 avail_in");
677 exit(1);
679 zlen = read(ctx->read_fd, ctx->zread_buff, RD_BUFF_SIZE);
680 ctx->zin.next_in = ctx->zread_buff;
681 ctx->zin.avail_in = zlen;
684 ctx->zin.next_out = ctx->head;
685 ctx->zin.avail_out = len;
687 /* FIXME: we don't always need Z_SYNC_FLUSH, do we? */
688 ret = inflate(&ctx->zin, Z_SYNC_FLUSH);
690 while (ctx->zin.avail_out == len);
692 if (ret == Z_OK)
694 ctx->tail = ctx->head + (len - ctx->zin.avail_out);
696 else
698 debug(DEBUG_APPERROR, "cvs_direct: zin: error %d %s", ret, ctx->zin.msg);
699 exit(1);
702 else
704 len = read(ctx->read_fd, ctx->head, len);
705 ctx->tail = (len <= 0) ? ctx->head : ctx->head + len;
708 return len;
711 static int read_line(CvsServerCtx * ctx, char * p)
713 int len = 0;
714 while (1)
716 if (ctx->head == ctx->tail)
717 if (refill_buffer(ctx) <= 0)
718 return -1;
720 *p = *ctx->head++;
722 if (*p == '\n')
724 *p = 0;
725 break;
727 p++;
728 len++;
731 return len;
734 static int read_response(CvsServerCtx * ctx, const char * str)
736 /* FIXME: more than 1 char at a time */
737 char resp[BUFSIZ];
739 if (read_line(ctx, resp) < 0)
740 return 0;
742 debug(DEBUG_TCP, "response '%s' read", resp);
744 return (strcmp(resp, str) == 0);
747 static void ctx_to_fp(CvsServerCtx * ctx, FILE * fp)
749 char line[BUFSIZ];
751 while (1)
753 read_line(ctx, line);
754 debug(DEBUG_TCP, "ctx_to_fp: %s", line);
755 if (memcmp(line, "M ", 2) == 0)
757 if (fp)
758 fprintf(fp, "%s\n", line + 2);
760 else if (memcmp(line, "E ", 2) == 0)
762 debug(DEBUG_APPMSG1, "%s", line + 2);
764 else if (strncmp(line, "ok", 2) == 0 || strncmp(line, "error", 5) == 0)
766 break;
770 if (fp)
771 fflush(fp);
774 void cvs_rdiff(CvsServerCtx * ctx,
775 const char * rep, const char * file,
776 const char * rev1, const char * rev2)
778 /* NOTE: opts are ignored for rdiff, '-u' is always used */
780 send_string(ctx, "Argument -u\n");
781 send_string(ctx, "Argument -r\n");
782 send_string(ctx, "Argument %s\n", rev1);
783 send_string(ctx, "Argument -r\n");
784 send_string(ctx, "Argument %s\n", rev2);
785 send_string(ctx, "Argument %s%s\n", rep, file);
786 send_string(ctx, "rdiff\n");
788 ctx_to_fp(ctx, stdout);
791 void cvs_rupdate(CvsServerCtx * ctx, const char * rep, const char * file, const char * rev, int create, const char * opts)
793 FILE * fp;
794 char cmdbuff[BUFSIZ];
796 snprintf(cmdbuff, BUFSIZ, "diff %s %s %s | sed -e '%s s|^\\([+-][+-][+-]\\) -|\\1 %s/%s|g'",
797 opts, create?DEV_NULL:"-", create?"-":DEV_NULL, create?"2":"1", rep, file);
799 debug(DEBUG_TCP, "cmdbuff: %s", cmdbuff);
801 if (!(fp = popen(cmdbuff, "w")))
803 debug(DEBUG_APPERROR, "cvs_direct: popen for diff failed: %s", cmdbuff);
804 exit(1);
807 send_string(ctx, "Argument -p\n");
808 send_string(ctx, "Argument -r\n");
809 send_string(ctx, "Argument %s\n", rev);
810 send_string(ctx, "Argument %s/%s\n", rep, file);
811 send_string(ctx, "co\n");
813 ctx_to_fp(ctx, fp);
815 pclose(fp);
818 static int parse_patch_arg(char * arg, char ** str)
820 char *tok, *tok2 = "";
821 tok = strsep(str, " ");
822 if (!tok)
823 return 0;
825 if (!*tok == '-')
827 debug(DEBUG_APPERROR, "diff_opts parse error: no '-' starting argument: %s", *str);
828 return 0;
831 /* if it's not 'long format' argument, we can process it efficiently */
832 if (tok[1] == '-')
834 debug(DEBUG_APPERROR, "diff_opts parse_error: long format args not supported");
835 return 0;
838 /* see if command wants two args and they're separated by ' ' */
839 if (tok[2] == 0 && strchr("BdDFgiorVxYz", tok[1]))
841 tok2 = strsep(str, " ");
842 if (!tok2)
844 debug(DEBUG_APPERROR, "diff_opts parse_error: argument %s requires two arguments", tok);
845 return 0;
849 snprintf(arg, 32, "%s%s", tok, tok2);
850 return 1;
853 void cvs_diff(CvsServerCtx * ctx,
854 const char * rep, const char * file,
855 const char * rev1, const char * rev2, const char * opts)
857 char argstr[BUFSIZ], *p = argstr;
858 char arg[32];
859 char file_buff[PATH_MAX], *basename;
861 strzncpy(argstr, opts, BUFSIZ);
862 while (parse_patch_arg(arg, &p))
863 send_string(ctx, "Argument %s\n", arg);
865 send_string(ctx, "Argument -r\n");
866 send_string(ctx, "Argument %s\n", rev1);
867 send_string(ctx, "Argument -r\n");
868 send_string(ctx, "Argument %s\n", rev2);
871 * we need to separate the 'basename' of file in order to
872 * generate the Directory directive(s)
874 strzncpy(file_buff, file, PATH_MAX);
875 if ((basename = strrchr(file_buff, '/')))
877 *basename = 0;
878 send_string(ctx, "Directory %s/%s\n", rep, file_buff);
879 send_string(ctx, "%s/%s/%s\n", ctx->root, rep, file_buff);
881 else
883 send_string(ctx, "Directory %s\n", rep, file_buff);
884 send_string(ctx, "%s/%s\n", ctx->root, rep);
887 send_string(ctx, "Directory .\n");
888 send_string(ctx, "%s\n", ctx->root);
889 send_string(ctx, "Argument %s/%s\n", rep, file);
890 send_string(ctx, "diff\n");
892 ctx_to_fp(ctx, stdout);
896 * FIXME: the design of this sucks. It was originally designed to fork a subprocess
897 * which read the cvs response and send it back through a pipe the main process,
898 * which fdopen(3)ed the other end, and juts used regular fgets. This however
899 * didn't work because the reads of compressed data in the child process altered
900 * the compression state, and there was no way to resynchronize that state with
901 * the parent process. We could use threads...
903 FILE * cvs_rlog_open(CvsServerCtx * ctx, const char * rep, const char * date_str)
905 /* note: use of the date_str is handled in a non-standard, cvsps specific way */
906 if (date_str && date_str[0])
908 send_string(ctx, "Argument -d\n", rep);
909 send_string(ctx, "Argument %s<1 Jan 2038 05:00:00 -0000\n", date_str);
910 send_string(ctx, "Argument -d\n", rep);
911 send_string(ctx, "Argument %s\n", date_str);
914 send_string(ctx, "Argument %s\n", rep);
915 send_string(ctx, "rlog\n");
918 * FIXME: is it possible to create a 'fake' FILE * whose 'refill'
919 * function is below?
921 return (FILE*)ctx;
924 char * cvs_rlog_fgets(char * buff, int buflen, CvsServerCtx * ctx)
926 char lbuff[BUFSIZ];
927 int len;
929 len = read_line(ctx, lbuff);
930 debug(DEBUG_TCP, "cvs_direct: rlog: read %s", lbuff);
932 if (memcmp(lbuff, "M ", 2) == 0)
934 memcpy(buff, lbuff + 2, len - 2);
935 buff[len - 2 ] = '\n';
936 buff[len - 1 ] = 0;
938 else if (memcmp(lbuff, "E ", 2) == 0)
940 debug(DEBUG_APPMSG1, "%s", lbuff + 2);
942 else if (strcmp(lbuff, "ok") == 0 ||strcmp(lbuff, "error") == 0)
944 debug(DEBUG_TCP, "cvs_direct: rlog: got command completion");
945 return NULL;
948 return buff;
951 void cvs_rlog_close(CvsServerCtx * ctx)
955 void cvs_version(CvsServerCtx * ctx, char * client_version, char * server_version)
957 char lbuff[BUFSIZ];
958 strcpy(client_version, "Client: Concurrent Versions System (CVS) 99.99.99 (client/server) cvs-direct");
959 send_string(ctx, "version\n");
960 read_line(ctx, lbuff);
961 if (memcmp(lbuff, "M ", 2) == 0)
962 sprintf(server_version, "Server: %s", lbuff + 2);
963 else
964 debug(DEBUG_APPERROR, "cvs_direct: didn't read version: %s", lbuff);
966 read_line(ctx, lbuff);
967 if (strcmp(lbuff, "ok") != 0)
968 debug(DEBUG_APPERROR, "cvs_direct: protocol error reading version");
970 debug(DEBUG_TCP, "cvs_direct: client version %s", client_version);
971 debug(DEBUG_TCP, "cvs_direct: server version %s", server_version);