Annotation of src/usr.bin/rcs/co.c, Revision 1.39
1.39 ! xsa 1: /* $OpenBSD: co.c,v 1.38 2005/11/29 10:55:37 xsa Exp $ */
1.1 joris 2: /*
3: * Copyright (c) 2005 Joris Vink <joris@openbsd.org>
4: * All rights reserved.
5: *
6: * Redistribution and use in source and binary forms, with or without
7: * modification, are permitted provided that the following conditions
8: * are met:
9: *
10: * 1. Redistributions of source code must retain the above copyright
11: * notice, this list of conditions and the following disclaimer.
12: * 2. The name of the author may not be used to endorse or promote products
13: * derived from this software without specific prior written permission.
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
24: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25: */
26:
27: #include <sys/param.h>
28: #include <sys/stat.h>
29:
30: #include <stdio.h>
31: #include <stdlib.h>
32: #include <string.h>
1.4 joris 33: #include <unistd.h>
1.1 joris 34:
35: #include "log.h"
36: #include "rcs.h"
37: #include "rcsprog.h"
38:
1.38 xsa 39: #define CO_OPTSTRING "f::k:l::M::p::q::r::s:Tu::Vw::x:"
40:
1.24 niallo 41: static int checkout_state(RCSFILE *, RCSNUM *, const char *, int,
42: const char *, const char *);
1.4 joris 43:
1.1 joris 44: int
45: checkout_main(int argc, char **argv)
46: {
1.33 xsa 47: int i, ch, flags, kflag;
1.4 joris 48: RCSNUM *frev, *rev;
1.1 joris 49: RCSFILE *file;
1.14 niallo 50: char fpath[MAXPATHLEN], buf[16];
1.4 joris 51: char *username;
1.38 xsa 52: const char *state;
1.37 xsa 53: time_t rcs_mtime = -1;
1.1 joris 54:
1.24 niallo 55: flags = 0;
1.33 xsa 56: kflag = RCS_KWEXP_ERR;
1.1 joris 57: rev = RCS_HEAD_REV;
1.6 joris 58: frev = NULL;
1.38 xsa 59: state = username = NULL;
1.4 joris 60:
1.38 xsa 61: while ((ch = rcs_getopt(argc, argv, CO_OPTSTRING)) != -1) {
1.1 joris 62: switch (ch) {
1.18 joris 63: case 'f':
1.19 joris 64: rcs_set_rev(rcs_optarg, &rev);
1.24 niallo 65: flags |= FORCE;
1.18 joris 66: break;
1.33 xsa 67: case 'k':
68: kflag = rcs_kflag_get(rcs_optarg);
69: if (RCS_KWEXP_INVAL(kflag)) {
70: cvs_log(LP_ERR,
71: "invalid RCS keyword expansion mode");
72: (usage)();
73: exit(1);
74: }
75: break;
1.4 joris 76: case 'l':
1.19 joris 77: rcs_set_rev(rcs_optarg, &rev);
1.24 niallo 78: flags |= CO_LOCK;
1.26 niallo 79: break;
80: case 'M':
81: rcs_set_rev(rcs_optarg, &rev);
82: flags |= CO_REVDATE;
1.4 joris 83: break;
1.20 joris 84: case 'p':
85: rcs_set_rev(rcs_optarg, &rev);
86: pipeout = 1;
87: break;
1.3 joris 88: case 'q':
1.32 xsa 89: rcs_set_rev(rcs_optarg, &rev);
1.3 joris 90: verbose = 0;
91: break;
1.1 joris 92: case 'r':
1.19 joris 93: rcs_set_rev(rcs_optarg, &rev);
1.4 joris 94: break;
1.24 niallo 95: case 's':
96: if ((state = strdup(rcs_optarg)) == NULL) {
97: cvs_log(LP_ERRNO, "out of memory");
98: exit(1);
99: }
100: flags |= CO_STATE;
1.34 xsa 101: break;
102: case 'T':
103: flags |= PRESERVETIME;
1.24 niallo 104: break;
1.4 joris 105: case 'u':
1.19 joris 106: rcs_set_rev(rcs_optarg, &rev);
1.24 niallo 107: flags |= CO_UNLOCK;
1.1 joris 108: break;
1.7 joris 109: case 'V':
110: printf("%s\n", rcs_version);
111: exit(0);
1.38 xsa 112: case 'w':
113: username = rcs_optarg;
114: break;
1.30 xsa 115: case 'x':
116: rcs_suffixes = rcs_optarg;
117: break;
1.1 joris 118: default:
119: (usage)();
120: exit(1);
121: }
122: }
123:
1.13 joris 124: argc -= rcs_optind;
125: argv += rcs_optind;
1.1 joris 126:
127: if (argc == 0) {
128: cvs_log(LP_ERR, "no input file");
129: (usage)();
130: exit (1);
131: }
1.11 deraadt 132:
1.38 xsa 133: if ((username == NULL) && ((username = getlogin()) == NULL)) {
134: cvs_log(LP_ERRNO, "failed to get username");
135: exit (1);
136: }
137:
1.1 joris 138: for (i = 0; i < argc; i++) {
139: if (rcs_statfile(argv[i], fpath, sizeof(fpath)) < 0)
140: continue;
141:
1.21 niallo 142: if (verbose == 1)
1.22 xsa 143: printf("%s --> %s\n", fpath,
144: (pipeout == 1) ? "standard output" : argv[i]);
1.35 xsa 145:
146: if ((flags & CO_LOCK) && (kflag & RCS_KWEXP_VAL)) {
147: cvs_log(LP_ERR, "%s: cannot combine -kv and -l", fpath);
148: continue;
149: }
1.21 niallo 150:
1.4 joris 151: if ((file = rcs_open(fpath, RCS_RDWR)) == NULL)
1.1 joris 152: continue;
153:
1.37 xsa 154: if (flags & PRESERVETIME)
155: rcs_mtime = rcs_get_mtime(file->rf_path);
156:
157: if (kflag != RCS_KWEXP_ERR)
158: rcs_kwexp_set(file, kflag);
159:
1.4 joris 160: if (rev == RCS_HEAD_REV)
161: frev = file->rf_head;
162: else
163: frev = rev;
1.17 joris 164:
1.4 joris 165: rcsnum_tostr(frev, buf, sizeof(buf));
1.33 xsa 166:
1.24 niallo 167: if (flags & CO_STATE) {
168: if (checkout_state(file, frev, argv[i], flags,
169: username, state) < 0) {
170: rcs_close(file);
171: continue;
172: }
1.37 xsa 173: } else {
1.24 niallo 174: if (checkout_rev(file, frev, argv[i], flags,
175: username) < 0) {
176: rcs_close(file);
177: continue;
178: }
1.8 niallo 179: }
1.17 joris 180:
1.1 joris 181: rcs_close(file);
1.37 xsa 182:
183: if (flags & PRESERVETIME)
184: rcs_set_mtime(fpath, rcs_mtime);
1.1 joris 185: }
186:
187: if (rev != RCS_HEAD_REV)
1.5 joris 188: rcsnum_free(frev);
1.1 joris 189:
190: return (0);
191: }
192:
193: void
194: checkout_usage(void)
195: {
1.11 deraadt 196: fprintf(stderr,
1.32 xsa 197: "usage: co [-V] [-ddate] [-f[rev]] [-I[rev]] [-kmode] [-l[rev]]\n"
198: " [-M[rev]] [-mmsg] [-p[rev]] [-q[rev]] [-r[rev]]\n"
1.38 xsa 199: " [-sstate] [-u[rev]] [-w[user]] [-xsuffixes] [-ztz] file ...\n");
1.1 joris 200: }
1.14 niallo 201:
202: /*
203: * Checkout revision <rev> from RCSFILE <file>, writing it to the path <dst>
1.29 xsa 204: * Currenly recognised <flags> are CO_LOCK, CO_UNLOCK and CO_REVDATE.
1.14 niallo 205: *
206: * Returns 0 on success, -1 on failure.
207: */
208: int
1.24 niallo 209: checkout_rev(RCSFILE *file, RCSNUM *frev, const char *dst, int flags,
210: const char *username)
1.14 niallo 211: {
1.18 joris 212: char buf[16], yn;
1.14 niallo 213: mode_t mode = 0444;
214: BUF *bp;
1.18 joris 215: struct stat st;
1.20 joris 216: char *content;
1.14 niallo 217:
1.39 ! xsa 218: /* Check out the latest revision if <frev> is greater than HEAD */
1.15 niallo 219: if (rcsnum_cmp(frev, file->rf_head, 0) == -1)
1.23 xsa 220: frev = file->rf_head;
1.15 niallo 221:
222: rcsnum_tostr(frev, buf, sizeof(buf));
223:
1.18 joris 224: if (verbose == 1)
225: printf("revision %s", buf);
226:
1.14 niallo 227: if ((bp = rcs_getrev(file, frev)) == NULL) {
228: cvs_log(LP_ERR, "cannot find revision `%s'", buf);
229: return (-1);
230: }
231:
1.24 niallo 232: if (flags & CO_LOCK) {
1.14 niallo 233: if ((username != NULL)
234: && (rcs_lock_add(file, username, frev) < 0)) {
1.21 niallo 235: if ((rcs_errno != RCS_ERR_DUPENT) && (verbose == 1))
1.14 niallo 236: cvs_log(LP_ERR, "failed to lock '%s'", buf);
237: }
1.18 joris 238:
1.14 niallo 239: mode = 0644;
1.18 joris 240: if (verbose == 1)
241: printf(" (locked)");
1.24 niallo 242: } else if (flags & CO_UNLOCK) {
1.14 niallo 243: if (rcs_lock_remove(file, frev) < 0) {
244: if (rcs_errno != RCS_ERR_NOENT)
245: cvs_log(LP_ERR,
246: "failed to remove lock '%s'", buf);
247: }
1.18 joris 248:
1.14 niallo 249: mode = 0444;
1.18 joris 250: if (verbose == 1)
251: printf(" (unlocked)");
252: }
253:
254: if (verbose == 1)
255: printf("\n");
256:
1.31 xsa 257: if ((pipeout == 0) && (stat(dst, &st) != -1) && !(flags & FORCE)) {
1.18 joris 258: if (st.st_mode & S_IWUSR) {
259: yn = 0;
1.21 niallo 260: if (verbose == 0) {
261: cvs_log(LP_ERR,
262: "writeable %s exists; checkout aborted",
263: dst);
264: return (-1);
265: }
1.18 joris 266: while (yn != 'y' && yn != 'n') {
1.21 niallo 267: printf("writeable %s exists; ", dst);
268: printf("remove it? [ny](n): ");
1.18 joris 269: fflush(stdout);
270: yn = getchar();
271: }
272:
273: if (yn == 'n') {
1.21 niallo 274: cvs_log(LP_ERR, "checkout aborted");
1.18 joris 275: return (-1);
276: }
277: }
1.14 niallo 278: }
1.17 joris 279:
1.20 joris 280: if (pipeout == 1) {
281: cvs_buf_putc(bp, '\0');
282: content = cvs_buf_release(bp);
283: printf("%s", content);
284: free(content);
285: } else {
286: if (cvs_buf_write(bp, dst, mode) < 0) {
287: cvs_log(LP_ERR, "failed to write revision to file");
288: cvs_buf_free(bp);
289: return (-1);
290: }
1.14 niallo 291: cvs_buf_free(bp);
1.24 niallo 292: if (flags & CO_REVDATE) {
293: struct timeval tv[2];
1.36 xsa 294: memset(&tv, 0, sizeof(tv));
1.24 niallo 295: tv[0].tv_sec = (long)rcs_rev_getdate(file, frev);
296: tv[1].tv_sec = tv[0].tv_sec;
297: if (utimes(dst, (const struct timeval *)&tv) < 0)
298: cvs_log(LP_ERRNO, "error setting utimes");
299: }
1.20 joris 300:
301: if (verbose == 1)
302: printf("done\n");
1.14 niallo 303: }
1.17 joris 304:
1.14 niallo 305: return (0);
306: }
307:
1.24 niallo 308: /*
309: * checkout_state()
310: *
311: * Search from supplied revision backwards until we find one
312: * with state <state> and check that out.
313: *
1.25 niallo 314: * Returns 0 on success, -1 on checkout_rev failure.
1.24 niallo 315: */
316: static int
1.39 ! xsa 317: checkout_state(RCSFILE *file, RCSNUM *rev, const char *dst, int flags,
1.24 niallo 318: const char *username, const char *state)
319: {
320: const char *tstate;
321:
322: if (rev == NULL) {
323: cvs_log(LP_ERR, "%s: No revision on branch has state %s",
1.39 ! xsa 324: file->rf_path, state);
1.24 niallo 325: return (-1);
1.38 xsa 326: } else {
1.39 ! xsa 327: if (((tstate = rcs_state_get(file, rev)) != NULL)
1.24 niallo 328: && (strcmp(state, tstate) == 0))
1.39 ! xsa 329: return (checkout_rev(file, rev, dst, flags, username));
1.24 niallo 330: else
331: rev = rcsnum_dec(rev);
1.39 ! xsa 332: return (checkout_state(file, rev, dst, flags, username, state));
1.24 niallo 333: }
334: }