GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
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 |
98 |
char fpath[PATH_MAX]; |
|
54 |
49 |
char *rev_str, *username; |
|
55 |
time_t rcs_mtime = -1; |
||
56 |
|||
57 |
flags = ret = 0; |
||
58 |
kflag = RCS_KWEXP_ERR; |
||
59 |
49 |
rev_str = NULL; |
|
60 |
author = date = state = NULL; |
||
61 |
|||
62 |
✓✓ | 191 |
while ((ch = rcs_getopt(argc, argv, CO_OPTSTRING)) != -1) { |
63 |
✗✗✗✗ ✓✗✗✓ ✗✗✗✓ ✗✗✗✗ ✗ |
93 |
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 |
✗✓ | 33 |
if (flags & CO_UNLOCK) { |
85 |
warnx("warning: -u overridden by -l"); |
||
86 |
flags &= ~CO_UNLOCK; |
||
87 |
} |
||
88 |
33 |
rcs_setrevstr(&rev_str, rcs_optarg); |
|
89 |
33 |
flags |= CO_LOCK; |
|
90 |
33 |
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 |
49 |
rcs_setrevstr(&rev_str, rcs_optarg); |
|
101 |
49 |
flags |= QUIET; |
|
102 |
49 |
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 |
11 |
rcs_setrevstr(&rev_str, rcs_optarg); |
|
115 |
✗✓ | 11 |
if (flags & CO_LOCK) { |
116 |
warnx("warning: -l overridden by -u"); |
||
117 |
flags &= ~CO_LOCK; |
||
118 |
} |
||
119 |
11 |
flags |= CO_UNLOCK; |
|
120 |
11 |
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 |
49 |
argc -= rcs_optind; |
|
146 |
49 |
argv += rcs_optind; |
|
147 |
|||
148 |
✗✓ | 49 |
if (argc == 0) { |
149 |
warnx("no input file"); |
||
150 |
(usage)(); |
||
151 |
} |
||
152 |
|||
153 |
✗✓ | 49 |
if ((username = getlogin()) == NULL) |
154 |
err(1, "getlogin"); |
||
155 |
|||
156 |
✓✓ | 194 |
for (i = 0; i < argc; i++) { |
157 |
49 |
fd = rcs_choosefile(argv[i], fpath, sizeof(fpath)); |
|
158 |
✗✓ | 49 |
if (fd < 0) { |
159 |
warn("%s", fpath); |
||
160 |
ret = 1; |
||
161 |
continue; |
||
162 |
} |
||
163 |
49 |
rcs_strip_suffix(argv[i]); |
|
164 |
|||
165 |
✗✓ | 49 |
if (!(flags & QUIET)) |
166 |
(void)fprintf(stderr, "%s --> %s\n", fpath, |
||
167 |
(flags & PIPEOUT) ? "standard output" : argv[i]); |
||
168 |
|||
169 |
✓✓✗✓ |
82 |
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 |
✓✗ | 96 |
if ((file = rcs_open(fpath, fd, |
176 |
48 |
RCS_RDWR|RCS_PARSE_FULLY)) == NULL) |
|
177 |
continue; |
||
178 |
|||
179 |
✗✓ | 48 |
if (flags & PRESERVETIME) |
180 |
rcs_mtime = rcs_get_mtime(file); |
||
181 |
|||
182 |
48 |
rcs_kwexp_set(file, kflag); |
|
183 |
|||
184 |
✗✓ | 48 |
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 |
✓✓ | 48 |
if (file->rf_ndelta == 0) { |
190 |
2 |
rev = rcsnum_parse("0.0"); |
|
191 |
✗✓ | 2 |
if (rev == NULL) |
192 |
errx(1, "failed to generate rev 0.0"); |
||
193 |
} else { |
||
194 |
46 |
rev = rcsnum_alloc(); |
|
195 |
46 |
rcsnum_cpy(file->rf_head, rev, 0); |
|
196 |
} |
||
197 |
} |
||
198 |
|||
199 |
✗✓ | 96 |
if (checkout_rev(file, rev, argv[i], flags, |
200 |
48 |
username, author, state, date) < 0) { |
|
201 |
rcs_close(file); |
||
202 |
rcsnum_free(rev); |
||
203 |
ret = 1; |
||
204 |
continue; |
||
205 |
} |
||
206 |
|||
207 |
✗✓ | 48 |
if (!(flags & QUIET)) |
208 |
(void)fprintf(stderr, "done\n"); |
||
209 |
|||
210 |
48 |
rcsnum_free(rev); |
|
211 |
|||
212 |
48 |
rcs_write(file); |
|
213 |
✗✓ | 48 |
if (flags & PRESERVETIME) |
214 |
rcs_set_mtime(file, rcs_mtime); |
||
215 |
48 |
rcs_close(file); |
|
216 |
48 |
} |
|
217 |
|||
218 |
48 |
return (ret); |
|
219 |
48 |
} |
|
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 |
154 |
char buf[RCS_REV_BUFSZ]; |
|
249 |
mode_t mode = DEFFILEMODE; |
||
250 |
77 |
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 |
✗✓✗✗ |
77 |
if (date != NULL && (givendate = date_parse(date)) == -1) { |
260 |
warnx("invalid date: %s", date); |
||
261 |
return -1; |
||
262 |
} |
||
263 |
|||
264 |
✓✓✗✓ |
79 |
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 |
✓✓ | 77 |
if (file->rf_ndelta != 0) { |
272 |
✓✓ | 450 |
for (i = 0; i < file->rf_head->rn_len; i++) { |
273 |
✗✓ | 150 |
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 |
✓✓ | 190 |
TAILQ_FOREACH(lkp, &(file->rf_locks), rl_list) { |
282 |
✓✗ | 18 |
if (!strcmp(lkp->rl_name, lockname)) |
283 |
18 |
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 |
✓✓✓✓ |
152 |
if (file->rf_ndelta != 0 && frev == file->rf_head) { |
294 |
✗✓ | 29 |
if (lcount > 1) { |
295 |
warnx("multiple revisions locked by %s; " |
||
296 |
"please specify one", lockname); |
||
297 |
return (-1); |
||
298 |
} |
||
299 |
|||
300 |
✓✗ | 58 |
TAILQ_FOREACH(rdp, &file->rf_delta, rd_list) { |
301 |
✗✓ | 29 |
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 |
✓✓✓✗ |
30 |
if (author != NULL && |
312 |
1 |
strcmp(rdp->rd_author, author)) |
|
313 |
continue; |
||
314 |
|||
315 |
✗✓✗✗ |
29 |
if (state != NULL && |
316 |
strcmp(rdp->rd_state, state)) |
||
317 |
continue; |
||
318 |
|||
319 |
29 |
frev = rdp->rd_num; |
|
320 |
29 |
break; |
|
321 |
} |
||
322 |
✓✓ | 48 |
} else if (file->rf_ndelta != 0) { |
323 |
46 |
rdp = rcs_findrev(file, frev); |
|
324 |
46 |
} |
|
325 |
|||
326 |
✗✓ | 77 |
if (file->rf_ndelta != 0 && rdp == NULL) { |
327 |
checkout_err_nobranch(file, author, date, state, flags); |
||
328 |
return (-1); |
||
329 |
} |
||
330 |
|||
331 |
✓✓ | 77 |
if (file->rf_ndelta == 0) |
332 |
2 |
rev = frev; |
|
333 |
else |
||
334 |
75 |
rev = rdp->rd_num; |
|
335 |
|||
336 |
77 |
rcsnum_tostr(rev, buf, sizeof(buf)); |
|
337 |
|||
338 |
✓✓✓✓ |
152 |
if (file->rf_ndelta != 0 && rdp->rd_locker != NULL) { |
339 |
✗✓ | 18 |
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 |
✗✓✗✗ ✗✗ |
77 |
if (!(flags & QUIET) && !(flags & NEWFILE) && |
348 |
!(flags & CO_REVERT) && file->rf_ndelta != 0) |
||
349 |
(void)fprintf(stderr, "revision %s", buf); |
||
350 |
|||
351 |
✓✓ | 77 |
if (file->rf_ndelta != 0) { |
352 |
✗✓ | 75 |
if ((bp = rcs_getrev(file, rev)) == NULL) { |
353 |
warnx("cannot find revision `%s'", buf); |
||
354 |
return (-1); |
||
355 |
} |
||
356 |
} else { |
||
357 |
2 |
bp = buf_alloc(1); |
|
358 |
} |
||
359 |
|||
360 |
/* |
||
361 |
* Do keyword expansion if required. |
||
362 |
*/ |
||
363 |
✓✓ | 77 |
if (file->rf_ndelta != 0) |
364 |
75 |
bp = rcs_kwexp_buf(bp, file, rev); |
|
365 |
/* |
||
366 |
* File inherits permissions from its ,v file |
||
367 |
*/ |
||
368 |
✓✓ | 77 |
if (file->rf_file != NULL) { |
369 |
✓✗✗✓ |
192 |
if (fstat(fileno(file->rf_file), &st) == -1) |
370 |
err(1, "%s", file->rf_path); |
||
371 |
64 |
file->rf_mode = mode = st.st_mode; |
|
372 |
64 |
} else { |
|
373 |
13 |
mode = file->rf_mode; |
|
374 |
} |
||
375 |
|||
376 |
✓✓ | 77 |
if (flags & CO_LOCK) { |
377 |
✓✓ | 120 |
if (file->rf_ndelta != 0) { |
378 |
✗✓ | 119 |
if (lockname != NULL && |
379 |
59 |
rcs_lock_add(file, lockname, rev) < 0) { |
|
380 |
59 |
if (rcs_errno != RCS_ERR_DUPENT) |
|
381 |
return (-1); |
||
382 |
} |
||
383 |
} |
||
384 |
|||
385 |
/* File should only be writable by owner. */ |
||
386 |
60 |
mode &= ~(S_IWGRP|S_IWOTH); |
|
387 |
60 |
mode |= S_IWUSR; |
|
388 |
|||
389 |
✓✓ | 60 |
if (file->rf_ndelta != 0) { |
390 |
✗✓✗✗ ✗✗ |
59 |
if (!(flags & QUIET) && !(flags & NEWFILE) && |
391 |
!(flags & CO_REVERT)) |
||
392 |
(void)fprintf(stderr, " (locked)"); |
||
393 |
} |
||
394 |
✓✓ | 17 |
} else if (flags & CO_UNLOCK) { |
395 |
✓✗ | 13 |
if (file->rf_ndelta != 0) { |
396 |
✗✓ | 26 |
if (rcs_lock_remove(file, lockname, rev) < 0) { |
397 |
13 |
if (rcs_errno != RCS_ERR_NOENT) |
|
398 |
return (-1); |
||
399 |
} |
||
400 |
} |
||
401 |
|||
402 |
/* Strip all write bits from mode */ |
||
403 |
13 |
mode &= ~(S_IWUSR|S_IWGRP|S_IWOTH); |
|
404 |
|||
405 |
✓✗ | 13 |
if (file->rf_ndelta != 0) { |
406 |
✗✓✗✗ ✗✗ |
13 |
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 |
✗✓ | 77 |
if (rcs_lock_getmode(file) == RCS_LOCK_LOOSE) |
414 |
mode |= S_IWUSR; |
||
415 |
|||
416 |
✓✓✗✓ ✗✗ |
79 |
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 |
✓✓ | 77 |
} else if (file->rf_ndelta != 0) { |
421 |
/* XXX - Not a good way to detect if a newline is needed. */ |
||
422 |
✗✓✗✗ ✗✗ |
75 |
if (!(flags & QUIET) && !(flags & NEWFILE) && |
423 |
!(flags & CO_REVERT)) |
||
424 |
(void)fprintf(stderr, "\n"); |
||
425 |
} |
||
426 |
|||
427 |
✓✓ | 77 |
if (flags & CO_LOCK) { |
428 |
✓✓ | 60 |
if (rcs_errno != RCS_ERR_DUPENT) |
429 |
41 |
lcount++; |
|
430 |
✗✓✗✗ |
60 |
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 |
✓✗✗✓ |
154 |
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 |
✗✓ | 77 |
if (flags & PIPEOUT) |
466 |
buf_write_fd(bp, STDOUT_FILENO); |
||
467 |
else { |
||
468 |
77 |
(void)unlink(dst); |
|
469 |
|||
470 |
✗✓ | 77 |
if ((fd = open(dst, O_WRONLY|O_CREAT|O_TRUNC, mode)) < 0) |
471 |
err(1, "%s", dst); |
||
472 |
|||
473 |
✗✓ | 77 |
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 |
✗✓ | 77 |
if (fchmod(fd, mode) == -1) |
481 |
warn("%s", dst); |
||
482 |
|||
483 |
✗✓ | 77 |
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 |
77 |
(void)close(fd); |
|
493 |
} |
||
494 |
|||
495 |
77 |
buf_free(bp); |
|
496 |
|||
497 |
77 |
return (0); |
|
498 |
77 |
} |
|
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 |
} |
Generated by: GCOVR (Version 3.3) |