Annotation of src/usr.bin/cvs/checkout.c, Revision 1.41
1.41 ! joris 1: /* $OpenBSD: checkout.c,v 1.40 2005/09/15 17:01:10 xsa Exp $ */
1.1 jfb 2: /*
3: * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
1.11 tedu 4: * All rights reserved.
1.1 jfb 5: *
1.11 tedu 6: * Redistribution and use in source and binary forms, with or without
7: * modification, are permitted provided that the following conditions
8: * are met:
1.1 jfb 9: *
1.11 tedu 10: * 1. Redistributions of source code must retain the above copyright
11: * notice, this list of conditions and the following disclaimer.
1.1 jfb 12: * 2. The name of the author may not be used to endorse or promote products
1.11 tedu 13: * derived from this software without specific prior written permission.
1.1 jfb 14: *
15: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
16: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
17: * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
18: * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
1.11 tedu 24: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1.1 jfb 25: */
26:
27: #include <sys/types.h>
1.23 joris 28: #include <sys/stat.h>
1.1 jfb 29:
30: #include <errno.h>
31: #include <stdio.h>
32: #include <stdlib.h>
1.28 xsa 33: #include <string.h>
1.1 jfb 34: #include <unistd.h>
35:
36: #include "cvs.h"
37: #include "log.h"
1.5 jfb 38: #include "proto.h"
1.1 jfb 39:
40:
1.39 xsa 41: #define CVS_LISTMOD 1
42: #define CVS_STATMOD 2
1.13 jfb 43:
1.39 xsa 44: static int cvs_checkout_init(struct cvs_cmd *, int, char **, int *);
45: static int cvs_checkout_pre_exec(struct cvsroot *);
1.41 ! joris 46: static int cvs_checkout_local(CVSFILE *cf, void *);
1.14 joris 47:
1.22 jfb 48: struct cvs_cmd cvs_cmd_checkout = {
49: CVS_OP_CHECKOUT, CVS_REQ_CO, "checkout",
50: { "co", "get" },
51: "Checkout sources for editing",
1.33 xsa 52: "[-AcflNnPpRs] [-D date | -r tag] [-d dir] [-j rev] [-k mode] "
1.22 jfb 53: "[-t id] module ...",
54: "AcD:d:fj:k:lNnPRr:st:",
55: NULL,
1.14 joris 56: 0,
1.22 jfb 57: cvs_checkout_init,
58: cvs_checkout_pre_exec,
59: NULL,
60: NULL,
61: NULL,
62: NULL,
1.23 joris 63: CVS_CMD_ALLOWSPEC | CVS_CMD_SENDDIR
1.14 joris 64: };
65:
1.33 xsa 66: struct cvs_cmd cvs_cmd_export = {
67: CVS_OP_EXPORT, CVS_REQ_EXPORT, "export",
68: { "ex", "exp" },
69: "Extract copy of a module without management directories",
70: "[-flNnR] [-d dir] [-k mode] -D date | -r tag module ...",
71: "D:d:fk:lNnRr:",
72: NULL,
73: 0,
74: cvs_checkout_init,
75: cvs_checkout_pre_exec,
76: NULL,
77: NULL,
78: NULL,
79: NULL,
80: CVS_CMD_ALLOWSPEC | CVS_CMD_SENDDIR
81: };
82:
1.41 ! joris 83: static char *currepo = NULL;
! 84: static DIR *dirp = NULL;
! 85: static int cwdfd = -1;
1.33 xsa 86: static char *date, *tag, *koptstr, *tgtdir, *rcsid;
1.14 joris 87: static int statmod = 0;
1.37 joris 88: static int shorten = 0;
1.22 jfb 89: static int usehead = 0;
1.14 joris 90: static int kflag = RCS_KWEXP_DEFAULT;
1.1 jfb 91:
1.22 jfb 92: /* modules */
93: static char **co_mods;
94: static int co_nmod;
95:
1.41 ! joris 96: /* XXX checkout has issues in remote mode, -N gets seen as module */
! 97:
1.22 jfb 98: static int
99: cvs_checkout_init(struct cvs_cmd *cmd, int argc, char **argv, int *arg)
1.1 jfb 100: {
1.14 joris 101: int ch;
1.33 xsa 102: RCSNUM *rcs;
1.14 joris 103:
1.33 xsa 104: date = tag = koptstr = tgtdir = rcsid = NULL;
1.13 jfb 105:
1.22 jfb 106: while ((ch = getopt(argc, argv, cmd->cmd_opts)) != -1) {
1.1 jfb 107: switch (ch) {
1.13 jfb 108: case 'A':
109: break;
1.10 jfb 110: case 'c':
1.13 jfb 111: statmod = CVS_LISTMOD;
112: break;
113: case 'D':
114: date = optarg;
1.32 xsa 115: cmd->cmd_flags |= CVS_CMD_PRUNEDIRS;
1.13 jfb 116: break;
117: case 'd':
118: tgtdir = optarg;
1.37 joris 119: shorten = 1;
1.13 jfb 120: break;
121: case 'f':
1.21 xsa 122: usehead = 1;
1.13 jfb 123: break;
124: case 'j':
125: break;
126: case 'k':
127: koptstr = optarg;
128: kflag = rcs_kflag_get(koptstr);
129: if (RCS_KWEXP_INVAL(kflag)) {
130: cvs_log(LP_ERR,
131: "invalid RCS keyword expansion mode");
132: rcs_kflag_usage();
1.18 joris 133: return (CVS_EX_USAGE);
1.13 jfb 134: }
1.24 joris 135: break;
136: case 'P':
137: cmd->cmd_flags |= CVS_CMD_PRUNEDIRS;
1.20 xsa 138: break;
1.22 jfb 139: case 'N':
140: shorten = 0;
141: break;
1.20 xsa 142: case 'p':
143: cvs_noexec = 1; /* no locks will be created */
1.13 jfb 144: break;
145: case 'r':
1.33 xsa 146: tag = optarg;
1.25 xsa 147: cmd->cmd_flags |= CVS_CMD_PRUNEDIRS;
1.13 jfb 148: break;
149: case 's':
150: statmod = CVS_STATMOD;
151: break;
152: case 't':
153: rcsid = optarg;
1.10 jfb 154: break;
1.1 jfb 155: default:
1.18 joris 156: return (CVS_EX_USAGE);
1.1 jfb 157: }
158: }
159:
160: argc -= optind;
161: argv += optind;
162:
1.22 jfb 163: co_mods = argv;
164: co_nmod = argc;
165:
1.38 xsa 166: if ((statmod == 0) && (argc == 0)) {
1.31 xsa 167: cvs_log(LP_ABORT,
1.1 jfb 168: "must specify at least one module or directory");
1.26 xsa 169: return (-1);
1.1 jfb 170: }
171:
1.13 jfb 172: if (statmod && (argc > 0)) {
1.31 xsa 173: cvs_log(LP_ABORT, "-c and -s must not get any arguments");
1.26 xsa 174: return (-1);
1.13 jfb 175: }
176:
1.33 xsa 177: /* `export' command exceptions */
178: if (cvs_cmdop == CVS_OP_EXPORT) {
179: if (!tag && !date) {
180: cvs_log(LP_ABORT, "must specify a tag or date");
181: return (-1);
182: }
183:
184: /* we don't want numerical revisions here */
185: if (tag && (rcs = rcsnum_parse(tag)) != NULL) {
186: cvs_log(LP_ABORT, "tag `%s' must be a symbolic tag",
187: tag);
188: rcsnum_free(rcs);
189: return (-1);
190: }
191: }
192:
1.14 joris 193: *arg = optind;
194: return (0);
195: }
1.9 jfb 196:
1.22 jfb 197: static int
198: cvs_checkout_pre_exec(struct cvsroot *root)
1.14 joris 199: {
1.41 ! joris 200: int i, ret;
! 201: char *sp, repo[MAXPATHLEN];
! 202:
! 203: if ((dirp = opendir(".")) == NULL) {
! 204: cvs_log(LP_ERRNO, "failed to save cwd");
! 205: return (CVS_EX_DATA);
! 206: }
! 207:
! 208: cwdfd = dirfd(dirp);
1.1 jfb 209:
1.22 jfb 210: for (i = 0; i < co_nmod; i++) {
1.23 joris 211: if ((sp = strchr(co_mods[i], '/')) != NULL)
212: *sp = '\0';
213:
214: if ((mkdir(co_mods[i], 0755) == -1) && (errno != EEXIST)) {
215: cvs_log(LP_ERRNO, "can't create base directory '%s'",
216: co_mods[i]);
217: return (CVS_EX_DATA);
218: }
219:
1.40 xsa 220: if (cvs_mkadmin(co_mods[i], root->cr_str, co_mods[i],
221: NULL, NULL, 0) < 0) {
1.27 xsa 222: cvs_log(LP_ERR, "can't create base directory '%s'",
1.23 joris 223: co_mods[i]);
1.22 jfb 224: return (CVS_EX_DATA);
1.23 joris 225: }
226:
227: if (sp != NULL)
228: *sp = '/';
1.22 jfb 229: }
230:
1.41 ! joris 231: if (root->cr_method == CVS_METHOD_LOCAL) {
! 232: if ((dirp = opendir(".")) == NULL)
! 233: return (CVS_EX_DATA);
! 234: cwdfd = dirfd(dirp);
! 235:
! 236: for (i = 0; i < co_nmod; i++) {
! 237: snprintf(repo, sizeof(repo), "%s/%s", root->cr_dir,
! 238: co_mods[i]);
! 239: currepo = co_mods[i];
! 240: ret = cvs_file_get(repo, CF_RECURSE | CF_REPO | CF_IGNORE,
! 241: cvs_checkout_local, NULL, NULL);
! 242: if (ret != CVS_EX_OK) {
! 243: closedir(dirp);
! 244: return (ret);
! 245: }
! 246: }
! 247:
! 248: closedir(dirp);
! 249: } else {
1.37 joris 250: /*
251: * These arguments are for the expand-modules
252: * command that we send to the server before requesting
253: * a checkout.
254: */
255: for (i = 0; i < co_nmod; i++)
256: if (cvs_sendarg(root, co_mods[i], 0) < 0)
257: return (CVS_EX_PROTO);
1.22 jfb 258: if (cvs_sendreq(root, CVS_REQ_DIRECTORY, ".") < 0)
259: return (CVS_EX_PROTO);
260: if (cvs_sendln(root, root->cr_dir) < 0)
261: return (CVS_EX_PROTO);
262:
263: if (cvs_sendreq(root, CVS_REQ_XPANDMOD, NULL) < 0)
264: cvs_log(LP_ERR, "failed to expand module");
265:
1.38 xsa 266: if ((usehead == 1) && (cvs_sendarg(root, "-f", 0) < 0))
1.22 jfb 267: return (CVS_EX_PROTO);
268:
269: if ((tgtdir != NULL) &&
270: ((cvs_sendarg(root, "-d", 0) < 0) ||
271: (cvs_sendarg(root, tgtdir, 0) < 0)))
272: return (CVS_EX_PROTO);
273:
1.38 xsa 274: if ((shorten == 0) && cvs_sendarg(root, "-N", 0) < 0)
1.29 xsa 275: return (CVS_EX_PROTO);
276:
1.30 xsa 277: if ((cvs_cmd_checkout.cmd_flags & CVS_CMD_PRUNEDIRS) &&
1.29 xsa 278: (cvs_sendarg(root, "-P", 0) < 0))
1.22 jfb 279: return (CVS_EX_PROTO);
280:
281: for (i = 0; i < co_nmod; i++)
282: if (cvs_sendarg(root, co_mods[i], 0) < 0)
283: return (CVS_EX_PROTO);
284:
285: if ((statmod == CVS_LISTMOD) &&
286: (cvs_sendarg(root, "-c", 0) < 0))
287: return (CVS_EX_PROTO);
288: else if ((statmod == CVS_STATMOD) &&
289: (cvs_sendarg(root, "-s", 0) < 0))
1.33 xsa 290: return (CVS_EX_PROTO);
291:
292: if ((tag != NULL) && ((cvs_sendarg(root, "-r", 0) < 0) ||
293: (cvs_sendarg(root, tag, 0) < 0)))
1.34 xsa 294: return (CVS_EX_PROTO);
295:
296: if ((date != NULL) && ((cvs_sendarg(root, "-D", 0) < 0) ||
297: (cvs_sendarg(root, date, 0) < 0)))
1.22 jfb 298: return (CVS_EX_PROTO);
299: }
1.1 jfb 300: return (0);
1.41 ! joris 301: }
! 302:
! 303: static int
! 304: cvs_checkout_local(CVSFILE *cf, void *arg)
! 305: {
! 306: char rcspath[MAXPATHLEN], fpath[MAXPATHLEN];
! 307: RCSFILE *rf;
! 308: struct cvsroot *root;
! 309: static int inattic = 0;
! 310:
! 311: /* we don't want these */
! 312: if ((cf->cf_type == DT_DIR) && !strcmp(cf->cf_name, "Attic")) {
! 313: inattic = 1;
! 314: return (CVS_EX_OK);
! 315: }
! 316:
! 317: root = CVS_DIR_ROOT(cf);
! 318: cvs_file_getpath(cf, fpath, sizeof(fpath));
! 319:
! 320: snprintf(rcspath, sizeof(rcspath), "%s/%s%s", root->cr_dir,
! 321: fpath, RCS_FILE_EXT);
! 322:
! 323: if (cf->cf_type == DT_DIR) {
! 324: inattic = 0;
! 325: if (verbosity > 1)
! 326: cvs_log(LP_INFO, "Updating %s", fpath);
! 327:
! 328: if (cvs_cmdop != CVS_OP_SERVER) {
! 329: /*
! 330: * We pass an empty repository name to
! 331: * cvs_create_dir(), because it will correctly
! 332: * create the repository directory for us.
! 333: */
! 334: if (cvs_create_dir(fpath, 1, root->cr_dir, NULL) < 0)
! 335: return (CVS_EX_FILE);
! 336: if (fchdir(cwdfd) < 0) {
! 337: cvs_log(LP_ERRNO, "fchdir failed");
! 338: return (CVS_EX_FILE);
! 339: }
! 340: } else {
! 341: /*
! 342: * TODO: send responses to client so it'll
! 343: * create it's directories.
! 344: */
! 345: }
! 346:
! 347: return (CVS_EX_OK);
! 348: }
! 349:
! 350: if (inattic == 1)
! 351: return (CVS_EX_OK);
! 352:
! 353: if ((rf = rcs_open(rcspath, RCS_READ)) == NULL) {
! 354: cvs_log(LP_ERR, "cvs_checkout_local: rcs_open failed");
! 355: return (CVS_EX_DATA);
! 356: }
! 357:
! 358: if (cvs_checkout_rev(rf, rf->rf_head, cf, fpath,
! 359: (cvs_cmdop != CVS_OP_SERVER) ? 1 : 0,
! 360: CHECKOUT_REV_CREATED) < 0) {
! 361: rcs_close(rf);
! 362: return (CVS_EX_DATA);
! 363: }
! 364:
! 365: rcs_close(rf);
! 366:
! 367: cvs_printf("U %s\n", fpath);
! 368: return (CVS_EX_OK);
1.1 jfb 369: }