1 |
|
|
/* $OpenBSD: co.c,v 1.123 2017/08/29 16:47:33 otto Exp $ */ |
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/stat.h> |
28 |
|
|
#include <sys/time.h> |
29 |
|
|
|
30 |
|
|
#include <err.h> |
31 |
|
|
#include <fcntl.h> |
32 |
|
|
#include <stdio.h> |
33 |
|
|
#include <stdlib.h> |
34 |
|
|
#include <string.h> |
35 |
|
|
#include <unistd.h> |
36 |
|
|
|
37 |
|
|
#include "rcsprog.h" |
38 |
|
|
#include "diff.h" |
39 |
|
|
|
40 |
|
|
#define CO_OPTSTRING "d:f::I::k:l::M::p::q::r::s:Tu::Vw::x::z::" |
41 |
|
|
|
42 |
|
|
static void checkout_err_nobranch(RCSFILE *, const char *, const char *, |
43 |
|
|
const char *, int); |
44 |
|
|
static int checkout_file_has_diffs(RCSFILE *, RCSNUM *, const char *); |
45 |
|
|
|
46 |
|
|
int |
47 |
|
|
checkout_main(int argc, char **argv) |
48 |
|
|
{ |
49 |
|
|
int fd, i, ch, flags, kflag, ret; |
50 |
|
|
RCSNUM *rev; |
51 |
|
|
RCSFILE *file; |
52 |
|
|
const char *author, *date, *state; |
53 |
|
|
char fpath[PATH_MAX]; |
54 |
|
|
char *rev_str, *username; |
55 |
|
|
time_t rcs_mtime = -1; |
56 |
|
|
|
57 |
|
|
flags = ret = 0; |
58 |
|
|
kflag = RCS_KWEXP_ERR; |
59 |
|
|
rev_str = NULL; |
60 |
|
|
author = date = state = NULL; |
61 |
|
|
|
62 |
|
|
while ((ch = rcs_getopt(argc, argv, CO_OPTSTRING)) != -1) { |
63 |
|
|
switch (ch) { |
64 |
|
|
case 'd': |
65 |
|
|
date = rcs_optarg; |
66 |
|
|
break; |
67 |
|
|
case 'f': |
68 |
|
|
rcs_setrevstr(&rev_str, rcs_optarg); |
69 |
|
|
flags |= FORCE; |
70 |
|
|
break; |
71 |
|
|
case 'I': |
72 |
|
|
rcs_setrevstr(&rev_str, rcs_optarg); |
73 |
|
|
flags |= INTERACTIVE; |
74 |
|
|
break; |
75 |
|
|
|
76 |
|
|
case 'k': |
77 |
|
|
kflag = rcs_kflag_get(rcs_optarg); |
78 |
|
|
if (RCS_KWEXP_INVAL(kflag)) { |
79 |
|
|
warnx("invalid RCS keyword substitution mode"); |
80 |
|
|
(usage)(); |
81 |
|
|
} |
82 |
|
|
break; |
83 |
|
|
case 'l': |
84 |
|
|
if (flags & CO_UNLOCK) { |
85 |
|
|
warnx("warning: -u overridden by -l"); |
86 |
|
|
flags &= ~CO_UNLOCK; |
87 |
|
|
} |
88 |
|
|
rcs_setrevstr(&rev_str, rcs_optarg); |
89 |
|
|
flags |= CO_LOCK; |
90 |
|
|
break; |
91 |
|
|
case 'M': |
92 |
|
|
rcs_setrevstr(&rev_str, rcs_optarg); |
93 |
|
|
flags |= CO_REVDATE; |
94 |
|
|
break; |
95 |
|
|
case 'p': |
96 |
|
|
rcs_setrevstr(&rev_str, rcs_optarg); |
97 |
|
|
flags |= PIPEOUT; |
98 |
|
|
break; |
99 |
|
|
case 'q': |
100 |
|
|
rcs_setrevstr(&rev_str, rcs_optarg); |
101 |
|
|
flags |= QUIET; |
102 |
|
|
break; |
103 |
|
|
case 'r': |
104 |
|
|
rcs_setrevstr(&rev_str, rcs_optarg); |
105 |
|
|
break; |
106 |
|
|
case 's': |
107 |
|
|
state = rcs_optarg; |
108 |
|
|
flags |= CO_STATE; |
109 |
|
|
break; |
110 |
|
|
case 'T': |
111 |
|
|
flags |= PRESERVETIME; |
112 |
|
|
break; |
113 |
|
|
case 'u': |
114 |
|
|
rcs_setrevstr(&rev_str, rcs_optarg); |
115 |
|
|
if (flags & CO_LOCK) { |
116 |
|
|
warnx("warning: -l overridden by -u"); |
117 |
|
|
flags &= ~CO_LOCK; |
118 |
|
|
} |
119 |
|
|
flags |= CO_UNLOCK; |
120 |
|
|
break; |
121 |
|
|
case 'V': |
122 |
|
|
printf("%s\n", rcs_version); |
123 |
|
|
exit(0); |
124 |
|
|
case 'w': |
125 |
|
|
/* if no argument, assume current user */ |
126 |
|
|
if (rcs_optarg == NULL) { |
127 |
|
|
if ((author = getlogin()) == NULL) |
128 |
|
|
err(1, "getlogin"); |
129 |
|
|
} else |
130 |
|
|
author = rcs_optarg; |
131 |
|
|
flags |= CO_AUTHOR; |
132 |
|
|
break; |
133 |
|
|
case 'x': |
134 |
|
|
/* Use blank extension if none given. */ |
135 |
|
|
rcs_suffixes = rcs_optarg ? rcs_optarg : ""; |
136 |
|
|
break; |
137 |
|
|
case 'z': |
138 |
|
|
timezone_flag = rcs_optarg; |
139 |
|
|
break; |
140 |
|
|
default: |
141 |
|
|
(usage)(); |
142 |
|
|
} |
143 |
|
|
} |
144 |
|
|
|
145 |
|
|
argc -= rcs_optind; |
146 |
|
|
argv += rcs_optind; |
147 |
|
|
|
148 |
|
|
if (argc == 0) { |
149 |
|
|
warnx("no input file"); |
150 |
|
|
(usage)(); |
151 |
|
|
} |
152 |
|
|
|
153 |
|
|
if ((username = getlogin()) == NULL) |
154 |
|
|
err(1, "getlogin"); |
155 |
|
|
|
156 |
|
|
for (i = 0; i < argc; i++) { |
157 |
|
|
fd = rcs_choosefile(argv[i], fpath, sizeof(fpath)); |
158 |
|
|
if (fd < 0) { |
159 |
|
|
warn("%s", fpath); |
160 |
|
|
ret = 1; |
161 |
|
|
continue; |
162 |
|
|
} |
163 |
|
|
rcs_strip_suffix(argv[i]); |
164 |
|
|
|
165 |
|
|
if (!(flags & QUIET)) |
166 |
|
|
(void)fprintf(stderr, "%s --> %s\n", fpath, |
167 |
|
|
(flags & PIPEOUT) ? "standard output" : argv[i]); |
168 |
|
|
|
169 |
|
|
if ((flags & CO_LOCK) && (kflag & RCS_KWEXP_VAL)) { |
170 |
|
|
warnx("%s: cannot combine -kv and -l", fpath); |
171 |
|
|
(void)close(fd); |
172 |
|
|
continue; |
173 |
|
|
} |
174 |
|
|
|
175 |
|
|
if ((file = rcs_open(fpath, fd, |
176 |
|
|
RCS_RDWR|RCS_PARSE_FULLY)) == NULL) |
177 |
|
|
continue; |
178 |
|
|
|
179 |
|
|
if (flags & PRESERVETIME) |
180 |
|
|
rcs_mtime = rcs_get_mtime(file); |
181 |
|
|
|
182 |
|
|
rcs_kwexp_set(file, kflag); |
183 |
|
|
|
184 |
|
|
if (rev_str != NULL) { |
185 |
|
|
if ((rev = rcs_getrevnum(rev_str, file)) == NULL) |
186 |
|
|
errx(1, "invalid revision: %s", rev_str); |
187 |
|
|
} else { |
188 |
|
|
/* no revisions in RCS file, generate empty 0.0 */ |
189 |
|
|
if (file->rf_ndelta == 0) { |
190 |
|
|
rev = rcsnum_parse("0.0"); |
191 |
|
|
if (rev == NULL) |
192 |
|
|
errx(1, "failed to generate rev 0.0"); |
193 |
|
|
} else { |
194 |
|
|
rev = rcsnum_alloc(); |
195 |
|
|
rcsnum_cpy(file->rf_head, rev, 0); |
196 |
|
|
} |
197 |
|
|
} |
198 |
|
|
|
199 |
|
|
if (checkout_rev(file, rev, argv[i], flags, |
200 |
|
|
username, author, state, date) < 0) { |
201 |
|
|
rcs_close(file); |
202 |
|
|
rcsnum_free(rev); |
203 |
|
|
ret = 1; |
204 |
|
|
continue; |
205 |
|
|
} |
206 |
|
|
|
207 |
|
|
if (!(flags & QUIET)) |
208 |
|
|
(void)fprintf(stderr, "done\n"); |
209 |
|
|
|
210 |
|
|
rcsnum_free(rev); |
211 |
|
|
|
212 |
|
|
rcs_write(file); |
213 |
|
|
if (flags & PRESERVETIME) |
214 |
|
|
rcs_set_mtime(file, rcs_mtime); |
215 |
|
|
rcs_close(file); |
216 |
|
|
} |
217 |
|
|
|
218 |
|
|
return (ret); |
219 |
|
|
} |
220 |
|
|
|
221 |
|
|
__dead void |
222 |
|
|
checkout_usage(void) |
223 |
|
|
{ |
224 |
|
|
fprintf(stderr, |
225 |
|
|
"usage: co [-TV] [-ddate] [-f[rev]] [-I[rev]] [-kmode] [-l[rev]]\n" |
226 |
|
|
" [-M[rev]] [-p[rev]] [-q[rev]] [-r[rev]] [-sstate]\n" |
227 |
|
|
" [-u[rev]] [-w[user]] [-xsuffixes] [-ztz] file ...\n"); |
228 |
|
|
|
229 |
|
|
exit(1); |
230 |
|
|
} |
231 |
|
|
|
232 |
|
|
/* |
233 |
|
|
* Checkout revision <rev> from RCSFILE <file>, writing it to the path <dst> |
234 |
|
|
* Currently recognised <flags> are CO_LOCK, CO_UNLOCK and CO_REVDATE. |
235 |
|
|
* |
236 |
|
|
* Looks up revision based upon <lockname>, <author>, <state> and <date> |
237 |
|
|
* |
238 |
|
|
* Returns 0 on success, -1 on failure. |
239 |
|
|
*/ |
240 |
|
|
int |
241 |
|
|
checkout_rev(RCSFILE *file, RCSNUM *frev, const char *dst, int flags, |
242 |
|
|
const char *lockname, const char *author, const char *state, |
243 |
|
|
const char *date) |
244 |
|
|
{ |
245 |
|
|
BUF *bp; |
246 |
|
|
u_int i; |
247 |
|
|
int fd, lcount; |
248 |
|
|
char buf[RCS_REV_BUFSZ]; |
249 |
|
|
mode_t mode = DEFFILEMODE; |
250 |
|
|
struct stat st; |
251 |
|
|
struct rcs_delta *rdp; |
252 |
|
|
struct rcs_lock *lkp; |
253 |
|
|
char *fdate; |
254 |
|
|
const char *fstatus; |
255 |
|
|
time_t rcsdate, givendate; |
256 |
|
|
RCSNUM *rev; |
257 |
|
|
|
258 |
|
|
givendate = -1; |
259 |
|
|
if (date != NULL && (givendate = date_parse(date)) == -1) { |
260 |
|
|
warnx("invalid date: %s", date); |
261 |
|
|
return -1; |
262 |
|
|
} |
263 |
|
|
|
264 |
|
|
if (file->rf_ndelta == 0 && !(flags & QUIET)) |
265 |
|
|
(void)fprintf(stderr, |
266 |
|
|
"no revisions present; generating empty revision 0.0\n"); |
267 |
|
|
|
268 |
|
|
/* XXX rcsnum_cmp() |
269 |
|
|
* Check out the latest revision if <frev> is greater than HEAD |
270 |
|
|
*/ |
271 |
|
|
if (file->rf_ndelta != 0) { |
272 |
|
|
for (i = 0; i < file->rf_head->rn_len; i++) { |
273 |
|
|
if (file->rf_head->rn_id[i] < frev->rn_id[i]) { |
274 |
|
|
frev = file->rf_head; |
275 |
|
|
break; |
276 |
|
|
} |
277 |
|
|
} |
278 |
|
|
} |
279 |
|
|
|
280 |
|
|
lcount = 0; |
281 |
|
|
TAILQ_FOREACH(lkp, &(file->rf_locks), rl_list) { |
282 |
|
|
if (!strcmp(lkp->rl_name, lockname)) |
283 |
|
|
lcount++; |
284 |
|
|
} |
285 |
|
|
|
286 |
|
|
/* |
287 |
|
|
* If the user didn't specify any revision, we cycle through |
288 |
|
|
* revisions to lookup the first one that matches what he specified. |
289 |
|
|
* |
290 |
|
|
* If we cannot find one, we return an error. |
291 |
|
|
*/ |
292 |
|
|
rdp = NULL; |
293 |
|
|
if (file->rf_ndelta != 0 && frev == file->rf_head) { |
294 |
|
|
if (lcount > 1) { |
295 |
|
|
warnx("multiple revisions locked by %s; " |
296 |
|
|
"please specify one", lockname); |
297 |
|
|
return (-1); |
298 |
|
|
} |
299 |
|
|
|
300 |
|
|
TAILQ_FOREACH(rdp, &file->rf_delta, rd_list) { |
301 |
|
|
if (date != NULL) { |
302 |
|
|
fdate = asctime(&rdp->rd_date); |
303 |
|
|
if ((rcsdate = date_parse(fdate)) == -1) { |
304 |
|
|
warnx("invalid date: %s", fdate); |
305 |
|
|
return -1; |
306 |
|
|
} |
307 |
|
|
if (givendate <= rcsdate) |
308 |
|
|
continue; |
309 |
|
|
} |
310 |
|
|
|
311 |
|
|
if (author != NULL && |
312 |
|
|
strcmp(rdp->rd_author, author)) |
313 |
|
|
continue; |
314 |
|
|
|
315 |
|
|
if (state != NULL && |
316 |
|
|
strcmp(rdp->rd_state, state)) |
317 |
|
|
continue; |
318 |
|
|
|
319 |
|
|
frev = rdp->rd_num; |
320 |
|
|
break; |
321 |
|
|
} |
322 |
|
|
} else if (file->rf_ndelta != 0) { |
323 |
|
|
rdp = rcs_findrev(file, frev); |
324 |
|
|
} |
325 |
|
|
|
326 |
|
|
if (file->rf_ndelta != 0 && rdp == NULL) { |
327 |
|
|
checkout_err_nobranch(file, author, date, state, flags); |
328 |
|
|
return (-1); |
329 |
|
|
} |
330 |
|
|
|
331 |
|
|
if (file->rf_ndelta == 0) |
332 |
|
|
rev = frev; |
333 |
|
|
else |
334 |
|
|
rev = rdp->rd_num; |
335 |
|
|
|
336 |
|
|
rcsnum_tostr(rev, buf, sizeof(buf)); |
337 |
|
|
|
338 |
|
|
if (file->rf_ndelta != 0 && rdp->rd_locker != NULL) { |
339 |
|
|
if (strcmp(lockname, rdp->rd_locker)) { |
340 |
|
|
warnx("Revision %s is already locked by %s; %s", |
341 |
|
|
buf, rdp->rd_locker, |
342 |
|
|
(flags & CO_UNLOCK) ? "use co -r or rcs -u" : ""); |
343 |
|
|
return (-1); |
344 |
|
|
} |
345 |
|
|
} |
346 |
|
|
|
347 |
|
|
if (!(flags & QUIET) && !(flags & NEWFILE) && |
348 |
|
|
!(flags & CO_REVERT) && file->rf_ndelta != 0) |
349 |
|
|
(void)fprintf(stderr, "revision %s", buf); |
350 |
|
|
|
351 |
|
|
if (file->rf_ndelta != 0) { |
352 |
|
|
if ((bp = rcs_getrev(file, rev)) == NULL) { |
353 |
|
|
warnx("cannot find revision `%s'", buf); |
354 |
|
|
return (-1); |
355 |
|
|
} |
356 |
|
|
} else { |
357 |
|
|
bp = buf_alloc(1); |
358 |
|
|
} |
359 |
|
|
|
360 |
|
|
/* |
361 |
|
|
* Do keyword expansion if required. |
362 |
|
|
*/ |
363 |
|
|
if (file->rf_ndelta != 0) |
364 |
|
|
bp = rcs_kwexp_buf(bp, file, rev); |
365 |
|
|
/* |
366 |
|
|
* File inherits permissions from its ,v file |
367 |
|
|
*/ |
368 |
|
|
if (file->rf_file != NULL) { |
369 |
|
|
if (fstat(fileno(file->rf_file), &st) == -1) |
370 |
|
|
err(1, "%s", file->rf_path); |
371 |
|
|
file->rf_mode = mode = st.st_mode; |
372 |
|
|
} else { |
373 |
|
|
mode = file->rf_mode; |
374 |
|
|
} |
375 |
|
|
|
376 |
|
|
if (flags & CO_LOCK) { |
377 |
|
|
if (file->rf_ndelta != 0) { |
378 |
|
|
if (lockname != NULL && |
379 |
|
|
rcs_lock_add(file, lockname, rev) < 0) { |
380 |
|
|
if (rcs_errno != RCS_ERR_DUPENT) |
381 |
|
|
return (-1); |
382 |
|
|
} |
383 |
|
|
} |
384 |
|
|
|
385 |
|
|
/* File should only be writable by owner. */ |
386 |
|
|
mode &= ~(S_IWGRP|S_IWOTH); |
387 |
|
|
mode |= S_IWUSR; |
388 |
|
|
|
389 |
|
|
if (file->rf_ndelta != 0) { |
390 |
|
|
if (!(flags & QUIET) && !(flags & NEWFILE) && |
391 |
|
|
!(flags & CO_REVERT)) |
392 |
|
|
(void)fprintf(stderr, " (locked)"); |
393 |
|
|
} |
394 |
|
|
} else if (flags & CO_UNLOCK) { |
395 |
|
|
if (file->rf_ndelta != 0) { |
396 |
|
|
if (rcs_lock_remove(file, lockname, rev) < 0) { |
397 |
|
|
if (rcs_errno != RCS_ERR_NOENT) |
398 |
|
|
return (-1); |
399 |
|
|
} |
400 |
|
|
} |
401 |
|
|
|
402 |
|
|
/* Strip all write bits from mode */ |
403 |
|
|
mode &= ~(S_IWUSR|S_IWGRP|S_IWOTH); |
404 |
|
|
|
405 |
|
|
if (file->rf_ndelta != 0) { |
406 |
|
|
if (!(flags & QUIET) && !(flags & NEWFILE) && |
407 |
|
|
!(flags & CO_REVERT)) |
408 |
|
|
(void)fprintf(stderr, " (unlocked)"); |
409 |
|
|
} |
410 |
|
|
} |
411 |
|
|
|
412 |
|
|
/* If strict locking is disabled, make file writable by owner. */ |
413 |
|
|
if (rcs_lock_getmode(file) == RCS_LOCK_LOOSE) |
414 |
|
|
mode |= S_IWUSR; |
415 |
|
|
|
416 |
|
|
if (file->rf_ndelta == 0 && !(flags & QUIET) && |
417 |
|
|
((flags & CO_LOCK) || (flags & CO_UNLOCK))) { |
418 |
|
|
(void)fprintf(stderr, "no revisions, so nothing can be %s\n", |
419 |
|
|
(flags & CO_LOCK) ? "locked" : "unlocked"); |
420 |
|
|
} else if (file->rf_ndelta != 0) { |
421 |
|
|
/* XXX - Not a good way to detect if a newline is needed. */ |
422 |
|
|
if (!(flags & QUIET) && !(flags & NEWFILE) && |
423 |
|
|
!(flags & CO_REVERT)) |
424 |
|
|
(void)fprintf(stderr, "\n"); |
425 |
|
|
} |
426 |
|
|
|
427 |
|
|
if (flags & CO_LOCK) { |
428 |
|
|
if (rcs_errno != RCS_ERR_DUPENT) |
429 |
|
|
lcount++; |
430 |
|
|
if (!(flags & QUIET) && lcount > 1 && !(flags & CO_REVERT)) |
431 |
|
|
warnx("%s: warning: You now have %d locks.", |
432 |
|
|
file->rf_path, lcount); |
433 |
|
|
} |
434 |
|
|
|
435 |
|
|
if ((flags & (PIPEOUT|FORCE)) == 0 && stat(dst, &st) != -1) { |
436 |
|
|
/* |
437 |
|
|
* Prompt the user if the file is writable or the file is |
438 |
|
|
* not writable but is different from the RCS head version. |
439 |
|
|
* This is different from GNU which will silently overwrite |
440 |
|
|
* the file regardless of its contents so long as it is |
441 |
|
|
* read-only. |
442 |
|
|
*/ |
443 |
|
|
if (st.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) |
444 |
|
|
fstatus = "writable"; |
445 |
|
|
else if (checkout_file_has_diffs(file, frev, dst) != D_SAME) |
446 |
|
|
fstatus = "modified"; |
447 |
|
|
else |
448 |
|
|
fstatus = NULL; |
449 |
|
|
if (fstatus) { |
450 |
|
|
(void)fprintf(stderr, "%s %s exists%s; ", fstatus, dst, |
451 |
|
|
(getuid() == st.st_uid) ? "" : |
452 |
|
|
", and you do not own it"); |
453 |
|
|
(void)fprintf(stderr, "remove it? [ny](n): "); |
454 |
|
|
if (rcs_yesno('n') == 'n') { |
455 |
|
|
if (!(flags & QUIET) && isatty(STDIN_FILENO)) |
456 |
|
|
warnx("%s %s exists; checkout aborted", |
457 |
|
|
fstatus, dst); |
458 |
|
|
else |
459 |
|
|
warnx("checkout aborted"); |
460 |
|
|
return (-1); |
461 |
|
|
} |
462 |
|
|
} |
463 |
|
|
} |
464 |
|
|
|
465 |
|
|
if (flags & PIPEOUT) |
466 |
|
|
buf_write_fd(bp, STDOUT_FILENO); |
467 |
|
|
else { |
468 |
|
|
(void)unlink(dst); |
469 |
|
|
|
470 |
|
|
if ((fd = open(dst, O_WRONLY|O_CREAT|O_TRUNC, mode)) < 0) |
471 |
|
|
err(1, "%s", dst); |
472 |
|
|
|
473 |
|
|
if (buf_write_fd(bp, fd) < 0) { |
474 |
|
|
warnx("failed to write revision to file"); |
475 |
|
|
buf_free(bp); |
476 |
|
|
(void)close(fd); |
477 |
|
|
return (-1); |
478 |
|
|
} |
479 |
|
|
|
480 |
|
|
if (fchmod(fd, mode) == -1) |
481 |
|
|
warn("%s", dst); |
482 |
|
|
|
483 |
|
|
if (flags & CO_REVDATE) { |
484 |
|
|
struct timeval tv[2]; |
485 |
|
|
memset(&tv, 0, sizeof(tv)); |
486 |
|
|
tv[0].tv_sec = rcs_rev_getdate(file, rev); |
487 |
|
|
tv[1].tv_sec = tv[0].tv_sec; |
488 |
|
|
if (futimes(fd, (const struct timeval *)&tv) < 0) |
489 |
|
|
warn("utimes"); |
490 |
|
|
} |
491 |
|
|
|
492 |
|
|
(void)close(fd); |
493 |
|
|
} |
494 |
|
|
|
495 |
|
|
buf_free(bp); |
496 |
|
|
|
497 |
|
|
return (0); |
498 |
|
|
} |
499 |
|
|
|
500 |
|
|
/* |
501 |
|
|
* checkout_err_nobranch() |
502 |
|
|
* |
503 |
|
|
* XXX - should handle the dates too. |
504 |
|
|
*/ |
505 |
|
|
static void |
506 |
|
|
checkout_err_nobranch(RCSFILE *file, const char *author, const char *date, |
507 |
|
|
const char *state, int flags) |
508 |
|
|
{ |
509 |
|
|
if (!(flags & CO_AUTHOR)) |
510 |
|
|
author = NULL; |
511 |
|
|
if (!(flags & CO_STATE)) |
512 |
|
|
state = NULL; |
513 |
|
|
|
514 |
|
|
warnx("%s: No revision on branch has %s%s%s%s%s%s%s%s.", |
515 |
|
|
file->rf_path, |
516 |
|
|
date ? "a date before " : "", |
517 |
|
|
date ? date : "", |
518 |
|
|
(date && author) ? " and " : "", |
519 |
|
|
author ? "author " : "", |
520 |
|
|
author ? author : "", |
521 |
|
|
((date || author) && state) ? " and " : "", |
522 |
|
|
state ? "state " : "", |
523 |
|
|
state ? state : ""); |
524 |
|
|
|
525 |
|
|
} |
526 |
|
|
|
527 |
|
|
/* |
528 |
|
|
* checkout_file_has_diffs() |
529 |
|
|
* |
530 |
|
|
* Check for diffs between the working file and its current revision. |
531 |
|
|
* Same return values as diffreg() |
532 |
|
|
*/ |
533 |
|
|
static int |
534 |
|
|
checkout_file_has_diffs(RCSFILE *rfp, RCSNUM *frev, const char *dst) |
535 |
|
|
{ |
536 |
|
|
char *tempfile; |
537 |
|
|
BUF *bp; |
538 |
|
|
int ret; |
539 |
|
|
|
540 |
|
|
tempfile = NULL; |
541 |
|
|
|
542 |
|
|
if ((bp = rcs_getrev(rfp, frev)) == NULL) { |
543 |
|
|
warnx("failed to load revision"); |
544 |
|
|
return (D_ERROR); |
545 |
|
|
} |
546 |
|
|
if ((bp = rcs_kwexp_buf(bp, rfp, frev)) == NULL) { |
547 |
|
|
warnx("failed to expand tags"); |
548 |
|
|
return (D_ERROR); |
549 |
|
|
} |
550 |
|
|
|
551 |
|
|
(void)xasprintf(&tempfile, "%s/diff.XXXXXXXXXX", rcs_tmpdir); |
552 |
|
|
buf_write_stmp(bp, tempfile); |
553 |
|
|
buf_empty(bp); |
554 |
|
|
|
555 |
|
|
diff_format = D_RCSDIFF; |
556 |
|
|
ret = diffreg(dst, tempfile, bp, D_FORCEASCII); |
557 |
|
|
|
558 |
|
|
buf_free(bp); |
559 |
|
|
unlink(tempfile); |
560 |
|
|
free(tempfile); |
561 |
|
|
|
562 |
|
|
return (ret); |
563 |
|
|
} |