Annotation of src/usr.bin/rcs/co.c, Revision 1.25
1.25 ! niallo 1: /* $OpenBSD: co.c,v 1.24 2005/11/02 20:32:45 niallo 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.24 niallo 39: static int checkout_state(RCSFILE *, RCSNUM *, const char *, int,
40: const char *, const char *);
1.4 joris 41:
1.1 joris 42: int
43: checkout_main(int argc, char **argv)
44: {
45: int i, ch;
1.24 niallo 46: int flags;
1.4 joris 47: RCSNUM *frev, *rev;
1.1 joris 48: RCSFILE *file;
1.14 niallo 49: char fpath[MAXPATHLEN], buf[16];
1.4 joris 50: char *username;
1.24 niallo 51: const char *state = NULL;
1.1 joris 52:
1.24 niallo 53: flags = 0;
1.1 joris 54: rev = RCS_HEAD_REV;
1.6 joris 55: frev = NULL;
1.4 joris 56:
57: if ((username = getlogin()) == NULL) {
1.10 xsa 58: cvs_log(LP_ERRNO, "failed to get username");
1.4 joris 59: exit (1);
60: }
61:
1.24 niallo 62: while ((ch = rcs_getopt(argc, argv, "f::l::p::qr::s:u::V")) != -1) {
1.1 joris 63: switch (ch) {
1.18 joris 64: case 'f':
1.19 joris 65: rcs_set_rev(rcs_optarg, &rev);
1.24 niallo 66: flags |= FORCE;
1.18 joris 67: break;
1.4 joris 68: case 'l':
1.19 joris 69: rcs_set_rev(rcs_optarg, &rev);
1.24 niallo 70: flags |= CO_LOCK;
1.4 joris 71: break;
1.20 joris 72: case 'p':
73: rcs_set_rev(rcs_optarg, &rev);
74: pipeout = 1;
75: break;
1.3 joris 76: case 'q':
77: verbose = 0;
78: break;
1.1 joris 79: case 'r':
1.19 joris 80: rcs_set_rev(rcs_optarg, &rev);
1.4 joris 81: break;
1.24 niallo 82: case 's':
83: if ((state = strdup(rcs_optarg)) == NULL) {
84: cvs_log(LP_ERRNO, "out of memory");
85: exit(1);
86: }
87: flags |= CO_STATE;
88: break;
1.4 joris 89: case 'u':
1.19 joris 90: rcs_set_rev(rcs_optarg, &rev);
1.24 niallo 91: flags |= CO_UNLOCK;
1.1 joris 92: break;
1.7 joris 93: case 'V':
94: printf("%s\n", rcs_version);
95: exit(0);
1.1 joris 96: default:
97: (usage)();
98: exit(1);
99: }
100: }
101:
1.13 joris 102: argc -= rcs_optind;
103: argv += rcs_optind;
1.1 joris 104:
105: if (argc == 0) {
106: cvs_log(LP_ERR, "no input file");
107: (usage)();
108: exit (1);
109: }
1.11 deraadt 110:
1.1 joris 111: for (i = 0; i < argc; i++) {
112: if (rcs_statfile(argv[i], fpath, sizeof(fpath)) < 0)
113: continue;
114:
1.21 niallo 115: if (verbose == 1)
1.22 xsa 116: printf("%s --> %s\n", fpath,
117: (pipeout == 1) ? "standard output" : argv[i]);
1.21 niallo 118:
1.4 joris 119: if ((file = rcs_open(fpath, RCS_RDWR)) == NULL)
1.1 joris 120: continue;
121:
1.4 joris 122: if (rev == RCS_HEAD_REV)
123: frev = file->rf_head;
124: else
125: frev = rev;
1.17 joris 126:
1.4 joris 127: rcsnum_tostr(frev, buf, sizeof(buf));
1.17 joris 128:
1.24 niallo 129: if (flags & CO_STATE) {
130: if (checkout_state(file, frev, argv[i], flags,
131: username, state) < 0) {
132: rcs_close(file);
133: continue;
134: }
135: }
136: else {
137: if (checkout_rev(file, frev, argv[i], flags,
138: username) < 0) {
139: rcs_close(file);
140: continue;
141: }
1.8 niallo 142: }
1.17 joris 143:
1.1 joris 144: rcs_close(file);
145: }
146:
147: if (rev != RCS_HEAD_REV)
1.5 joris 148: rcsnum_free(frev);
1.1 joris 149:
150: return (0);
151: }
152:
153: void
154: checkout_usage(void)
155: {
1.11 deraadt 156: fprintf(stderr,
1.24 niallo 157: "usage: co [-qV] [-l[rev]] [-p[rev]] [-r[rev]] [-sstate]\n"
158: " [-u[rev]] file ...\n");
1.1 joris 159: }
1.14 niallo 160:
161: /*
162: * Checkout revision <rev> from RCSFILE <file>, writing it to the path <dst>
163: * <lkmode> is either LOCK_LOCK or LOCK_UNLOCK or something else
164: * (which has no effect).
165: * In the case of LOCK_LOCK, a lock is set for <username> if it is not NULL.
166: * In the case of LOCK_UNLOCK, all locks are removed for that revision.
167: *
168: * Returns 0 on success, -1 on failure.
169: */
170: int
1.24 niallo 171: checkout_rev(RCSFILE *file, RCSNUM *frev, const char *dst, int flags,
172: const char *username)
1.14 niallo 173: {
1.18 joris 174: char buf[16], yn;
1.14 niallo 175: mode_t mode = 0444;
176: BUF *bp;
1.18 joris 177: struct stat st;
1.20 joris 178: char *content;
1.14 niallo 179:
180: /*
1.15 niallo 181: * Check out the latest revision if <frev> is greater than HEAD
1.14 niallo 182: */
1.15 niallo 183: if (rcsnum_cmp(frev, file->rf_head, 0) == -1)
1.23 xsa 184: frev = file->rf_head;
1.15 niallo 185:
186: rcsnum_tostr(frev, buf, sizeof(buf));
187:
1.18 joris 188: if (verbose == 1)
189: printf("revision %s", buf);
190:
1.14 niallo 191: if ((bp = rcs_getrev(file, frev)) == NULL) {
192: cvs_log(LP_ERR, "cannot find revision `%s'", buf);
193: return (-1);
194: }
195:
1.24 niallo 196: if (flags & CO_LOCK) {
1.14 niallo 197: if ((username != NULL)
198: && (rcs_lock_add(file, username, frev) < 0)) {
1.21 niallo 199: if ((rcs_errno != RCS_ERR_DUPENT) && (verbose == 1))
1.14 niallo 200: cvs_log(LP_ERR, "failed to lock '%s'", buf);
201: }
1.18 joris 202:
1.14 niallo 203: mode = 0644;
1.18 joris 204: if (verbose == 1)
205: printf(" (locked)");
1.24 niallo 206: } else if (flags & CO_UNLOCK) {
1.14 niallo 207: if (rcs_lock_remove(file, frev) < 0) {
208: if (rcs_errno != RCS_ERR_NOENT)
209: cvs_log(LP_ERR,
210: "failed to remove lock '%s'", buf);
211: }
1.18 joris 212:
1.14 niallo 213: mode = 0444;
1.18 joris 214: if (verbose == 1)
215: printf(" (unlocked)");
216: }
217:
218: if (verbose == 1)
219: printf("\n");
220:
1.24 niallo 221: if ((stat(dst, &st) != -1) && !(flags & FORCE)) {
1.18 joris 222: if (st.st_mode & S_IWUSR) {
223: yn = 0;
1.21 niallo 224: if (verbose == 0) {
225: cvs_log(LP_ERR,
226: "writeable %s exists; checkout aborted",
227: dst);
228: return (-1);
229: }
1.18 joris 230: while (yn != 'y' && yn != 'n') {
1.21 niallo 231: printf("writeable %s exists; ", dst);
232: printf("remove it? [ny](n): ");
1.18 joris 233: fflush(stdout);
234: yn = getchar();
235: }
236:
237: if (yn == 'n') {
1.21 niallo 238: cvs_log(LP_ERR, "checkout aborted");
1.18 joris 239: return (-1);
240: }
241: }
1.14 niallo 242: }
1.17 joris 243:
1.20 joris 244: if (pipeout == 1) {
245: cvs_buf_putc(bp, '\0');
246: content = cvs_buf_release(bp);
247: printf("%s", content);
248: free(content);
249: } else {
250: if (cvs_buf_write(bp, dst, mode) < 0) {
251: cvs_log(LP_ERR, "failed to write revision to file");
252: cvs_buf_free(bp);
253: return (-1);
254: }
1.14 niallo 255: cvs_buf_free(bp);
1.24 niallo 256: if (flags & CO_REVDATE) {
257: struct timeval tv[2];
258: bzero(&tv, sizeof(tv));
259: tv[0].tv_sec = (long)rcs_rev_getdate(file, frev);
260: tv[1].tv_sec = tv[0].tv_sec;
261: if (utimes(dst, (const struct timeval *)&tv) < 0)
262: cvs_log(LP_ERRNO, "error setting utimes");
263: }
1.20 joris 264:
265: if (verbose == 1)
266: printf("done\n");
1.14 niallo 267: }
1.17 joris 268:
1.14 niallo 269: return (0);
270: }
271:
1.24 niallo 272: /*
273: * checkout_state()
274: *
275: * Search from supplied revision backwards until we find one
276: * with state <state> and check that out.
277: *
1.25 ! niallo 278: * Returns 0 on success, -1 on checkout_rev failure.
1.24 niallo 279: */
280: static int
281: checkout_state(RCSFILE *rfp, RCSNUM *rev, const char *dst, int flags,
282: const char *username, const char *state)
283: {
284: const char *tstate;
285:
286: if (rev == NULL) {
287: cvs_log(LP_ERR, "%s: No revision on branch has state %s",
288: rfp->rf_path, state);
289: return (-1);
290: }
291: else {
292: if (((tstate = rcs_state_get(rfp, rev)) != NULL)
293: && (strcmp(state, tstate) == 0))
294: return (checkout_rev(rfp, rev, dst, flags, username));
295: else
296: rev = rcsnum_dec(rev);
297: return (checkout_state(rfp, rev, dst, flags, username, state));
298: }
299: }