Annotation of src/usr.bin/cvs/server.c, Revision 1.67
1.67 ! xsa 1: /* $OpenBSD: server.c,v 1.66 2007/08/23 13:17:53 joris Exp $ */
1.1 jfb 2: /*
1.29 joris 3: * Copyright (c) 2006 Joris Vink <joris@openbsd.org>
1.1 jfb 4: *
1.29 joris 5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
1.1 jfb 8: *
1.29 joris 9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.1 jfb 16: */
17:
1.55 otto 18: #include <sys/stat.h>
19:
20: #include <errno.h>
21: #include <fcntl.h>
22: #include <libgen.h>
23: #include <stdlib.h>
24: #include <string.h>
25: #include <unistd.h>
1.1 jfb 26:
27: #include "cvs.h"
1.29 joris 28: #include "remote.h"
29:
30: struct cvs_resp cvs_responses[] = {
31: /* this is what our server uses, the client should support it */
32: { "Valid-requests", 1, cvs_client_validreq, RESP_NEEDED },
33: { "ok", 0, cvs_client_ok, RESP_NEEDED},
34: { "error", 0, cvs_client_error, RESP_NEEDED },
35: { "E", 0, cvs_client_e, RESP_NEEDED },
36: { "M", 0, cvs_client_m, RESP_NEEDED },
37: { "Checked-in", 0, cvs_client_checkedin, RESP_NEEDED },
38: { "Updated", 0, cvs_client_updated, RESP_NEEDED },
39: { "Merged", 0, cvs_client_merged, RESP_NEEDED },
40: { "Removed", 0, cvs_client_removed, RESP_NEEDED },
41: { "Remove-entry", 0, cvs_client_remove_entry, RESP_NEEDED },
1.46 xsa 42: { "Set-static-directory", 0, cvs_client_set_static_directory, RESP_NEEDED },
1.45 xsa 43: { "Clear-static-directory", 0, cvs_client_clear_static_directory, RESP_NEEDED },
44: { "Set-sticky", 0, cvs_client_set_sticky, RESP_NEEDED },
45: { "Clear-sticky", 0, cvs_client_clear_sticky, RESP_NEEDED },
1.29 joris 46:
47: /* unsupported responses until told otherwise */
48: { "New-entry", 0, NULL, 0 },
49: { "Created", 0, NULL, 0 },
50: { "Update-existing", 0, NULL, 0 },
51: { "Rcs-diff", 0, NULL, 0 },
52: { "Patched", 0, NULL, 0 },
53: { "Mode", 0, NULL, 0 },
54: { "Mod-time", 0, NULL, 0 },
55: { "Checksum", 0, NULL, 0 },
56: { "Copy-file", 0, NULL, 0 },
57: { "Template", 0, NULL, 0 },
58: { "Set-checkin-prog", 0, NULL, 0 },
59: { "Set-update-prog", 0, NULL, 0 },
60: { "Notified", 0, NULL, 0 },
61: { "Module-expansion", 0, NULL, 0 },
62: { "Wrapper-rcsOption", 0, NULL, 0 },
63: { "Mbinary", 0, NULL, 0 },
64: { "F", 0, NULL, 0 },
65: { "MT", 0, NULL, 0 },
66: { "", -1, NULL, 0 }
67: };
1.1 jfb 68:
1.29 joris 69: int cvs_server(int, char **);
70: char *cvs_server_path = NULL;
1.1 jfb 71:
1.29 joris 72: static char *server_currentdir = NULL;
73: static char *server_argv[CVS_CMD_MAXARG];
74: static int server_argc = 1;
1.8 joris 75:
1.17 jfb 76: struct cvs_cmd cvs_cmd_server = {
1.29 joris 77: CVS_OP_SERVER, 0, "server", { "", "" },
78: "server mode",
1.17 jfb 79: NULL,
1.29 joris 80: NULL,
81: NULL,
82: cvs_server
1.17 jfb 83: };
1.1 jfb 84:
1.14 joris 85:
1.1 jfb 86: int
87: cvs_server(int argc, char **argv)
88: {
1.29 joris 89: char *cmd, *data;
90: struct cvs_req *req;
91:
92: server_argv[0] = xstrdup("server");
93:
1.58 ray 94: (void)xasprintf(&cvs_server_path, "%s/cvs-serv%d", cvs_tmpdir,
95: getpid());
1.29 joris 96:
97: if (mkdir(cvs_server_path, 0700) == -1)
98: fatal("failed to create temporary server directory: %s, %s",
99: cvs_server_path, strerror(errno));
100:
101: if (chdir(cvs_server_path) == -1)
102: fatal("failed to change directory to '%s'", cvs_server_path);
103:
104: for (;;) {
105: cmd = cvs_remote_input();
106:
107: if ((data = strchr(cmd, ' ')) != NULL)
108: (*data++) = '\0';
109:
110: req = cvs_remote_get_request_info(cmd);
111: if (req == NULL)
112: fatal("request '%s' is not supported by our server",
113: cmd);
114:
115: if (req->hdlr == NULL)
116: fatal("opencvs server does not support '%s'", cmd);
1.63 joris 117:
118: if ((req->flags & REQ_NEEDDIR) && (server_currentdir == NULL))
119: fatal("`%s' needs a directory to be sent with "
120: "the `Directory` request first", cmd);
1.29 joris 121:
122: (*req->hdlr)(data);
123: xfree(cmd);
1.14 joris 124: }
125:
1.29 joris 126: return (0);
127: }
128:
129: void
130: cvs_server_send_response(char *fmt, ...)
131: {
132: va_list ap;
1.59 ray 133: char *data;
1.29 joris 134:
135: va_start(ap, fmt);
1.56 ray 136: if (vasprintf(&data, fmt, ap) == -1)
137: fatal("vasprintf: %s", strerror(errno));
1.29 joris 138: va_end(ap);
1.14 joris 139:
1.30 joris 140: cvs_log(LP_TRACE, "%s", data);
1.29 joris 141: cvs_remote_output(data);
142: xfree(data);
143: }
144:
145: void
146: cvs_server_root(char *data)
147: {
148: fatal("duplicate Root request from client, violates the protocol");
149: }
150:
151: void
152: cvs_server_validresp(char *data)
153: {
154: int i;
155: char *sp, *ep;
156: struct cvs_resp *resp;
157:
1.57 ray 158: if ((sp = data) == NULL)
159: fatal("Missing argument for Valid-responses");
160:
1.29 joris 161: do {
162: if ((ep = strchr(sp, ' ')) != NULL)
163: *ep = '\0';
164:
165: resp = cvs_remote_get_response_info(sp);
166: if (resp != NULL)
167: resp->supported = 1;
168:
169: if (ep != NULL)
170: sp = ep + 1;
171: } while (ep != NULL);
172:
173: for (i = 0; cvs_responses[i].supported != -1; i++) {
174: resp = &cvs_responses[i];
175: if ((resp->flags & RESP_NEEDED) &&
176: resp->supported != 1) {
177: fatal("client does not support required '%s'",
178: resp->name);
1.1 jfb 179: }
1.29 joris 180: }
181: }
1.1 jfb 182:
1.29 joris 183: void
184: cvs_server_validreq(char *data)
185: {
186: BUF *bp;
187: char *d;
188: int i, first;
189:
190: first = 0;
191: bp = cvs_buf_alloc(512, BUF_AUTOEXT);
192: for (i = 0; cvs_requests[i].supported != -1; i++) {
193: if (cvs_requests[i].hdlr == NULL)
1.5 jfb 194: continue;
1.1 jfb 195:
1.29 joris 196: if (first != 0)
197: cvs_buf_append(bp, " ", 1);
198: else
199: first++;
200:
201: cvs_buf_append(bp, cvs_requests[i].name,
202: strlen(cvs_requests[i].name));
203: }
204:
205: cvs_buf_putc(bp, '\0');
206: d = cvs_buf_release(bp);
207:
208: cvs_server_send_response("Valid-requests %s", d);
209: cvs_server_send_response("ok");
210: xfree(d);
1.42 xsa 211: }
212:
213: void
1.43 xsa 214: cvs_server_static_directory(char *data)
215: {
216: FILE *fp;
1.51 otto 217: char fpath[MAXPATHLEN];
1.43 xsa 218:
1.54 xsa 219: (void)xsnprintf(fpath, MAXPATHLEN, "%s/%s",
220: server_currentdir, CVS_PATH_STATICENTRIES);
1.43 xsa 221:
222: if ((fp = fopen(fpath, "w+")) == NULL) {
223: cvs_log(LP_ERRNO, "%s", fpath);
1.51 otto 224: return;
1.43 xsa 225: }
226: (void)fclose(fp);
227: }
228:
229: void
1.42 xsa 230: cvs_server_sticky(char *data)
231: {
232: FILE *fp;
1.51 otto 233: char tagpath[MAXPATHLEN];
1.42 xsa 234:
1.57 ray 235: if (data == NULL)
236: fatal("Missing argument for Sticky");
237:
1.54 xsa 238: (void)xsnprintf(tagpath, MAXPATHLEN, "%s/%s",
239: server_currentdir, CVS_PATH_TAG);
1.42 xsa 240:
1.43 xsa 241: if ((fp = fopen(tagpath, "w+")) == NULL) {
1.42 xsa 242: cvs_log(LP_ERRNO, "%s", tagpath);
1.51 otto 243: return;
1.42 xsa 244: }
245:
246: (void)fprintf(fp, "%s\n", data);
247: (void)fclose(fp);
1.29 joris 248: }
249:
250: void
251: cvs_server_globalopt(char *data)
252: {
1.57 ray 253: if (data == NULL)
254: fatal("Missing argument for Global_option");
255:
1.38 xsa 256: if (!strcmp(data, "-l"))
257: cvs_nolog = 1;
1.29 joris 258:
259: if (!strcmp(data, "-n"))
260: cvs_noexec = 1;
1.38 xsa 261:
262: if (!strcmp(data, "-Q"))
263: verbosity = 0;
264:
265: if (!strcmp(data, "-r"))
266: cvs_readonly = 1;
267:
268: if (!strcmp(data, "-t"))
269: cvs_trace = 1;
1.29 joris 270:
271: if (!strcmp(data, "-V"))
272: verbosity = 2;
1.39 xsa 273: }
274:
275: void
276: cvs_server_set(char *data)
277: {
278: char *ep;
279:
1.57 ray 280: if (data == NULL)
281: fatal("Missing argument for Set");
282:
1.39 xsa 283: ep = strchr(data, '=');
284: if (ep == NULL)
285: fatal("no = in variable assignment");
286:
287: *(ep++) = '\0';
288: if (cvs_var_set(data, ep) < 0)
289: fatal("cvs_server_set: cvs_var_set failed");
1.29 joris 290: }
1.1 jfb 291:
1.29 joris 292: void
293: cvs_server_directory(char *data)
294: {
295: CVSENTRIES *entlist;
1.51 otto 296: char *dir, *repo, *parent, entry[CVS_ENT_MAXLINELEN], *dirn, *p;
1.29 joris 297:
298: dir = cvs_remote_input();
1.49 joris 299: STRIP_SLASH(dir);
300:
301: if (strlen(dir) < strlen(current_cvsroot->cr_dir))
1.29 joris 302: fatal("cvs_server_directory: bad Directory request");
303:
1.49 joris 304: repo = dir + strlen(current_cvsroot->cr_dir);
305:
306: /*
307: * This is somewhat required for checkout, as the
308: * directory request will be:
309: *
310: * Directory .
311: * /path/to/cvs/root
312: */
313: if (repo[0] == '\0')
314: p = xstrdup(".");
315: else
316: p = xstrdup(repo + 1);
317:
1.65 joris 318: cvs_mkpath(p, NULL);
1.29 joris 319:
1.49 joris 320: if ((dirn = basename(p)) == NULL)
1.29 joris 321: fatal("cvs_server_directory: %s", strerror(errno));
322:
1.49 joris 323: if ((parent = dirname(p)) == NULL)
1.29 joris 324: fatal("cvs_server_directory: %s", strerror(errno));
325:
326: if (strcmp(parent, ".")) {
327: entlist = cvs_ent_open(parent);
1.53 xsa 328: (void)xsnprintf(entry, CVS_ENT_MAXLINELEN, "D/%s////", dirn);
1.29 joris 329:
330: cvs_ent_add(entlist, entry);
331: cvs_ent_close(entlist, ENT_SYNC);
1.1 jfb 332: }
333:
1.29 joris 334: if (server_currentdir != NULL)
335: xfree(server_currentdir);
1.61 ray 336: server_currentdir = p;
1.29 joris 337:
338: xfree(dir);
339: }
340:
341: void
342: cvs_server_entry(char *data)
343: {
344: CVSENTRIES *entlist;
345:
1.57 ray 346: if (data == NULL)
347: fatal("Missing argument for Entry");
348:
1.29 joris 349: entlist = cvs_ent_open(server_currentdir);
350: cvs_ent_add(entlist, data);
351: cvs_ent_close(entlist, ENT_SYNC);
352: }
353:
354: void
355: cvs_server_modified(char *data)
356: {
1.41 xsa 357: int fd;
1.29 joris 358: size_t flen;
359: mode_t fmode;
360: const char *errstr;
1.51 otto 361: char *mode, *len, fpath[MAXPATHLEN];
1.29 joris 362:
1.57 ray 363: if (data == NULL)
364: fatal("Missing argument for Modified");
365:
1.29 joris 366: mode = cvs_remote_input();
367: len = cvs_remote_input();
368:
369: cvs_strtomode(mode, &fmode);
370: xfree(mode);
371:
372: flen = strtonum(len, 0, INT_MAX, &errstr);
373: if (errstr != NULL)
374: fatal("cvs_server_modified: %s", errstr);
375: xfree(len);
376:
1.54 xsa 377: (void)xsnprintf(fpath, MAXPATHLEN, "%s/%s", server_currentdir, data);
1.29 joris 378:
379: if ((fd = open(fpath, O_WRONLY | O_CREAT | O_TRUNC)) == -1)
380: fatal("cvs_server_modified: %s: %s", fpath, strerror(errno));
381:
1.48 joris 382: cvs_remote_receive_file(fd, flen);
1.29 joris 383:
384: if (fchmod(fd, 0600) == -1)
385: fatal("cvs_server_modified: failed to set file mode");
386:
387: (void)close(fd);
388: }
389:
390: void
391: cvs_server_useunchanged(char *data)
392: {
393: }
394:
395: void
396: cvs_server_unchanged(char *data)
397: {
1.41 xsa 398: int fd;
1.51 otto 399: char fpath[MAXPATHLEN];
1.29 joris 400: CVSENTRIES *entlist;
401: struct cvs_ent *ent;
402: struct timeval tv[2];
403:
1.57 ray 404: if (data == NULL)
405: fatal("Missing argument for Unchanged");
406:
1.54 xsa 407: (void)xsnprintf(fpath, MAXPATHLEN, "%s/%s", server_currentdir, data);
1.29 joris 408:
409: if ((fd = open(fpath, O_RDWR | O_CREAT | O_TRUNC)) == -1)
410: fatal("cvs_server_unchanged: %s: %s", fpath, strerror(errno));
411:
412: entlist = cvs_ent_open(server_currentdir);
413: ent = cvs_ent_get(entlist, data);
414: if (ent == NULL)
415: fatal("received Unchanged request for non-existing file");
416: cvs_ent_close(entlist, ENT_NOSYNC);
417:
1.52 joris 418: tv[0].tv_sec = ent->ce_mtime;
1.29 joris 419: tv[0].tv_usec = 0;
420: tv[1] = tv[0];
421: if (futimes(fd, tv) == -1)
422: fatal("cvs_server_unchanged: failed to set modified time");
423:
424: if (fchmod(fd, 0600) == -1)
425: fatal("cvs_server_unchanged: failed to set mode");
426:
427: cvs_ent_free(ent);
428: (void)close(fd);
429: }
430:
431: void
432: cvs_server_questionable(char *data)
433: {
434: }
435:
436: void
437: cvs_server_argument(char *data)
438: {
1.60 ray 439: if (server_argc >= CVS_CMD_MAXARG)
1.29 joris 440: fatal("cvs_server_argument: too many arguments sent");
1.57 ray 441:
442: if (data == NULL)
443: fatal("Missing argument for Argument");
1.29 joris 444:
445: server_argv[server_argc++] = xstrdup(data);
1.37 xsa 446: }
447:
448: void
449: cvs_server_argumentx(char *data)
450: {
1.66 joris 451: int idx;
452: size_t len;
453:
454: if (server_argc < 0)
455: fatal("Protocol Error: ArgumentX without previous argument");
456:
457: idx = server_argc - 1;
458:
459: len = strlen(server_argv[idx]) + strlen(data) + 2;
460: server_argv[idx] = xrealloc(server_argv[idx], len, sizeof(char));
461: strlcat(server_argv[idx], "\n", len);
462: strlcat(server_argv[idx], data, len);
1.44 xsa 463: }
464:
465: void
466: cvs_server_update_patches(char *data)
467: {
468: /*
469: * This does not actually do anything.
470: * It is used to tell that the server is able to
471: * generate patches when given an `update' request.
472: * The client must issue the -u argument to `update'
473: * to receive patches.
474: */
1.29 joris 475: }
476:
477: void
1.31 xsa 478: cvs_server_add(char *data)
479: {
480: if (chdir(server_currentdir) == -1)
481: fatal("cvs_server_add: %s", strerror(errno));
482:
483: cvs_cmdop = CVS_OP_ADD;
484: cvs_add(server_argc, server_argv);
1.50 joris 485: cvs_server_send_response("ok");
486: }
487:
488: void
489: cvs_server_import(char *data)
490: {
491: if (chdir(server_currentdir) == -1)
492: fatal("cvs_server_import: %s", strerror(errno));
493:
494: cvs_cmdop = CVS_OP_IMPORT;
495: cvs_import(server_argc, server_argv);
1.31 xsa 496: cvs_server_send_response("ok");
497: }
1.35 xsa 498:
499: void
500: cvs_server_admin(char *data)
501: {
502: if (chdir(server_currentdir) == -1)
503: fatal("cvs_server_admin: %s", strerror(errno));
504:
505: cvs_cmdop = CVS_OP_ADMIN;
506: cvs_admin(server_argc, server_argv);
507: cvs_server_send_response("ok");
508: }
509:
1.40 xsa 510: void
511: cvs_server_annotate(char *data)
512: {
513: if (chdir(server_currentdir) == -1)
514: fatal("cvs_server_annotate: %s", strerror(errno));
515:
516: cvs_cmdop = CVS_OP_ANNOTATE;
517: cvs_annotate(server_argc, server_argv);
518: cvs_server_send_response("ok");
519: }
1.31 xsa 520:
521: void
1.29 joris 522: cvs_server_commit(char *data)
523: {
524: if (chdir(server_currentdir) == -1)
525: fatal("cvs_server_commit: %s", strerror(errno));
526:
527: cvs_cmdop = CVS_OP_COMMIT;
528: cvs_commit(server_argc, server_argv);
1.49 joris 529: cvs_server_send_response("ok");
530: }
531:
532: void
533: cvs_server_checkout(char *data)
534: { if (chdir(server_currentdir) == -1)
535: fatal("cvs_server_checkout: %s", strerror(errno));
536:
537: cvs_cmdop = CVS_OP_CHECKOUT;
538: cvs_checkout(server_argc, server_argv);
1.29 joris 539: cvs_server_send_response("ok");
540: }
541:
542: void
543: cvs_server_diff(char *data)
544: {
545: if (chdir(server_currentdir) == -1)
546: fatal("cvs_server_diff: %s", strerror(errno));
547:
548: cvs_cmdop = CVS_OP_DIFF;
549: cvs_diff(server_argc, server_argv);
1.34 xsa 550: cvs_server_send_response("ok");
551: }
552:
553: void
554: cvs_server_init(char *data)
555: {
556: if (chdir(server_currentdir) == -1)
557: fatal("cvs_server_init: %s", strerror(errno));
558:
559: cvs_cmdop = CVS_OP_INIT;
560: cvs_init(server_argc, server_argv);
1.64 xsa 561: cvs_server_send_response("ok");
562: }
563:
564: void
565: cvs_server_release(char *data)
566: {
567: if (chdir(server_currentdir) == -1)
1.67 ! xsa 568: fatal("cvs_server_release: %s", strerror(errno));
1.64 xsa 569:
570: cvs_cmdop = CVS_OP_RELEASE;
571: cvs_release(server_argc, server_argv);
1.31 xsa 572: cvs_server_send_response("ok");
573: }
574:
575: void
576: cvs_server_remove(char *data)
577: {
578: if (chdir(server_currentdir) == -1)
579: fatal("cvs_server_remove: %s", strerror(errno));
580:
581: cvs_cmdop = CVS_OP_REMOVE;
582: cvs_remove(server_argc, server_argv);
1.29 joris 583: cvs_server_send_response("ok");
584: }
585:
586: void
587: cvs_server_status(char *data)
588: {
589: if (chdir(server_currentdir) == -1)
590: fatal("cvs_server_status: %s", strerror(errno));
591:
592: cvs_cmdop = CVS_OP_STATUS;
593: cvs_status(server_argc, server_argv);
594: cvs_server_send_response("ok");
595: }
596:
597: void
598: cvs_server_log(char *data)
599: {
600: if (chdir(server_currentdir) == -1)
601: fatal("cvs_server_log: %s", strerror(errno));
602:
603: cvs_cmdop = CVS_OP_LOG;
1.62 niallo 604: cvs_getlog(server_argc, server_argv);
605: cvs_server_send_response("ok");
606: }
607:
608: void
609: cvs_server_rlog(char *data)
610: {
611: char fpath[MAXPATHLEN];
612: struct cvsroot *cvsroot;
613:
614: cvsroot = cvsroot_get(NULL);
615:
616: (void)xsnprintf(fpath, MAXPATHLEN, "%s/%s",
617: cvsroot->cr_dir, server_currentdir);
618:
619: if (chdir(fpath) == -1)
1.67 ! xsa 620: fatal("cvs_server_rlog: %s", strerror(errno));
1.62 niallo 621:
622: cvs_cmdop = CVS_OP_RLOG;
1.32 xsa 623: cvs_getlog(server_argc, server_argv);
624: cvs_server_send_response("ok");
625: }
626:
627: void
628: cvs_server_tag(char *data)
629: {
630: if (chdir(server_currentdir) == -1)
631: fatal("cvs_server_tag: %s", strerror(errno));
632:
633: cvs_cmdop = CVS_OP_TAG;
1.33 xsa 634: cvs_tag(server_argc, server_argv);
1.29 joris 635: cvs_server_send_response("ok");
636: }
637:
638: void
639: cvs_server_update(char *data)
640: {
641: if (chdir(server_currentdir) == -1)
642: fatal("cvs_server_update: %s", strerror(errno));
1.14 joris 643:
1.29 joris 644: cvs_cmdop = CVS_OP_UPDATE;
645: cvs_update(server_argc, server_argv);
1.36 xsa 646: cvs_server_send_response("ok");
647: }
648:
649: void
650: cvs_server_version(char *data)
651: {
652: cvs_cmdop = CVS_OP_VERSION;
653: cvs_version(server_argc, server_argv);
1.29 joris 654: cvs_server_send_response("ok");
1.47 joris 655: }
656:
657: void
658: cvs_server_update_entry(const char *resp, struct cvs_file *cf)
659: {
1.58 ray 660: char *p;
1.65 joris 661: char repo[MAXPATHLEN], fpath[MAXPATHLEN];
1.47 joris 662:
663: if ((p = strrchr(cf->file_rpath, ',')) != NULL)
664: *p = '\0';
665:
1.65 joris 666: cvs_get_repository_path(cf->file_wd, repo, MAXPATHLEN);
667: (void)xsnprintf(fpath, MAXPATHLEN, "%s/%s", repo, cf->file_name);
668:
1.58 ray 669: cvs_server_send_response("%s %s/", resp, cf->file_wd);
1.65 joris 670: cvs_remote_output(fpath);
1.47 joris 671:
672: if (p != NULL)
673: *p = ',';
1.65 joris 674: }
675:
676: void
677: cvs_server_set_sticky(char *dir, char *tag)
678: {
679: char fpath[MAXPATHLEN], tbuf[CVS_ENT_MAXLINELEN];
680:
681: (void)xsnprintf(fpath, MAXPATHLEN, "%s/%s",
682: current_cvsroot->cr_dir, dir);
683:
684: (void)xsnprintf(tbuf, MAXPATHLEN, "T%s", tag);
685:
686: cvs_server_send_response("Set-sticky %s", dir);
687: cvs_remote_output(fpath);
688: cvs_remote_output(tbuf);
689: }
690:
691: void
692: cvs_server_clear_sticky(char *dir)
693: {
694: char fpath[MAXPATHLEN];
695:
696: (void)xsnprintf(fpath, MAXPATHLEN, "%s/%s",
697: current_cvsroot->cr_dir, dir);
698:
699: cvs_server_send_response("Clear-sticky %s", dir);
700: cvs_remote_output(fpath);
1.1 jfb 701: }