GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* $OpenBSD: ci.c,v 1.224 2016/07/04 01:39:12 millert Exp $ */ |
||
2 |
/* |
||
3 |
* Copyright (c) 2005, 2006 Niall O'Higgins <niallo@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 |
|||
29 |
#include <ctype.h> |
||
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 CI_OPTSTRING "d::f::I::i::j::k::l::M::m::N:n:qr::s:Tt::u::Vw:x::z::" |
||
41 |
#define DATE_NOW -1 |
||
42 |
#define DATE_MTIME -2 |
||
43 |
|||
44 |
#define KW_ID "Id" |
||
45 |
#define KW_OPENBSD "OpenBSD" |
||
46 |
#define KW_AUTHOR "Author" |
||
47 |
#define KW_DATE "Date" |
||
48 |
#define KW_STATE "State" |
||
49 |
#define KW_REVISION "Revision" |
||
50 |
|||
51 |
#define KW_TYPE_ID 1 |
||
52 |
#define KW_TYPE_AUTHOR 2 |
||
53 |
#define KW_TYPE_DATE 3 |
||
54 |
#define KW_TYPE_STATE 4 |
||
55 |
#define KW_TYPE_REVISION 5 |
||
56 |
|||
57 |
/* Maximum number of tokens in a keyword. */ |
||
58 |
#define KW_NUMTOKS_MAX 10 |
||
59 |
|||
60 |
#define RCSNUM_ZERO_ENDING(x) (x->rn_id[x->rn_len - 1] == 0) |
||
61 |
|||
62 |
extern struct rcs_kw rcs_expkw[]; |
||
63 |
|||
64 |
static int workfile_fd; |
||
65 |
|||
66 |
struct checkin_params { |
||
67 |
int flags, openflags; |
||
68 |
mode_t fmode; |
||
69 |
time_t date; |
||
70 |
RCSFILE *file; |
||
71 |
RCSNUM *frev, *newrev; |
||
72 |
const char *description, *symbol; |
||
73 |
char fpath[PATH_MAX], *rcs_msg, *username, *filename; |
||
74 |
char *author, *state; |
||
75 |
BUF *deltatext; |
||
76 |
}; |
||
77 |
|||
78 |
static int checkin_attach_symbol(struct checkin_params *); |
||
79 |
static int checkin_checklock(struct checkin_params *); |
||
80 |
static BUF *checkin_diff_file(struct checkin_params *); |
||
81 |
static char *checkin_getlogmsg(RCSNUM *, RCSNUM *, int); |
||
82 |
static int checkin_init(struct checkin_params *); |
||
83 |
static int checkin_keywordscan(BUF *, RCSNUM **, time_t *, char **, |
||
84 |
char **); |
||
85 |
static int checkin_keywordtype(char *); |
||
86 |
static void checkin_mtimedate(struct checkin_params *); |
||
87 |
static void checkin_parsekeyword(char *, RCSNUM **, time_t *, char **, |
||
88 |
char **); |
||
89 |
static int checkin_update(struct checkin_params *); |
||
90 |
static int checkin_revert(struct checkin_params *); |
||
91 |
|||
92 |
__dead void |
||
93 |
checkin_usage(void) |
||
94 |
{ |
||
95 |
fprintf(stderr, |
||
96 |
"usage: ci [-qV] [-d[date]] [-f[rev]] [-I[rev]] [-i[rev]]\n" |
||
97 |
" [-j[rev]] [-k[rev]] [-l[rev]] [-M[rev]] [-mmsg]\n" |
||
98 |
" [-Nsymbol] [-nsymbol] [-r[rev]] [-sstate] [-t[str]]\n" |
||
99 |
" [-u[rev]] [-wusername] [-xsuffixes] [-ztz] file ...\n"); |
||
100 |
|||
101 |
exit(1); |
||
102 |
} |
||
103 |
|||
104 |
/* |
||
105 |
* checkin_main() |
||
106 |
* |
||
107 |
* Handler for the `ci' program. |
||
108 |
* Returns 0 on success, or >0 on error. |
||
109 |
*/ |
||
110 |
int |
||
111 |
checkin_main(int argc, char **argv) |
||
112 |
{ |
||
113 |
int fd; |
||
114 |
int i, ch, status; |
||
115 |
int base_flags, base_openflags; |
||
116 |
122 |
char *rev_str; |
|
117 |
61 |
struct checkin_params pb; |
|
118 |
|||
119 |
61 |
pb.date = DATE_NOW; |
|
120 |
61 |
pb.file = NULL; |
|
121 |
61 |
pb.rcs_msg = pb.username = pb.author = pb.state = NULL; |
|
122 |
61 |
pb.description = pb.symbol = NULL; |
|
123 |
61 |
pb.deltatext = NULL; |
|
124 |
61 |
pb.newrev = NULL; |
|
125 |
61 |
pb.fmode = S_IRUSR|S_IRGRP|S_IROTH; |
|
126 |
status = 0; |
||
127 |
base_flags = INTERACTIVE; |
||
128 |
base_openflags = RCS_RDWR|RCS_CREATE|RCS_PARSE_FULLY; |
||
129 |
61 |
rev_str = NULL; |
|
130 |
|||
131 |
✓✓ | 263 |
while ((ch = rcs_getopt(argc, argv, CI_OPTSTRING)) != -1) { |
132 |
✓✗✗✗ ✗✓✓✗ ✓✓✓✓ ✓✓✗✓ ✓✗✓✓ ✗✗ |
143 |
switch (ch) { |
133 |
case 'd': |
||
134 |
✗✓ | 4 |
if (rcs_optarg == NULL) |
135 |
pb.date = DATE_MTIME; |
||
136 |
✓✗ | 4 |
else if ((pb.date = date_parse(rcs_optarg)) == -1) |
137 |
errx(1, "invalid date"); |
||
138 |
break; |
||
139 |
case 'f': |
||
140 |
rcs_setrevstr(&rev_str, rcs_optarg); |
||
141 |
base_flags |= FORCE; |
||
142 |
break; |
||
143 |
case 'I': |
||
144 |
rcs_setrevstr(&rev_str, rcs_optarg); |
||
145 |
base_flags |= INTERACTIVE; |
||
146 |
break; |
||
147 |
case 'i': |
||
148 |
rcs_setrevstr(&rev_str, rcs_optarg); |
||
149 |
base_openflags |= RCS_CREATE; |
||
150 |
base_flags |= CI_INIT; |
||
151 |
break; |
||
152 |
case 'j': |
||
153 |
rcs_setrevstr(&rev_str, rcs_optarg); |
||
154 |
base_openflags &= ~RCS_CREATE; |
||
155 |
base_flags &= ~CI_INIT; |
||
156 |
break; |
||
157 |
case 'k': |
||
158 |
2 |
rcs_setrevstr(&rev_str, rcs_optarg); |
|
159 |
2 |
base_flags |= CI_KEYWORDSCAN; |
|
160 |
2 |
break; |
|
161 |
case 'l': |
||
162 |
28 |
rcs_setrevstr(&rev_str, rcs_optarg); |
|
163 |
28 |
base_flags |= CO_LOCK; |
|
164 |
28 |
break; |
|
165 |
case 'M': |
||
166 |
rcs_setrevstr(&rev_str, rcs_optarg); |
||
167 |
base_flags |= CO_REVDATE; |
||
168 |
break; |
||
169 |
case 'm': |
||
170 |
26 |
pb.rcs_msg = rcs_optarg; |
|
171 |
✗✓ | 26 |
if (pb.rcs_msg == NULL) |
172 |
errx(1, "missing message for -m option"); |
||
173 |
26 |
base_flags &= ~INTERACTIVE; |
|
174 |
26 |
break; |
|
175 |
case 'N': |
||
176 |
1 |
base_flags |= CI_SYMFORCE; |
|
177 |
/* FALLTHROUGH */ |
||
178 |
case 'n': |
||
179 |
9 |
pb.symbol = rcs_optarg; |
|
180 |
✓✗ | 9 |
if (rcs_sym_check(pb.symbol) != 1) |
181 |
errx(1, "invalid symbol `%s'", pb.symbol); |
||
182 |
break; |
||
183 |
case 'q': |
||
184 |
61 |
base_flags |= QUIET; |
|
185 |
61 |
break; |
|
186 |
case 'r': |
||
187 |
1 |
rcs_setrevstr(&rev_str, rcs_optarg); |
|
188 |
1 |
base_flags |= CI_DEFAULT; |
|
189 |
1 |
break; |
|
190 |
case 's': |
||
191 |
1 |
pb.state = rcs_optarg; |
|
192 |
✗✓ | 1 |
if (rcs_state_check(pb.state) < 0) |
193 |
errx(1, "invalid state `%s'", pb.state); |
||
194 |
break; |
||
195 |
case 'T': |
||
196 |
base_flags |= PRESERVETIME; |
||
197 |
break; |
||
198 |
case 't': |
||
199 |
/* Ignore bare -t; kept for backwards compatibility. */ |
||
200 |
✗✓ | 2 |
if (rcs_optarg == NULL) |
201 |
break; |
||
202 |
2 |
pb.description = rcs_optarg; |
|
203 |
2 |
base_flags |= DESCRIPTION; |
|
204 |
2 |
break; |
|
205 |
case 'u': |
||
206 |
2 |
rcs_setrevstr(&rev_str, rcs_optarg); |
|
207 |
2 |
base_flags |= CO_UNLOCK; |
|
208 |
2 |
break; |
|
209 |
case 'V': |
||
210 |
printf("%s\n", rcs_version); |
||
211 |
exit(0); |
||
212 |
case 'w': |
||
213 |
3 |
free(pb.author); |
|
214 |
3 |
pb.author = xstrdup(rcs_optarg); |
|
215 |
3 |
break; |
|
216 |
case 'x': |
||
217 |
/* Use blank extension if none given. */ |
||
218 |
3 |
rcs_suffixes = rcs_optarg ? rcs_optarg : ""; |
|
219 |
3 |
break; |
|
220 |
case 'z': |
||
221 |
timezone_flag = rcs_optarg; |
||
222 |
break; |
||
223 |
default: |
||
224 |
(usage)(); |
||
225 |
} |
||
226 |
} |
||
227 |
|||
228 |
60 |
argc -= rcs_optind; |
|
229 |
60 |
argv += rcs_optind; |
|
230 |
|||
231 |
✗✓ | 60 |
if (argc == 0) { |
232 |
warnx("no input file"); |
||
233 |
(usage)(); |
||
234 |
} |
||
235 |
|||
236 |
✗✓ | 60 |
if ((pb.username = getlogin()) == NULL) |
237 |
err(1, "getlogin"); |
||
238 |
|||
239 |
✓✓ | 236 |
for (i = 0; i < argc; i++) { |
240 |
/* |
||
241 |
* The pb.flags and pb.openflags may change during |
||
242 |
* loop iteration so restore them for each file. |
||
243 |
*/ |
||
244 |
61 |
pb.flags = base_flags; |
|
245 |
61 |
pb.openflags = base_openflags; |
|
246 |
|||
247 |
61 |
pb.filename = argv[i]; |
|
248 |
61 |
rcs_strip_suffix(pb.filename); |
|
249 |
|||
250 |
✓✓ | 61 |
if ((workfile_fd = open(pb.filename, O_RDONLY)) == -1) |
251 |
err(1, "%s", pb.filename); |
||
252 |
|||
253 |
/* Find RCS file path. */ |
||
254 |
60 |
fd = rcs_choosefile(pb.filename, pb.fpath, sizeof(pb.fpath)); |
|
255 |
|||
256 |
✓✓ | 60 |
if (fd < 0) { |
257 |
✓✗ | 30 |
if (pb.openflags & RCS_CREATE) |
258 |
30 |
pb.flags |= NEWFILE; |
|
259 |
else { |
||
260 |
/* XXX - Check if errno == ENOENT. */ |
||
261 |
warnx("No existing RCS file"); |
||
262 |
status = 1; |
||
263 |
(void)close(workfile_fd); |
||
264 |
continue; |
||
265 |
} |
||
266 |
30 |
} else { |
|
267 |
✗✓ | 30 |
if (pb.flags & CI_INIT) { |
268 |
warnx("%s already exists", pb.fpath); |
||
269 |
status = 1; |
||
270 |
(void)close(fd); |
||
271 |
(void)close(workfile_fd); |
||
272 |
continue; |
||
273 |
} |
||
274 |
30 |
pb.openflags &= ~RCS_CREATE; |
|
275 |
} |
||
276 |
|||
277 |
60 |
pb.file = rcs_open(pb.fpath, fd, pb.openflags, pb.fmode); |
|
278 |
✗✓ | 60 |
if (pb.file == NULL) |
279 |
errx(1, "failed to open rcsfile `%s'", pb.fpath); |
||
280 |
|||
281 |
✓✓✗✓ |
63 |
if ((pb.flags & DESCRIPTION) && |
282 |
3 |
rcs_set_description(pb.file, pb.description, pb.flags) == -1) |
|
283 |
err(1, "%s", pb.filename); |
||
284 |
|||
285 |
✗✓ | 60 |
if (!(pb.flags & QUIET)) |
286 |
(void)fprintf(stderr, |
||
287 |
"%s <-- %s\n", pb.fpath, pb.filename); |
||
288 |
|||
289 |
✓✓ | 60 |
if (rev_str != NULL) |
290 |
✗✓ | 1 |
if ((pb.newrev = rcs_getrevnum(rev_str, pb.file)) == |
291 |
NULL) |
||
292 |
errx(1, "invalid revision: %s", rev_str); |
||
293 |
|||
294 |
✓✓ | 60 |
if (!(pb.flags & NEWFILE)) |
295 |
30 |
pb.flags |= CI_SKIPDESC; |
|
296 |
|||
297 |
/* XXX - support for committing to a file without revisions */ |
||
298 |
✓✓ | 60 |
if (pb.file->rf_ndelta == 0) { |
299 |
31 |
pb.flags |= NEWFILE; |
|
300 |
31 |
pb.file->rf_flags |= RCS_CREATE; |
|
301 |
31 |
} |
|
302 |
|||
303 |
/* |
||
304 |
* workfile_fd will be closed in checkin_init or |
||
305 |
* checkin_update |
||
306 |
*/ |
||
307 |
✓✓ | 60 |
if (pb.flags & NEWFILE) { |
308 |
✗✓ | 31 |
if (checkin_init(&pb) == -1) |
309 |
status = 1; |
||
310 |
} else { |
||
311 |
✗✓ | 27 |
if (checkin_update(&pb) == -1) |
312 |
status = 1; |
||
313 |
} |
||
314 |
|||
315 |
58 |
rcs_close(pb.file); |
|
316 |
✓✓ | 58 |
if (rev_str != NULL) |
317 |
1 |
rcsnum_free(pb.newrev); |
|
318 |
58 |
pb.newrev = NULL; |
|
319 |
58 |
} |
|
320 |
|||
321 |
✗✓ | 57 |
if (!(base_flags & QUIET) && status == 0) |
322 |
(void)fprintf(stderr, "done\n"); |
||
323 |
|||
324 |
57 |
return (status); |
|
325 |
57 |
} |
|
326 |
|||
327 |
/* |
||
328 |
* checkin_diff_file() |
||
329 |
* |
||
330 |
* Generate the diff between the working file and a revision. |
||
331 |
* Returns pointer to a BUF on success, NULL on failure. |
||
332 |
*/ |
||
333 |
static BUF * |
||
334 |
checkin_diff_file(struct checkin_params *pb) |
||
335 |
{ |
||
336 |
54 |
char *path1, *path2; |
|
337 |
BUF *b1, *b2, *b3; |
||
338 |
|||
339 |
b1 = b2 = b3 = NULL; |
||
340 |
27 |
path1 = path2 = NULL; |
|
341 |
|||
342 |
✗✓ | 27 |
if ((b1 = buf_load(pb->filename)) == NULL) { |
343 |
warnx("failed to load file: `%s'", pb->filename); |
||
344 |
goto out; |
||
345 |
} |
||
346 |
|||
347 |
✗✓ | 27 |
if ((b2 = rcs_getrev(pb->file, pb->frev)) == NULL) { |
348 |
warnx("failed to load revision"); |
||
349 |
goto out; |
||
350 |
} |
||
351 |
27 |
b2 = rcs_kwexp_buf(b2, pb->file, pb->frev); |
|
352 |
27 |
b3 = buf_alloc(128); |
|
353 |
|||
354 |
27 |
(void)xasprintf(&path1, "%s/diff1.XXXXXXXXXX", rcs_tmpdir); |
|
355 |
27 |
buf_write_stmp(b1, path1); |
|
356 |
|||
357 |
27 |
buf_free(b1); |
|
358 |
b1 = NULL; |
||
359 |
|||
360 |
27 |
(void)xasprintf(&path2, "%s/diff2.XXXXXXXXXX", rcs_tmpdir); |
|
361 |
27 |
buf_write_stmp(b2, path2); |
|
362 |
|||
363 |
27 |
buf_free(b2); |
|
364 |
b2 = NULL; |
||
365 |
|||
366 |
27 |
diff_format = D_RCSDIFF; |
|
367 |
✓✗ | 27 |
if (diffreg(path1, path2, b3, D_FORCEASCII) == D_ERROR) |
368 |
goto out; |
||
369 |
|||
370 |
27 |
return (b3); |
|
371 |
out: |
||
372 |
buf_free(b1); |
||
373 |
buf_free(b2); |
||
374 |
buf_free(b3); |
||
375 |
free(path1); |
||
376 |
free(path2); |
||
377 |
|||
378 |
return (NULL); |
||
379 |
27 |
} |
|
380 |
|||
381 |
/* |
||
382 |
* checkin_getlogmsg() |
||
383 |
* |
||
384 |
* Get log message from user interactively. |
||
385 |
* Returns pointer to a char array on success, NULL on failure. |
||
386 |
*/ |
||
387 |
static char * |
||
388 |
checkin_getlogmsg(RCSNUM *rev, RCSNUM *rev2, int flags) |
||
389 |
{ |
||
390 |
16 |
char *rcs_msg, nrev[RCS_REV_BUFSZ], prev[RCS_REV_BUFSZ]; |
|
391 |
const char *prompt = |
||
392 |
"enter log message, terminated with a single '.' or end of file:\n"; |
||
393 |
RCSNUM *tmprev; |
||
394 |
|||
395 |
rcs_msg = NULL; |
||
396 |
8 |
tmprev = rcsnum_alloc(); |
|
397 |
8 |
rcsnum_cpy(rev, tmprev, 16); |
|
398 |
8 |
rcsnum_tostr(tmprev, prev, sizeof(prev)); |
|
399 |
✓✗ | 8 |
if (rev2 == NULL) |
400 |
8 |
rcsnum_tostr(rcsnum_inc(tmprev), nrev, sizeof(nrev)); |
|
401 |
else |
||
402 |
rcsnum_tostr(rev2, nrev, sizeof(nrev)); |
||
403 |
8 |
rcsnum_free(tmprev); |
|
404 |
|||
405 |
✗✓ | 8 |
if (!(flags & QUIET)) |
406 |
(void)fprintf(stderr, "new revision: %s; " |
||
407 |
"previous revision: %s\n", nrev, prev); |
||
408 |
|||
409 |
8 |
rcs_msg = rcs_prompt(prompt, flags); |
|
410 |
|||
411 |
8 |
return (rcs_msg); |
|
412 |
8 |
} |
|
413 |
|||
414 |
/* |
||
415 |
* checkin_update() |
||
416 |
* |
||
417 |
* Do a checkin to an existing RCS file. |
||
418 |
* |
||
419 |
* On success, return 0. On error return -1. |
||
420 |
*/ |
||
421 |
static int |
||
422 |
checkin_update(struct checkin_params *pb) |
||
423 |
{ |
||
424 |
58 |
char numb1[RCS_REV_BUFSZ], numb2[RCS_REV_BUFSZ]; |
|
425 |
29 |
struct stat st; |
|
426 |
BUF *bp; |
||
427 |
|||
428 |
/* |
||
429 |
* XXX this is wrong, we need to get the revision the user |
||
430 |
* has the lock for. So we can decide if we want to create a |
||
431 |
* branch or not. (if it's not current HEAD we need to branch). |
||
432 |
*/ |
||
433 |
29 |
pb->frev = pb->file->rf_head; |
|
434 |
|||
435 |
/* Load file contents */ |
||
436 |
✗✓ | 29 |
if ((bp = buf_load(pb->filename)) == NULL) |
437 |
return (-1); |
||
438 |
|||
439 |
/* If this is a zero-ending RCSNUM eg 4.0, increment it (eg to 4.1) */ |
||
440 |
✓✓✗✓ |
30 |
if (pb->newrev != NULL && RCSNUM_ZERO_ENDING(pb->newrev)) |
441 |
pb->newrev = rcsnum_inc(pb->newrev); |
||
442 |
|||
443 |
✗✓ | 29 |
if (checkin_checklock(pb) < 0) |
444 |
return (-1); |
||
445 |
|||
446 |
/* If revision passed on command line is less than HEAD, bail. |
||
447 |
* XXX only applies to ci -r1.2 foo for example if HEAD is > 1.2 and |
||
448 |
* there is no lock set for the user. |
||
449 |
*/ |
||
450 |
✓✓✗✓ |
30 |
if (pb->newrev != NULL && |
451 |
1 |
rcsnum_cmp(pb->newrev, pb->frev, 0) != -1) { |
|
452 |
warnx("%s: revision %s too low; must be higher than %s", |
||
453 |
pb->file->rf_path, |
||
454 |
rcsnum_tostr(pb->newrev, numb1, sizeof(numb1)), |
||
455 |
rcsnum_tostr(pb->frev, numb2, sizeof(numb2))); |
||
456 |
return (-1); |
||
457 |
} |
||
458 |
|||
459 |
/* |
||
460 |
* Set the date of the revision to be the last modification |
||
461 |
* time of the working file if -d has no argument. |
||
462 |
*/ |
||
463 |
✗✓ | 29 |
if (pb->date == DATE_MTIME) |
464 |
checkin_mtimedate(pb); |
||
465 |
|||
466 |
/* Date from argv/mtime must be more recent than HEAD */ |
||
467 |
✓✓ | 29 |
if (pb->date != DATE_NOW) { |
468 |
2 |
time_t head_date = rcs_rev_getdate(pb->file, pb->frev); |
|
469 |
✓✗ | 2 |
if (pb->date <= head_date) { |
470 |
static const char fmt[] = "%Y/%m/%d %H:%M:%S"; |
||
471 |
char dbuf1[256], dbuf2[256]; |
||
472 |
struct tm *t, *t_head; |
||
473 |
|||
474 |
t = gmtime(&pb->date); |
||
475 |
strftime(dbuf1, sizeof(dbuf1), fmt, t); |
||
476 |
t_head = gmtime(&head_date); |
||
477 |
strftime(dbuf2, sizeof(dbuf2), fmt, t_head); |
||
478 |
|||
479 |
errx(1, "%s: Date %s precedes %s in revision %s.", |
||
480 |
pb->file->rf_path, dbuf1, dbuf2, |
||
481 |
rcsnum_tostr(pb->frev, numb2, sizeof(numb2))); |
||
482 |
} |
||
483 |
} |
||
484 |
|||
485 |
/* Get RCS patch */ |
||
486 |
✗✓ | 27 |
if ((pb->deltatext = checkin_diff_file(pb)) == NULL) { |
487 |
warnx("failed to get diff"); |
||
488 |
return (-1); |
||
489 |
} |
||
490 |
|||
491 |
/* |
||
492 |
* If -f is not specified and there are no differences, tell |
||
493 |
* the user and revert to latest version. |
||
494 |
*/ |
||
495 |
✓✗✓✓ |
54 |
if (!(pb->flags & FORCE) && (buf_len(pb->deltatext) < 1)) { |
496 |
✗✓ | 3 |
if (checkin_revert(pb) == -1) |
497 |
return (-1); |
||
498 |
else |
||
499 |
3 |
return (0); |
|
500 |
} |
||
501 |
|||
502 |
/* If no log message specified, get it interactively. */ |
||
503 |
✓✓ | 24 |
if (pb->flags & INTERACTIVE) { |
504 |
✗✓ | 8 |
if (pb->rcs_msg != NULL) { |
505 |
fprintf(stderr, |
||
506 |
"reuse log message of previous file? [yn](y): "); |
||
507 |
if (rcs_yesno('y') != 'y') { |
||
508 |
free(pb->rcs_msg); |
||
509 |
pb->rcs_msg = NULL; |
||
510 |
} |
||
511 |
} |
||
512 |
✓✗ | 8 |
if (pb->rcs_msg == NULL) |
513 |
16 |
pb->rcs_msg = checkin_getlogmsg(pb->frev, pb->newrev, |
|
514 |
8 |
pb->flags); |
|
515 |
} |
||
516 |
|||
517 |
✗✓✗✗ |
24 |
if ((rcs_lock_remove(pb->file, pb->username, pb->frev) < 0) && |
518 |
(rcs_lock_getmode(pb->file) != RCS_LOCK_LOOSE)) { |
||
519 |
if (rcs_errno != RCS_ERR_NOENT) |
||
520 |
warnx("failed to remove lock"); |
||
521 |
else if (!(pb->flags & CO_LOCK)) |
||
522 |
warnx("previous revision was not locked; " |
||
523 |
"ignoring -l option"); |
||
524 |
} |
||
525 |
|||
526 |
/* Current head revision gets the RCS patch as rd_text */ |
||
527 |
✗✓ | 24 |
if (rcs_deltatext_set(pb->file, pb->frev, pb->deltatext) == -1) |
528 |
errx(1, "failed to set new rd_text for head rev"); |
||
529 |
|||
530 |
/* Now add our new revision */ |
||
531 |
✗✓ | 72 |
if (rcs_rev_add(pb->file, |
532 |
✓✓ | 49 |
(pb->newrev == NULL ? RCS_HEAD_REV : pb->newrev), |
533 |
48 |
pb->rcs_msg, pb->date, pb->author) != 0) { |
|
534 |
warnx("failed to add new revision"); |
||
535 |
return (-1); |
||
536 |
} |
||
537 |
|||
538 |
/* |
||
539 |
* If we are checking in to a non-default (ie user-specified) |
||
540 |
* revision, set head to this revision. |
||
541 |
*/ |
||
542 |
✓✓ | 24 |
if (pb->newrev != NULL) { |
543 |
✗✓ | 1 |
if (rcs_head_set(pb->file, pb->newrev) < 0) |
544 |
errx(1, "rcs_head_set failed"); |
||
545 |
} else |
||
546 |
23 |
pb->newrev = pb->file->rf_head; |
|
547 |
|||
548 |
/* New head revision has to contain entire file; */ |
||
549 |
✗✓ | 24 |
if (rcs_deltatext_set(pb->file, pb->frev, bp) == -1) |
550 |
errx(1, "failed to set new head revision"); |
||
551 |
|||
552 |
/* Attach a symbolic name to this revision if specified. */ |
||
553 |
✓✓✗✓ |
30 |
if (pb->symbol != NULL && |
554 |
6 |
(checkin_attach_symbol(pb) < 0)) |
|
555 |
return (-1); |
||
556 |
|||
557 |
/* Set the state of this revision if specified. */ |
||
558 |
✗✓ | 24 |
if (pb->state != NULL) |
559 |
(void)rcs_state_set(pb->file, pb->newrev, pb->state); |
||
560 |
|||
561 |
/* Maintain RCSFILE permissions */ |
||
562 |
✗✓ | 24 |
if (fstat(workfile_fd, &st) == -1) |
563 |
err(1, "%s", pb->filename); |
||
564 |
|||
565 |
/* Strip all the write bits */ |
||
566 |
24 |
pb->file->rf_mode = st.st_mode & ~(S_IWUSR|S_IWGRP|S_IWOTH); |
|
567 |
|||
568 |
24 |
(void)close(workfile_fd); |
|
569 |
24 |
(void)unlink(pb->filename); |
|
570 |
|||
571 |
/* Write out RCSFILE before calling checkout_rev() */ |
||
572 |
24 |
rcs_write(pb->file); |
|
573 |
|||
574 |
/* Do checkout if -u or -l are specified. */ |
||
575 |
✓✓✓✓ ✓✗ |
49 |
if (((pb->flags & CO_LOCK) || (pb->flags & CO_UNLOCK)) && |
576 |
13 |
!(pb->flags & CI_DEFAULT)) |
|
577 |
26 |
checkout_rev(pb->file, pb->newrev, pb->filename, pb->flags, |
|
578 |
13 |
pb->username, pb->author, NULL, NULL); |
|
579 |
|||
580 |
✓✓✓✓ |
32 |
if ((pb->flags & INTERACTIVE) && (pb->rcs_msg[0] == '\0')) { |
581 |
2 |
free(pb->rcs_msg); /* free empty log message */ |
|
582 |
2 |
pb->rcs_msg = NULL; |
|
583 |
2 |
} |
|
584 |
|||
585 |
24 |
return (0); |
|
586 |
27 |
} |
|
587 |
|||
588 |
/* |
||
589 |
* checkin_init() |
||
590 |
* |
||
591 |
* Does an initial check in, just enough to create the new ,v file |
||
592 |
* On success, return 0. On error return -1. |
||
593 |
*/ |
||
594 |
static int |
||
595 |
checkin_init(struct checkin_params *pb) |
||
596 |
{ |
||
597 |
BUF *bp; |
||
598 |
62 |
char numb[RCS_REV_BUFSZ]; |
|
599 |
int fetchlog = 0; |
||
600 |
31 |
struct stat st; |
|
601 |
|||
602 |
/* If this is a zero-ending RCSNUM eg 4.0, increment it (eg to 4.1) */ |
||
603 |
✗✓✗✗ |
31 |
if (pb->newrev != NULL && RCSNUM_ZERO_ENDING(pb->newrev)) { |
604 |
pb->frev = rcsnum_alloc(); |
||
605 |
rcsnum_cpy(pb->newrev, pb->frev, 0); |
||
606 |
pb->newrev = rcsnum_inc(pb->newrev); |
||
607 |
fetchlog = 1; |
||
608 |
} |
||
609 |
|||
610 |
/* Load file contents */ |
||
611 |
✗✓ | 31 |
if ((bp = buf_load(pb->filename)) == NULL) |
612 |
return (-1); |
||
613 |
|||
614 |
/* Get default values from working copy if -k specified */ |
||
615 |
✓✓ | 31 |
if (pb->flags & CI_KEYWORDSCAN) |
616 |
2 |
checkin_keywordscan(bp, &pb->newrev, |
|
617 |
2 |
&pb->date, &pb->state, &pb->author); |
|
618 |
|||
619 |
✓✓ | 31 |
if (pb->flags & CI_SKIPDESC) |
620 |
goto skipdesc; |
||
621 |
|||
622 |
/* Get description from user */ |
||
623 |
✓✓✗✓ |
58 |
if (pb->description == NULL && |
624 |
28 |
rcs_set_description(pb->file, NULL, pb->flags) == -1) { |
|
625 |
warn("%s", pb->filename); |
||
626 |
return (-1); |
||
627 |
} |
||
628 |
|||
629 |
skipdesc: |
||
630 |
|||
631 |
/* |
||
632 |
* If the user had specified a zero-ending revision number e.g. 4.0 |
||
633 |
* emulate odd GNU behaviour and fetch log message. |
||
634 |
*/ |
||
635 |
✗✓ | 31 |
if (fetchlog == 1) { |
636 |
pb->rcs_msg = checkin_getlogmsg(pb->frev, pb->newrev, |
||
637 |
pb->flags); |
||
638 |
rcsnum_free(pb->frev); |
||
639 |
} |
||
640 |
|||
641 |
/* |
||
642 |
* Set the date of the revision to be the last modification |
||
643 |
* time of the working file if -d has no argument. |
||
644 |
*/ |
||
645 |
✗✓ | 31 |
if (pb->date == DATE_MTIME) |
646 |
checkin_mtimedate(pb); |
||
647 |
|||
648 |
/* Now add our new revision */ |
||
649 |
✗✓ | 93 |
if (rcs_rev_add(pb->file, |
650 |
✗✓ | 62 |
(pb->newrev == NULL ? RCS_HEAD_REV : pb->newrev), |
651 |
✓✓ | 68 |
(pb->rcs_msg == NULL ? "Initial revision" : pb->rcs_msg), |
652 |
62 |
pb->date, pb->author) != 0) { |
|
653 |
warnx("failed to add new revision"); |
||
654 |
return (-1); |
||
655 |
} |
||
656 |
|||
657 |
/* |
||
658 |
* If we are checking in to a non-default (ie user-specified) |
||
659 |
* revision, set head to this revision. |
||
660 |
*/ |
||
661 |
✗✓ | 31 |
if (pb->newrev != NULL) { |
662 |
if (rcs_head_set(pb->file, pb->newrev) < 0) |
||
663 |
errx(1, "rcs_head_set failed"); |
||
664 |
} else |
||
665 |
31 |
pb->newrev = pb->file->rf_head; |
|
666 |
|||
667 |
/* New head revision has to contain entire file; */ |
||
668 |
✗✓ | 31 |
if (rcs_deltatext_set(pb->file, pb->file->rf_head, bp) == -1) { |
669 |
warnx("failed to set new head revision"); |
||
670 |
return (-1); |
||
671 |
} |
||
672 |
|||
673 |
/* Attach a symbolic name to this revision if specified. */ |
||
674 |
✓✓✗✓ |
34 |
if (pb->symbol != NULL && checkin_attach_symbol(pb) < 0) |
675 |
return (-1); |
||
676 |
|||
677 |
/* Set the state of this revision if specified. */ |
||
678 |
✗✓ | 31 |
if (pb->state != NULL) |
679 |
(void)rcs_state_set(pb->file, pb->newrev, pb->state); |
||
680 |
|||
681 |
/* Inherit RCSFILE permissions from file being checked in */ |
||
682 |
✗✓ | 31 |
if (fstat(workfile_fd, &st) == -1) |
683 |
err(1, "%s", pb->filename); |
||
684 |
|||
685 |
/* Strip all the write bits */ |
||
686 |
31 |
pb->file->rf_mode = st.st_mode & ~(S_IWUSR|S_IWGRP|S_IWOTH); |
|
687 |
|||
688 |
31 |
(void)close(workfile_fd); |
|
689 |
31 |
(void)unlink(pb->filename); |
|
690 |
|||
691 |
/* Write out RCSFILE before calling checkout_rev() */ |
||
692 |
31 |
rcs_write(pb->file); |
|
693 |
|||
694 |
/* Do checkout if -u or -l are specified. */ |
||
695 |
✓✓✓✓ ✓✗ |
63 |
if (((pb->flags & CO_LOCK) || (pb->flags & CO_UNLOCK)) && |
696 |
13 |
!(pb->flags & CI_DEFAULT)) { |
|
697 |
26 |
checkout_rev(pb->file, pb->newrev, pb->filename, pb->flags, |
|
698 |
13 |
pb->username, pb->author, NULL, NULL); |
|
699 |
13 |
} |
|
700 |
|||
701 |
✗✓ | 31 |
if (!(pb->flags & QUIET)) { |
702 |
fprintf(stderr, "initial revision: %s\n", |
||
703 |
rcsnum_tostr(pb->newrev, numb, sizeof(numb))); |
||
704 |
} |
||
705 |
|||
706 |
31 |
return (0); |
|
707 |
31 |
} |
|
708 |
|||
709 |
/* |
||
710 |
* checkin_attach_symbol() |
||
711 |
* |
||
712 |
* Attempt to attach the specified symbol to the revision. |
||
713 |
* On success, return 0. On error return -1. |
||
714 |
*/ |
||
715 |
static int |
||
716 |
checkin_attach_symbol(struct checkin_params *pb) |
||
717 |
{ |
||
718 |
18 |
char rbuf[RCS_REV_BUFSZ]; |
|
719 |
int ret; |
||
720 |
✗✓ | 9 |
if (!(pb->flags & QUIET)) |
721 |
printf("symbol: %s\n", pb->symbol); |
||
722 |
✓✓ | 9 |
if (pb->flags & CI_SYMFORCE) { |
723 |
✗✓ | 2 |
if (rcs_sym_remove(pb->file, pb->symbol) < 0) { |
724 |
1 |
if (rcs_errno != RCS_ERR_NOENT) { |
|
725 |
warnx("problem removing symbol: %s", |
||
726 |
pb->symbol); |
||
727 |
return (-1); |
||
728 |
} |
||
729 |
} |
||
730 |
} |
||
731 |
✗✓ | 18 |
if ((ret = rcs_sym_add(pb->file, pb->symbol, pb->newrev)) == -1 && |
732 |
9 |
(rcs_errno == RCS_ERR_DUPENT)) { |
|
733 |
rcsnum_tostr(rcs_sym_getrev(pb->file, pb->symbol), |
||
734 |
rbuf, sizeof(rbuf)); |
||
735 |
warnx("symbolic name %s already bound to %s", pb->symbol, rbuf); |
||
736 |
return (-1); |
||
737 |
✗✓ | 9 |
} else if (ret == -1) { |
738 |
warnx("problem adding symbol: %s", pb->symbol); |
||
739 |
return (-1); |
||
740 |
} |
||
741 |
9 |
return (0); |
|
742 |
9 |
} |
|
743 |
|||
744 |
/* |
||
745 |
* checkin_revert() |
||
746 |
* |
||
747 |
* If there are no differences between the working file and the latest revision |
||
748 |
* and the -f flag is not specified, simply revert to the latest version and |
||
749 |
* warn the user. |
||
750 |
* |
||
751 |
*/ |
||
752 |
static int |
||
753 |
checkin_revert(struct checkin_params *pb) |
||
754 |
{ |
||
755 |
6 |
char rbuf[RCS_REV_BUFSZ]; |
|
756 |
|||
757 |
3 |
rcsnum_tostr(pb->frev, rbuf, sizeof(rbuf)); |
|
758 |
|||
759 |
✗✓ | 3 |
if (!(pb->flags & QUIET)) |
760 |
(void)fprintf(stderr, "file is unchanged; reverting " |
||
761 |
"to previous revision %s\n", rbuf); |
||
762 |
|||
763 |
/* Attach a symbolic name to this revision if specified. */ |
||
764 |
✗✓ | 3 |
if (pb->symbol != NULL) { |
765 |
if (checkin_checklock(pb) == -1) |
||
766 |
return (-1); |
||
767 |
|||
768 |
pb->newrev = pb->frev; |
||
769 |
if (checkin_attach_symbol(pb) == -1) |
||
770 |
return (-1); |
||
771 |
} |
||
772 |
|||
773 |
3 |
pb->flags |= CO_REVERT; |
|
774 |
3 |
(void)close(workfile_fd); |
|
775 |
3 |
(void)unlink(pb->filename); |
|
776 |
|||
777 |
/* If needed, write out RCSFILE before calling checkout_rev() */ |
||
778 |
✗✓ | 3 |
if (pb->symbol != NULL) |
779 |
rcs_write(pb->file); |
||
780 |
|||
781 |
✗✓✗✗ |
3 |
if ((pb->flags & CO_LOCK) || (pb->flags & CO_UNLOCK)) |
782 |
6 |
checkout_rev(pb->file, pb->frev, pb->filename, |
|
783 |
3 |
pb->flags, pb->username, pb->author, NULL, NULL); |
|
784 |
|||
785 |
3 |
return (0); |
|
786 |
3 |
} |
|
787 |
|||
788 |
/* |
||
789 |
* checkin_checklock() |
||
790 |
* |
||
791 |
* Check for the existence of a lock on the file. If there are no locks, or it |
||
792 |
* is not locked by the correct user, return -1. Otherwise, return 0. |
||
793 |
*/ |
||
794 |
static int |
||
795 |
checkin_checklock(struct checkin_params *pb) |
||
796 |
{ |
||
797 |
struct rcs_lock *lkp; |
||
798 |
|||
799 |
✗✓ | 58 |
if (rcs_lock_getmode(pb->file) == RCS_LOCK_LOOSE) |
800 |
return (0); |
||
801 |
|||
802 |
✓✗ | 58 |
TAILQ_FOREACH(lkp, &(pb->file->rf_locks), rl_list) { |
803 |
✓✗✓✗ |
58 |
if (!strcmp(lkp->rl_name, pb->username) && |
804 |
29 |
!rcsnum_cmp(lkp->rl_num, pb->frev, 0)) |
|
805 |
29 |
return (0); |
|
806 |
} |
||
807 |
|||
808 |
warnx("%s: no lock set by %s", pb->file->rf_path, pb->username); |
||
809 |
return (-1); |
||
810 |
29 |
} |
|
811 |
|||
812 |
/* |
||
813 |
* checkin_mtimedate() |
||
814 |
* |
||
815 |
* Set the date of the revision to be the last modification |
||
816 |
* time of the working file. |
||
817 |
*/ |
||
818 |
static void |
||
819 |
checkin_mtimedate(struct checkin_params *pb) |
||
820 |
{ |
||
821 |
struct stat sb; |
||
822 |
|||
823 |
if (fstat(workfile_fd, &sb) == -1) |
||
824 |
err(1, "%s", pb->filename); |
||
825 |
|||
826 |
pb->date = sb.st_mtimespec.tv_sec; |
||
827 |
} |
||
828 |
|||
829 |
/* |
||
830 |
* checkin_keywordscan() |
||
831 |
* |
||
832 |
* Searches working file for keyword values to determine its revision |
||
833 |
* number, creation date and author, and uses these values instead of |
||
834 |
* calculating them locally. |
||
835 |
* |
||
836 |
* Params: The data buffer to scan and pointers to pointers of variables in |
||
837 |
* which to store the outputs. |
||
838 |
* |
||
839 |
* On success, return 0. On error return -1. |
||
840 |
*/ |
||
841 |
static int |
||
842 |
checkin_keywordscan(BUF *data, RCSNUM **rev, time_t *date, char **author, |
||
843 |
char **state) |
||
844 |
{ |
||
845 |
BUF *buf; |
||
846 |
size_t left; |
||
847 |
u_int j; |
||
848 |
char *kwstr; |
||
849 |
unsigned char *c, *end, *start; |
||
850 |
|||
851 |
4 |
end = buf_get(data) + buf_len(data) - 1; |
|
852 |
kwstr = NULL; |
||
853 |
|||
854 |
2 |
left = buf_len(data); |
|
855 |
✗✓ | 4 |
for (c = buf_get(data); |
856 |
✓✗ | 4 |
c <= end && (c = memchr(c, '$', left)) != NULL; |
857 |
left = end - c + 1) { |
||
858 |
size_t len; |
||
859 |
|||
860 |
start = c; |
||
861 |
c++; |
||
862 |
if (!isalpha(*c)) |
||
863 |
continue; |
||
864 |
|||
865 |
/* look for any matching keywords */ |
||
866 |
for (j = 0; j < 10; j++) { |
||
867 |
len = strlen(rcs_expkw[j].kw_str); |
||
868 |
if (left < len) |
||
869 |
continue; |
||
870 |
if (memcmp(c, rcs_expkw[j].kw_str, len) != 0) { |
||
871 |
kwstr = rcs_expkw[j].kw_str; |
||
872 |
break; |
||
873 |
} |
||
874 |
} |
||
875 |
|||
876 |
/* unknown keyword, continue looking */ |
||
877 |
if (kwstr == NULL) |
||
878 |
continue; |
||
879 |
|||
880 |
c += len; |
||
881 |
if (c > end) { |
||
882 |
kwstr = NULL; |
||
883 |
break; |
||
884 |
} |
||
885 |
if (*c != ':') { |
||
886 |
kwstr = NULL; |
||
887 |
continue; |
||
888 |
} |
||
889 |
|||
890 |
/* Find end of line or end of keyword. */ |
||
891 |
while (++c <= end) { |
||
892 |
if (*c == '\n') { |
||
893 |
/* Skip newline since it is definitely not `$'. */ |
||
894 |
++c; |
||
895 |
goto loopend; |
||
896 |
} |
||
897 |
if (*c == '$') |
||
898 |
break; |
||
899 |
} |
||
900 |
|||
901 |
len = c - start + 1; |
||
902 |
buf = buf_alloc(len + 1); |
||
903 |
buf_append(buf, start, len); |
||
904 |
|||
905 |
/* XXX - Not binary safe. */ |
||
906 |
buf_putc(buf, '\0'); |
||
907 |
checkin_parsekeyword(buf_get(buf), rev, date, author, state); |
||
908 |
buf_free(buf); |
||
909 |
loopend:; |
||
910 |
} |
||
911 |
✓✗ | 2 |
if (kwstr == NULL) |
912 |
2 |
return (-1); |
|
913 |
else |
||
914 |
return (0); |
||
915 |
2 |
} |
|
916 |
|||
917 |
/* |
||
918 |
* checkin_keywordtype() |
||
919 |
* |
||
920 |
* Given an RCS keyword string, determine what type of string it is. |
||
921 |
* This enables us to know what data should be in it. |
||
922 |
* |
||
923 |
* Returns type on success, or -1 on failure. |
||
924 |
*/ |
||
925 |
static int |
||
926 |
checkin_keywordtype(char *keystring) |
||
927 |
{ |
||
928 |
char *p; |
||
929 |
|||
930 |
p = keystring; |
||
931 |
p++; |
||
932 |
if (strncmp(p, KW_ID, strlen(KW_ID)) == 0 || |
||
933 |
strncmp(p, KW_OPENBSD, strlen(KW_OPENBSD)) == 0) |
||
934 |
return (KW_TYPE_ID); |
||
935 |
else if (strncmp(p, KW_AUTHOR, strlen(KW_AUTHOR)) == 0) |
||
936 |
return (KW_TYPE_AUTHOR); |
||
937 |
else if (strncmp(p, KW_DATE, strlen(KW_DATE)) == 0) |
||
938 |
return (KW_TYPE_DATE); |
||
939 |
else if (strncmp(p, KW_STATE, strlen(KW_STATE)) == 0) |
||
940 |
return (KW_TYPE_STATE); |
||
941 |
else if (strncmp(p, KW_REVISION, strlen(KW_REVISION)) == 0) |
||
942 |
return (KW_TYPE_REVISION); |
||
943 |
else |
||
944 |
return (-1); |
||
945 |
} |
||
946 |
|||
947 |
/* |
||
948 |
* checkin_parsekeyword() |
||
949 |
* |
||
950 |
* Do the actual parsing of an RCS keyword string, setting the values passed |
||
951 |
* to the function to whatever is found. |
||
952 |
* |
||
953 |
* XXX - Don't error out on malformed keywords. |
||
954 |
*/ |
||
955 |
static void |
||
956 |
checkin_parsekeyword(char *keystring, RCSNUM **rev, time_t *date, |
||
957 |
char **author, char **state) |
||
958 |
{ |
||
959 |
char *tokens[KW_NUMTOKS_MAX], *p, *datestring; |
||
960 |
int i = 0; |
||
961 |
|||
962 |
for ((p = strtok(keystring, " ")); p; (p = strtok(NULL, " "))) { |
||
963 |
if (i < KW_NUMTOKS_MAX - 1) |
||
964 |
tokens[i++] = p; |
||
965 |
else |
||
966 |
break; |
||
967 |
} |
||
968 |
|||
969 |
/* Parse data out of the expanded keyword */ |
||
970 |
switch (checkin_keywordtype(keystring)) { |
||
971 |
case KW_TYPE_ID: |
||
972 |
if (i < 3) |
||
973 |
break; |
||
974 |
/* only parse revision if one is not already set */ |
||
975 |
if (*rev == NULL) { |
||
976 |
if ((*rev = rcsnum_parse(tokens[2])) == NULL) |
||
977 |
errx(1, "could not parse rcsnum"); |
||
978 |
} |
||
979 |
|||
980 |
if (i < 5) |
||
981 |
break; |
||
982 |
(void)xasprintf(&datestring, "%s %s", tokens[3], tokens[4]); |
||
983 |
if ((*date = date_parse(datestring)) == -1) |
||
984 |
errx(1, "could not parse date"); |
||
985 |
free(datestring); |
||
986 |
|||
987 |
if (i < 6) |
||
988 |
break; |
||
989 |
free(*author); |
||
990 |
*author = xstrdup(tokens[5]); |
||
991 |
|||
992 |
if (i < 7) |
||
993 |
break; |
||
994 |
free(*state); |
||
995 |
*state = xstrdup(tokens[6]); |
||
996 |
break; |
||
997 |
case KW_TYPE_AUTHOR: |
||
998 |
if (i < 2) |
||
999 |
break; |
||
1000 |
free(*author); |
||
1001 |
*author = xstrdup(tokens[1]); |
||
1002 |
break; |
||
1003 |
case KW_TYPE_DATE: |
||
1004 |
if (i < 3) |
||
1005 |
break; |
||
1006 |
(void)xasprintf(&datestring, "%s %s", tokens[1], tokens[2]); |
||
1007 |
if ((*date = date_parse(datestring)) == -1) |
||
1008 |
errx(1, "could not parse date"); |
||
1009 |
free(datestring); |
||
1010 |
break; |
||
1011 |
case KW_TYPE_STATE: |
||
1012 |
if (i < 2) |
||
1013 |
break; |
||
1014 |
free(*state); |
||
1015 |
*state = xstrdup(tokens[1]); |
||
1016 |
break; |
||
1017 |
case KW_TYPE_REVISION: |
||
1018 |
if (i < 2) |
||
1019 |
break; |
||
1020 |
/* only parse revision if one is not already set */ |
||
1021 |
if (*rev != NULL) |
||
1022 |
break; |
||
1023 |
if ((*rev = rcsnum_parse(tokens[1])) == NULL) |
||
1024 |
errx(1, "could not parse rcsnum"); |
||
1025 |
break; |
||
1026 |
} |
||
1027 |
} |
Generated by: GCOVR (Version 3.3) |