fix-macintosh.p
[cvsps/4msysgit.git] / cvs_direct.c
blob06011600f223fa97ae41adcd4e15448fafb01835
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 #else
19 #include <sys/socket.h>
20 #endif
21 #include <cbtcommon/debug.h>
22 #include <cbtcommon/text_util.h>
23 #include <cbtcommon/tcpsocket.h>
24 #include <cbtcommon/sio.h>
26 #include "cvs_direct.h"
27 #include "util.h"
29 #define RD_BUFF_SIZE 4096
31 struct _CvsServerCtx
33 int read_fd;
34 int write_fd;
35 char root[PATH_MAX];
37 int is_pserver;
39 /* buffered reads from descriptor */
40 char read_buff[RD_BUFF_SIZE];
41 char * head;
42 char * tail;
44 int compressed;
45 z_stream zout;
46 z_stream zin;
48 /* when reading compressed data, the compressed data buffer */
49 char zread_buff[RD_BUFF_SIZE];
52 static void get_cvspass(char *, const char *);
53 static void send_string(CvsServerCtx *, const char *, ...);
54 static int read_response(CvsServerCtx *, const char *);
55 static void ctx_to_fp(CvsServerCtx * ctx, FILE * fp);
56 static int read_line(CvsServerCtx * ctx, char * p);
58 static CvsServerCtx * open_ctx_pserver(CvsServerCtx *, const char *);
59 static CvsServerCtx * open_ctx_forked(CvsServerCtx *, const char *);
61 CvsServerCtx * open_cvs_server(char * p_root, int compress)
63 CvsServerCtx * ctx = (CvsServerCtx*)malloc(sizeof(*ctx));
64 char root[PATH_MAX];
65 char * p = root, *tok;
67 if (!ctx)
68 return NULL;
70 ctx->head = ctx->tail = ctx->read_buff;
71 ctx->read_fd = ctx->write_fd = -1;
72 ctx->compressed = 0;
73 ctx->is_pserver = 0;
75 if (compress)
77 memset(&ctx->zout, 0, sizeof(z_stream));
78 memset(&ctx->zin, 0, sizeof(z_stream));
80 /*
81 * to 'prime' the reads, make it look like there was output
82 * room available (i.e. we have processed all pending compressed
83 * data
85 ctx->zin.avail_out = 1;
87 if (deflateInit(&ctx->zout, compress) != Z_OK)
89 free(ctx);
90 return NULL;
93 if (inflateInit(&ctx->zin) != Z_OK)
95 deflateEnd(&ctx->zout);
96 free(ctx);
97 return NULL;
101 strcpy(root, p_root);
103 tok = strsep(&p, ":");
105 /* if root string looks like :pserver:... then the first token will be empty */
106 if (strlen(tok) == 0)
108 char * method = strsep(&p, ":");
109 if (strcmp(method, "pserver") == 0)
111 ctx = open_ctx_pserver(ctx, p);
113 else if (strstr("local:ext:fork:server", method))
115 /* handle all of these via fork, even local */
116 ctx = open_ctx_forked(ctx, p);
118 else
120 debug(DEBUG_APPERROR, "cvs_direct: unsupported cvs access method: %s", method);
121 free(ctx);
122 ctx = NULL;
125 else
127 ctx = open_ctx_forked(ctx, p_root);
130 if (ctx)
132 char buff[BUFSIZ];
134 send_string(ctx, "Root %s\n", ctx->root);
136 /* this is taken from 1.11.1p1 trace - but with Mbinary removed. we can't handle it (yet!) */
137 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);
139 send_string(ctx, "valid-requests\n");
141 /* check for the commands we will issue */
142 read_line(ctx, buff);
143 if (strncmp(buff, "Valid-requests", 14) != 0)
145 debug(DEBUG_APPERROR, "cvs_direct: bad response to valid-requests command");
146 close_cvs_server(ctx);
147 return NULL;
150 if (!strstr(buff, " version") ||
151 !strstr(buff, " rlog") ||
152 !strstr(buff, " rdiff") ||
153 !strstr(buff, " diff") ||
154 !strstr(buff, " co"))
156 debug(DEBUG_APPERROR, "cvs_direct: cvs server too old for cvs_direct");
157 close_cvs_server(ctx);
158 return NULL;
161 read_line(ctx, buff);
162 if (strcmp(buff, "ok") != 0)
164 debug(DEBUG_APPERROR, "cvs_direct: bad ok trailer to valid-requests command");
165 close_cvs_server(ctx);
166 return NULL;
169 /* this is myterious but 'mandatory' */
170 send_string(ctx, "UseUnchanged\n");
172 if (compress)
174 send_string(ctx, "Gzip-stream %d\n", compress);
175 ctx->compressed = 1;
178 debug(DEBUG_APPMSG1, "cvs_direct initialized to CVSROOT %s", ctx->root);
181 return ctx;
184 static CvsServerCtx * open_ctx_pserver(CvsServerCtx * ctx, const char * p_root)
186 char root[PATH_MAX];
187 char full_root[PATH_MAX];
188 char * p = root, *tok, *tok2;
189 char user[BUFSIZ];
190 char server[BUFSIZ];
191 char pass[BUFSIZ];
192 char port[8];
194 strcpy(root, p_root);
196 tok = strsep(&p, ":");
197 if (strlen(tok) == 0 || !p)
199 debug(DEBUG_APPERROR, "parse error on third token");
200 goto out_free_err;
203 tok2 = strsep(&tok, "@");
204 if (!strlen(tok2) || (!tok || !strlen(tok)))
206 debug(DEBUG_APPERROR, "parse error on user@server in pserver");
207 goto out_free_err;
210 strcpy(user, tok2);
211 strcpy(server, tok);
213 if (*p != '/')
215 tok = strchr(p, '/');
216 if (!tok)
218 debug(DEBUG_APPERROR, "parse error: expecting / in root");
219 goto out_free_err;
222 memset(port, 0, sizeof(port));
223 memcpy(port, p, tok - p);
225 p = tok;
227 else
229 strcpy(port, "2401");
232 #ifdef __MINGW32__
233 /* the line from registry does not contain port, so rebuild */
234 snprintf(full_root, PATH_MAX, ":pserver:%s@%s:%s", user, server, p);
235 #else
236 /* the line from .cvspass is fully qualified, so rebuild */
237 snprintf(full_root, PATH_MAX, ":pserver:%s@%s:%s%s", user, server, port, p);
238 #endif
239 get_cvspass(pass, full_root);
241 debug(DEBUG_TCP, "user:%s server:%s port:%s pass:%s full_root:%s", user, server, port, pass, full_root);
243 if ((ctx->read_fd = tcp_create_socket(REUSE_ADDR)) < 0)
244 goto out_free_err;
246 ctx->write_fd = dup(ctx->read_fd);
248 if (tcp_connect(ctx->read_fd, server, atoi(port)) < 0)
249 goto out_close_err;
251 send_string(ctx, "BEGIN AUTH REQUEST\n");
252 send_string(ctx, "%s\n", p);
253 send_string(ctx, "%s\n", user);
254 send_string(ctx, "%s\n", pass);
255 send_string(ctx, "END AUTH REQUEST\n");
257 if (!read_response(ctx, "I LOVE YOU"))
258 goto out_close_err;
260 strcpy(ctx->root, p);
261 ctx->is_pserver = 1;
263 return ctx;
265 out_close_err:
266 close(ctx->read_fd);
267 out_free_err:
268 free(ctx);
269 return NULL;
272 static CvsServerCtx * open_ctx_forked(CvsServerCtx * ctx, const char * p_root)
274 #ifdef __MINGW32__
275 debug(DEBUG_SYSERROR, "cvs_direct: fork not supported on MinGW");
276 #else
277 char root[PATH_MAX];
278 char * p = root, *tok, *tok2, *rep;
279 char execcmd[PATH_MAX];
280 int to_cvs[2];
281 int from_cvs[2];
282 pid_t pid;
283 const char * cvs_server = getenv("CVS_SERVER");
285 if (!cvs_server)
286 cvs_server = "cvs";
288 strcpy(root, p_root);
290 /* if there's a ':', it's remote */
291 tok = strsep(&p, ":");
293 if (p)
295 const char * cvs_rsh = getenv("CVS_RSH");
297 if (!cvs_rsh)
298 cvs_rsh = "rsh";
300 tok2 = strsep(&tok, "@");
302 if (tok)
303 snprintf(execcmd, PATH_MAX, "%s -l %s %s %s server", cvs_rsh, tok2, tok, cvs_server);
304 else
305 snprintf(execcmd, PATH_MAX, "%s %s %s server", cvs_rsh, tok2, cvs_server);
307 rep = p;
309 else
311 snprintf(execcmd, PATH_MAX, "%s server", cvs_server);
312 rep = tok;
315 if (pipe(to_cvs) < 0)
317 debug(DEBUG_SYSERROR, "cvs_direct: failed to create pipe to_cvs");
318 goto out_free_err;
321 if (pipe(from_cvs) < 0)
323 debug(DEBUG_SYSERROR, "cvs_direct: failed to create pipe from_cvs");
324 goto out_close_err;
327 debug(DEBUG_TCP, "forked cmdline: %s", execcmd);
329 if ((pid = fork()) < 0)
331 debug(DEBUG_SYSERROR, "cvs_direct: can't fork");
332 goto out_close2_err;
334 else if (pid == 0) /* child */
336 char * argp[4];
337 argp[0] = "sh";
338 argp[1] = "-c";
339 argp[2] = execcmd;
340 argp[3] = NULL;
342 close(to_cvs[1]);
343 close(from_cvs[0]);
345 close(0);
346 dup(to_cvs[0]);
347 close(1);
348 dup(from_cvs[1]);
350 execv("/bin/sh",argp);
352 debug(DEBUG_APPERROR, "cvs_direct: fatal: shouldn't be reached");
353 exit(1);
356 close(to_cvs[0]);
357 close(from_cvs[1]);
358 ctx->read_fd = from_cvs[0];
359 ctx->write_fd = to_cvs[1];
361 strcpy(ctx->root, rep);
363 return ctx;
365 out_close2_err:
366 close(from_cvs[0]);
367 close(from_cvs[1]);
368 out_close_err:
369 close(to_cvs[0]);
370 close(to_cvs[1]);
371 out_free_err:
372 free(ctx);
373 #endif
374 return NULL;
377 void close_cvs_server(CvsServerCtx * ctx)
379 /* FIXME: some sort of flushing should be done for non-compressed case */
381 if (ctx->compressed)
383 int ret, len;
384 char buff[BUFSIZ];
387 * there shouldn't be anything left, but we do want
388 * to send an 'end of stream' marker, (if such a thing
389 * actually exists..)
393 ctx->zout.next_out = buff;
394 ctx->zout.avail_out = BUFSIZ;
395 ret = deflate(&ctx->zout, Z_FINISH);
397 if ((ret == Z_OK || ret == Z_STREAM_END) && ctx->zout.avail_out != BUFSIZ)
399 len = BUFSIZ - ctx->zout.avail_out;
400 if (writen(ctx->write_fd, buff, len) != len)
401 debug(DEBUG_APPERROR, "cvs_direct: zout: error writing final state");
403 //hexdump(buff, len, "cvs_direct: zout: sending unsent data");
405 } while (ret == Z_OK);
407 if ((ret = deflateEnd(&ctx->zout)) != Z_OK)
408 debug(DEBUG_APPERROR, "cvs_direct: zout: deflateEnd error: %s: %s",
409 (ret == Z_STREAM_ERROR) ? "Z_STREAM_ERROR":"Z_DATA_ERROR", ctx->zout.msg);
412 /* we're done writing now */
413 debug(DEBUG_TCP, "cvs_direct: closing cvs server write connection %d", ctx->write_fd);
414 close(ctx->write_fd);
417 * if this is pserver, then read_fd is a bi-directional socket.
418 * we want to shutdown the write side, just to make sure the
419 * server get's eof
421 if (ctx->is_pserver)
423 debug(DEBUG_TCP, "cvs_direct: shutdown on read socket");
424 if (shutdown(ctx->read_fd, SHUT_WR) < 0)
425 debug(DEBUG_SYSERROR, "cvs_direct: error with shutdown on pserver socket");
428 if (ctx->compressed)
430 int ret = Z_OK, len, eof = 0;
431 char buff[BUFSIZ];
433 /* read to the 'eof'/'eos' marker. there are two states we
434 * track, looking for Z_STREAM_END (application level EOS)
435 * and EOF on socket. Both should happen at the same time,
436 * but we need to do the read first, the first time through
437 * the loop, but we want to do one read after getting Z_STREAM_END
438 * too. so this loop has really ugly exit conditions.
440 for(;;)
443 * if there's nothing in the avail_in, and we
444 * inflated everything last pass (avail_out != 0)
445 * then slurp some more from the descriptor,
446 * if we get EOF, exit the loop
448 if (ctx->zin.avail_in == 0 && ctx->zin.avail_out != 0)
450 debug(DEBUG_TCP, "cvs_direct: doing final slurp");
451 len = read(ctx->read_fd, ctx->zread_buff, RD_BUFF_SIZE);
452 debug(DEBUG_TCP, "cvs_direct: did final slurp: %d", len);
454 if (len <= 0)
456 eof = 1;
457 break;
460 /* put the data into the inflate input stream */
461 ctx->zin.next_in = ctx->zread_buff;
462 ctx->zin.avail_in = len;
466 * if the last time through we got Z_STREAM_END, and we
467 * get back here, it means we should've gotten EOF but
468 * didn't
470 if (ret == Z_STREAM_END)
471 break;
473 ctx->zin.next_out = buff;
474 ctx->zin.avail_out = BUFSIZ;
476 ret = inflate(&ctx->zin, Z_SYNC_FLUSH);
477 len = BUFSIZ - ctx->zin.avail_out;
479 if (ret == Z_BUF_ERROR)
480 debug(DEBUG_APPERROR, "Z_BUF_ERROR");
482 if (ret == Z_OK && len == 0)
483 debug(DEBUG_TCP, "cvs_direct: no data out of inflate");
485 if (ret == Z_STREAM_END)
486 debug(DEBUG_TCP, "cvs_direct: got Z_STREAM_END");
488 if ((ret == Z_OK || ret == Z_STREAM_END) && len > 0)
489 hexdump(buff, BUFSIZ - ctx->zin.avail_out, "cvs_direct: zin: unread data at close");
492 if (ret != Z_STREAM_END)
493 debug(DEBUG_APPERROR, "cvs_direct: zin: Z_STREAM_END not encountered (premature EOF?)");
495 if (eof == 0)
496 debug(DEBUG_APPERROR, "cvs_direct: zin: EOF not encountered (premature Z_STREAM_END?)");
498 if ((ret = inflateEnd(&ctx->zin)) != Z_OK)
499 debug(DEBUG_APPERROR, "cvs_direct: zin: inflateEnd error: %s: %s",
500 (ret == Z_STREAM_ERROR) ? "Z_STREAM_ERROR":"Z_DATA_ERROR", ctx->zin.msg ? ctx->zin.msg : "");
503 debug(DEBUG_TCP, "cvs_direct: closing cvs server read connection %d", ctx->read_fd);
504 close(ctx->read_fd);
506 free(ctx);
509 static void get_cvspass(char * pass, const char * root)
511 #ifdef __MINGW32__
512 /* CVSNT stores password in registry HKCU\Software\Cvsnt\cvspass
513 * See http://issues.apache.org/bugzilla/show_bug.cgi?id=21657#c5
514 * See http://www.adp-gmbh.ch/misc/tools/cvs/cvspass.html
516 HKEY hKey;
517 if (RegOpenKeyExA(HKEY_CURRENT_USER,"Software\\Cvsnt\\cvspass",0,KEY_READ,&hKey))
519 debug(DEBUG_APPERROR, "Cannot open HKCU\\Software\\Cvsnt\\cvspass registry key");
520 exit(1);
522 /* "root" is connection string that require a password */
523 static char buf[4096];
524 DWORD dwType,dwLen;
525 LONG ret;
526 dwLen = sizeof(buf);
527 ret = RegQueryValueExA(hKey,root,NULL,&dwType,(LPBYTE)buf,&dwLen);
528 RegCloseKey(hKey);
529 if (ret != ERROR_SUCCESS)
531 debug(DEBUG_APPERROR, "HKCU\\Software\\Cvsnt\\cvspass registry key not readable");
532 exit(1);
534 strcpy(pass,buf); /* If this function knew the size of pass, we could
535 use strncpy and avoid potential buffer-overflow. */
536 #else
537 char cvspass[PATH_MAX];
538 const char * home;
539 FILE * fp;
541 pass[0] = 0;
543 if (!(home = getenv("HOME")))
545 debug(DEBUG_APPERROR, "HOME environment variable not set");
546 exit(1);
549 if (snprintf(cvspass, PATH_MAX, "%s/.cvspass", home) >= PATH_MAX)
551 debug(DEBUG_APPERROR, "prefix buffer overflow");
552 exit(1);
555 if ((fp = fopen(cvspass, "r")))
557 char buff[BUFSIZ];
558 int len = strlen(root);
560 while (fgets(buff, BUFSIZ, fp))
562 /* FIXME: what does /1 mean? */
563 if (strncmp(buff, "/1 ", 3) != 0)
564 continue;
566 if (strncmp(buff + 3, root, len) == 0)
568 strcpy(pass, buff + 3 + len + 1);
569 chop(pass);
570 break;
574 fclose(fp);
577 if (!pass[0])
578 pass[0] = 'A';
579 #endif
582 static void send_string(CvsServerCtx * ctx, const char * str, ...)
584 int len;
585 char buff[BUFSIZ];
586 va_list ap;
588 va_start(ap, str);
590 len = vsnprintf(buff, BUFSIZ, str, ap);
591 if (len >= BUFSIZ)
593 debug(DEBUG_APPERROR, "cvs_direct: command send string overflow");
594 exit(1);
597 if (ctx->compressed)
599 char zbuff[BUFSIZ];
601 if (ctx->zout.avail_in != 0)
603 debug(DEBUG_APPERROR, "cvs_direct: zout: last output command not flushed");
604 exit(1);
607 ctx->zout.next_in = buff;
608 ctx->zout.avail_in = len;
609 ctx->zout.avail_out = 0;
611 while (ctx->zout.avail_in > 0 || ctx->zout.avail_out == 0)
613 int ret;
615 ctx->zout.next_out = zbuff;
616 ctx->zout.avail_out = BUFSIZ;
618 /* FIXME: for the arguments before a command, flushing is counterproductive */
619 ret = deflate(&ctx->zout, Z_SYNC_FLUSH);
621 if (ret == Z_OK)
623 len = BUFSIZ - ctx->zout.avail_out;
625 if (writen(ctx->write_fd, zbuff, len) != len)
627 debug(DEBUG_SYSERROR, "cvs_direct: zout: can't write");
628 exit(1);
631 else
633 debug(DEBUG_APPERROR, "cvs_direct: zout: error %d %s", ret, ctx->zout.msg);
637 else
639 if (writen(ctx->write_fd, buff, len) != len)
641 debug(DEBUG_SYSERROR, "cvs_direct: can't send command");
642 exit(1);
646 debug(DEBUG_TCP, "string: '%s' sent", buff);
649 static int refill_buffer(CvsServerCtx * ctx)
651 int len;
653 if (ctx->head != ctx->tail)
655 debug(DEBUG_APPERROR, "cvs_direct: refill_buffer called on non-empty buffer");
656 exit(1);
659 ctx->head = ctx->read_buff;
660 len = RD_BUFF_SIZE;
662 if (ctx->compressed)
664 int zlen, ret;
666 /* if there was leftover buffer room, it's time to slurp more data */
669 if (ctx->zin.avail_out > 0)
671 if (ctx->zin.avail_in != 0)
673 debug(DEBUG_APPERROR, "cvs_direct: zin: expect 0 avail_in");
674 exit(1);
676 zlen = read(ctx->read_fd, ctx->zread_buff, RD_BUFF_SIZE);
677 ctx->zin.next_in = ctx->zread_buff;
678 ctx->zin.avail_in = zlen;
681 ctx->zin.next_out = ctx->head;
682 ctx->zin.avail_out = len;
684 /* FIXME: we don't always need Z_SYNC_FLUSH, do we? */
685 ret = inflate(&ctx->zin, Z_SYNC_FLUSH);
687 while (ctx->zin.avail_out == len);
689 if (ret == Z_OK)
691 ctx->tail = ctx->head + (len - ctx->zin.avail_out);
693 else
695 debug(DEBUG_APPERROR, "cvs_direct: zin: error %d %s", ret, ctx->zin.msg);
696 exit(1);
699 else
701 len = read(ctx->read_fd, ctx->head, len);
702 ctx->tail = (len <= 0) ? ctx->head : ctx->head + len;
705 return len;
708 static int read_line(CvsServerCtx * ctx, char * p)
710 int len = 0;
711 while (1)
713 if (ctx->head == ctx->tail)
714 if (refill_buffer(ctx) <= 0)
715 return -1;
717 *p = *ctx->head++;
719 if (*p == '\n')
721 *p = 0;
722 break;
724 p++;
725 len++;
728 return len;
731 static int read_response(CvsServerCtx * ctx, const char * str)
733 /* FIXME: more than 1 char at a time */
734 char resp[BUFSIZ];
736 if (read_line(ctx, resp) < 0)
737 return 0;
739 debug(DEBUG_TCP, "response '%s' read", resp);
741 return (strcmp(resp, str) == 0);
744 static void ctx_to_fp(CvsServerCtx * ctx, FILE * fp)
746 char line[BUFSIZ];
748 while (1)
750 read_line(ctx, line);
751 debug(DEBUG_TCP, "ctx_to_fp: %s", line);
752 if (memcmp(line, "M ", 2) == 0)
754 if (fp)
755 fprintf(fp, "%s\n", line + 2);
757 else if (memcmp(line, "E ", 2) == 0)
759 debug(DEBUG_APPMSG1, "%s", line + 2);
761 else if (strncmp(line, "ok", 2) == 0 || strncmp(line, "error", 5) == 0)
763 break;
767 if (fp)
768 fflush(fp);
771 void cvs_rdiff(CvsServerCtx * ctx,
772 const char * rep, const char * file,
773 const char * rev1, const char * rev2)
775 /* NOTE: opts are ignored for rdiff, '-u' is always used */
777 send_string(ctx, "Argument -u\n");
778 send_string(ctx, "Argument -r\n");
779 send_string(ctx, "Argument %s\n", rev1);
780 send_string(ctx, "Argument -r\n");
781 send_string(ctx, "Argument %s\n", rev2);
782 send_string(ctx, "Argument %s%s\n", rep, file);
783 send_string(ctx, "rdiff\n");
785 ctx_to_fp(ctx, stdout);
788 void cvs_rupdate(CvsServerCtx * ctx, const char * rep, const char * file, const char * rev, int create, const char * opts)
790 FILE * fp;
791 char cmdbuff[BUFSIZ];
793 snprintf(cmdbuff, BUFSIZ, "diff %s %s /dev/null %s | sed -e '%s s|^\\([+-][+-][+-]\\) -|\\1 %s/%s|g'",
794 opts, create?"":"-", create?"-":"", create?"2":"1", rep, file);
796 debug(DEBUG_TCP, "cmdbuff: %s", cmdbuff);
798 if (!(fp = popen(cmdbuff, "w")))
800 debug(DEBUG_APPERROR, "cvs_direct: popen for diff failed: %s", cmdbuff);
801 exit(1);
804 send_string(ctx, "Argument -p\n");
805 send_string(ctx, "Argument -r\n");
806 send_string(ctx, "Argument %s\n", rev);
807 send_string(ctx, "Argument %s/%s\n", rep, file);
808 send_string(ctx, "co\n");
810 ctx_to_fp(ctx, fp);
812 pclose(fp);
815 static int parse_patch_arg(char * arg, char ** str)
817 char *tok, *tok2 = "";
818 tok = strsep(str, " ");
819 if (!tok)
820 return 0;
822 if (!*tok == '-')
824 debug(DEBUG_APPERROR, "diff_opts parse error: no '-' starting argument: %s", *str);
825 return 0;
828 /* if it's not 'long format' argument, we can process it efficiently */
829 if (tok[1] == '-')
831 debug(DEBUG_APPERROR, "diff_opts parse_error: long format args not supported");
832 return 0;
835 /* see if command wants two args and they're separated by ' ' */
836 if (tok[2] == 0 && strchr("BdDFgiorVxYz", tok[1]))
838 tok2 = strsep(str, " ");
839 if (!tok2)
841 debug(DEBUG_APPERROR, "diff_opts parse_error: argument %s requires two arguments", tok);
842 return 0;
846 snprintf(arg, 32, "%s%s", tok, tok2);
847 return 1;
850 void cvs_diff(CvsServerCtx * ctx,
851 const char * rep, const char * file,
852 const char * rev1, const char * rev2, const char * opts)
854 char argstr[BUFSIZ], *p = argstr;
855 char arg[32];
856 char file_buff[PATH_MAX], *basename;
858 strzncpy(argstr, opts, BUFSIZ);
859 while (parse_patch_arg(arg, &p))
860 send_string(ctx, "Argument %s\n", arg);
862 send_string(ctx, "Argument -r\n");
863 send_string(ctx, "Argument %s\n", rev1);
864 send_string(ctx, "Argument -r\n");
865 send_string(ctx, "Argument %s\n", rev2);
868 * we need to separate the 'basename' of file in order to
869 * generate the Directory directive(s)
871 strzncpy(file_buff, file, PATH_MAX);
872 if ((basename = strrchr(file_buff, '/')))
874 *basename = 0;
875 send_string(ctx, "Directory %s/%s\n", rep, file_buff);
876 send_string(ctx, "%s/%s/%s\n", ctx->root, rep, file_buff);
878 else
880 send_string(ctx, "Directory %s\n", rep, file_buff);
881 send_string(ctx, "%s/%s\n", ctx->root, rep);
884 send_string(ctx, "Directory .\n");
885 send_string(ctx, "%s\n", ctx->root);
886 send_string(ctx, "Argument %s/%s\n", rep, file);
887 send_string(ctx, "diff\n");
889 ctx_to_fp(ctx, stdout);
893 * FIXME: the design of this sucks. It was originally designed to fork a subprocess
894 * which read the cvs response and send it back through a pipe the main process,
895 * which fdopen(3)ed the other end, and juts used regular fgets. This however
896 * didn't work because the reads of compressed data in the child process altered
897 * the compression state, and there was no way to resynchronize that state with
898 * the parent process. We could use threads...
900 FILE * cvs_rlog_open(CvsServerCtx * ctx, const char * rep, const char * date_str)
902 /* note: use of the date_str is handled in a non-standard, cvsps specific way */
903 if (date_str && date_str[0])
905 send_string(ctx, "Argument -d\n", rep);
906 send_string(ctx, "Argument %s<1 Jan 2038 05:00:00 -0000\n", date_str);
907 send_string(ctx, "Argument -d\n", rep);
908 send_string(ctx, "Argument %s\n", date_str);
911 send_string(ctx, "Argument %s\n", rep);
912 send_string(ctx, "rlog\n");
915 * FIXME: is it possible to create a 'fake' FILE * whose 'refill'
916 * function is below?
918 return (FILE*)ctx;
921 char * cvs_rlog_fgets(char * buff, int buflen, CvsServerCtx * ctx)
923 char lbuff[BUFSIZ];
924 int len;
926 len = read_line(ctx, lbuff);
927 debug(DEBUG_TCP, "cvs_direct: rlog: read %s", lbuff);
929 if (memcmp(lbuff, "M ", 2) == 0)
931 memcpy(buff, lbuff + 2, len - 2);
932 buff[len - 2 ] = '\n';
933 buff[len - 1 ] = 0;
935 else if (memcmp(lbuff, "E ", 2) == 0)
937 debug(DEBUG_APPMSG1, "%s", lbuff + 2);
939 else if (strcmp(lbuff, "ok") == 0 ||strcmp(lbuff, "error") == 0)
941 debug(DEBUG_TCP, "cvs_direct: rlog: got command completion");
942 return NULL;
945 return buff;
948 void cvs_rlog_close(CvsServerCtx * ctx)
952 void cvs_version(CvsServerCtx * ctx, char * client_version, char * server_version)
954 char lbuff[BUFSIZ];
955 strcpy(client_version, "Client: Concurrent Versions System (CVS) 99.99.99 (client/server) cvs-direct");
956 send_string(ctx, "version\n");
957 read_line(ctx, lbuff);
958 if (memcmp(lbuff, "M ", 2) == 0)
959 sprintf(server_version, "Server: %s", lbuff + 2);
960 else
961 debug(DEBUG_APPERROR, "cvs_direct: didn't read version: %s", lbuff);
963 read_line(ctx, lbuff);
964 if (strcmp(lbuff, "ok") != 0)
965 debug(DEBUG_APPERROR, "cvs_direct: protocol error reading version");
967 debug(DEBUG_TCP, "cvs_direct: client version %s", client_version);
968 debug(DEBUG_TCP, "cvs_direct: server version %s", server_version);