GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/cvs/entries.c Lines: 221 294 75.2 %
Date: 2017-11-07 Branches: 110 175 62.9 %

Line Branch Exec Source
1
/*	$OpenBSD: entries.c,v 1.107 2016/10/13 20:51:25 fcambus Exp $	*/
2
/*
3
 * Copyright (c) 2006 Joris Vink <joris@openbsd.org>
4
 *
5
 * Permission to use, copy, modify, and distribute this software for any
6
 * purpose with or without fee is hereby granted, provided that the above
7
 * copyright notice and this permission notice appear in all copies.
8
 *
9
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
 */
17
18
#include <errno.h>
19
#include <stdlib.h>
20
#include <string.h>
21
#include <time.h>
22
#include <unistd.h>
23
24
#include "cvs.h"
25
#include "remote.h"
26
27
#define CVS_ENTRIES_NFIELDS	6
28
#define CVS_ENTRIES_DELIM	'/'
29
30
static struct cvs_ent_line *ent_get_line(CVSENTRIES *, const char *);
31
32
CVSENTRIES *current_list = NULL;
33
34
CVSENTRIES *
35
cvs_ent_open(const char *dir)
36
{
37
	FILE *fp;
38
	CVSENTRIES *ep;
39
388
	char *p, buf[PATH_MAX];
40
	struct cvs_ent *ent;
41
	struct cvs_ent_line *line;
42
43
194
	cvs_log(LP_TRACE, "cvs_ent_open(%s)", dir);
44
45
194
	(void)xsnprintf(buf, sizeof(buf), "%s/%s", dir, CVS_PATH_ENTRIES);
46
47

351
	if (current_list != NULL && !strcmp(current_list->cef_path, buf))
48
132
		return (current_list);
49
50
62
	if (current_list != NULL) {
51
25
		cvs_ent_close(current_list, ENT_SYNC);
52
25
		current_list = NULL;
53
25
	}
54
55
62
	ep = xcalloc(1, sizeof(*ep));
56
62
	ep->cef_path = xstrdup(buf);
57
58
62
	(void)xsnprintf(buf, sizeof(buf), "%s/%s",
59
	    dir, CVS_PATH_BACKUPENTRIES);
60
61
62
	ep->cef_bpath = xstrdup(buf);
62
63
62
	(void)xsnprintf(buf, sizeof(buf), "%s/%s", dir, CVS_PATH_LOGENTRIES);
64
65
62
	ep->cef_lpath = xstrdup(buf);
66
67
62
	TAILQ_INIT(&(ep->cef_ent));
68
69
62
	if ((fp = fopen(ep->cef_path, "r")) != NULL) {
70
245
		while (fgets(buf, sizeof(buf), fp)) {
71
128
			buf[strcspn(buf, "\n")] = '\0';
72
73

187
			if (buf[0] == 'D' && buf[1] == '\0')
74
				break;
75
76
93
			line = xmalloc(sizeof(*line));
77
93
			line->buf = xstrdup(buf);
78
93
			TAILQ_INSERT_TAIL(&(ep->cef_ent), line, entries_list);
79
		}
80
81
59
		(void)fclose(fp);
82
59
	}
83
84
62
	if ((fp = fopen(ep->cef_lpath, "r")) != NULL) {
85
		while (fgets(buf, sizeof(buf), fp)) {
86
			buf[strcspn(buf, "\n")] = '\0';
87
88
			if (strlen(buf) < 2)
89
				fatal("cvs_ent_open: %s: malformed line %s",
90
				    ep->cef_lpath, buf);
91
92
			p = &buf[2];
93
94
			if (buf[0] == 'A') {
95
				line = xmalloc(sizeof(*line));
96
				line->buf = xstrdup(p);
97
				TAILQ_INSERT_TAIL(&(ep->cef_ent), line,
98
				    entries_list);
99
			} else if (buf[0] == 'R') {
100
				ent = cvs_ent_parse(p);
101
				line = ent_get_line(ep, ent->ce_name);
102
				if (line != NULL) {
103
					TAILQ_REMOVE(&(ep->cef_ent), line,
104
					    entries_list);
105
					free(line->buf);
106
					free(line);
107
				}
108
				cvs_ent_free(ent);
109
			}
110
		}
111
112
		(void)fclose(fp);
113
	}
114
115
62
	current_list = ep;
116
62
	return (ep);
117
194
}
118
119
struct cvs_ent *
120
cvs_ent_parse(const char *entry)
121
{
122
	int i;
123
430
	struct tm t, dt;
124
	struct cvs_ent *ent;
125
215
	char *fields[CVS_ENTRIES_NFIELDS], *buf, *sp, *dp, *p;
126
127
215
	buf = sp = xstrdup(entry);
128
	i = 0;
129
215
	do {
130
1290
		dp = strchr(sp, CVS_ENTRIES_DELIM);
131
1290
		if (dp != NULL)
132
1075
			*(dp++) = '\0';
133
1290
		fields[i++] = sp;
134
		sp = dp;
135
1290
	} while (dp != NULL && i < CVS_ENTRIES_NFIELDS);
136
137
215
	if (i < CVS_ENTRIES_NFIELDS)
138
		fatal("missing fields in entry line '%s'", entry);
139
140
215
	ent = xmalloc(sizeof(*ent));
141
215
	ent->ce_buf = buf;
142
143
215
	if (*fields[0] == '\0')
144
177
		ent->ce_type = CVS_ENT_FILE;
145
38
	else if (*fields[0] == 'D')
146
		ent->ce_type = CVS_ENT_DIR;
147
	else
148
		ent->ce_type = CVS_ENT_NONE;
149
150
215
	ent->ce_status = CVS_ENT_REG;
151
215
	ent->ce_name = fields[1];
152
215
	ent->ce_rev = NULL;
153
215
	ent->ce_date = -1;
154
215
	ent->ce_tag = NULL;
155
215
	ent->ce_time = NULL;
156
157
215
	if (ent->ce_type == CVS_ENT_FILE) {
158
177
		if (*fields[2] == '-') {
159
5
			ent->ce_status = CVS_ENT_REMOVED;
160
5
			sp = fields[2] + 1;
161
177
		} else if (*fields[2] == CVS_SERVER_QUESTIONABLE) {
162
			sp = NULL;
163
			ent->ce_status = CVS_ENT_UNKNOWN;
164
		} else {
165
			sp = fields[2];
166

184
			if (fields[2][0] == '0' && fields[2][1] == '\0')
167
12
				ent->ce_status = CVS_ENT_ADDED;
168
		}
169
170
177
		if (sp != NULL) {
171
177
			if ((ent->ce_rev = rcsnum_parse(sp)) == NULL) {
172
				fatal("failed to parse entry revision '%s'",
173
				    entry);
174
			}
175
		}
176
177

342
		if (fields[3][0] == '\0' ||
178
177
		    strncmp(fields[3], CVS_DATE_DUMMY,
179
177
		    sizeof(CVS_DATE_DUMMY) - 1) == 0 ||
180
177
		    strncmp(fields[3], "Initial ", 8) == 0 ||
181
165
		    strcmp(fields[3], "Result of merge") == 0) {
182
12
			ent->ce_mtime = CVS_DATE_DMSEC;
183

177
		} else if (cvs_server_active == 1 &&
184
		    strncmp(fields[3], CVS_SERVER_UNCHANGED,
185
		    strlen(CVS_SERVER_UNCHANGED)) == 0) {
186
			ent->ce_mtime = CVS_SERVER_UPTODATE;
187
		} else {
188
165
			p = fields[3];
189
165
			if (strncmp(fields[3], "Result of merge+", 16) == 0)
190
				p += 16;
191
192
165
			ent->ce_time = xstrdup(p);
193
194
			/* Date field can be a '+=' with remote to indicate
195
			 * conflict.  In this case do nothing. */
196
165
			if (strptime(p, "%a %b %d %T %Y", &t) != NULL) {
197
165
				t.tm_isdst = -1;	/* Figure out DST. */
198
165
				t.tm_gmtoff = 0;
199
165
				ent->ce_mtime = mktime(&t);
200
165
				ent->ce_mtime += t.tm_gmtoff;
201
165
			}
202
		}
203
	}
204
205
215
	ent->ce_conflict = fields[3];
206
215
	if ((dp = strchr(ent->ce_conflict, '+')) != NULL)
207
		*dp = '\0';
208
	else
209
215
		ent->ce_conflict = NULL;
210
211
430
	if (strcmp(fields[4], ""))
212
215
		ent->ce_opts = fields[4];
213
	else
214
		ent->ce_opts = NULL;
215
216
215
	if (strcmp(fields[5], "")) {
217
60
		switch (*fields[5]) {
218
		case 'D':
219
			if (sscanf(fields[5] + 1, "%d.%d.%d.%d.%d.%d",
220
			    &dt.tm_year, &dt.tm_mon, &dt.tm_mday,
221
			    &dt.tm_hour, &dt.tm_min, &dt.tm_sec) != 6)
222
				fatal("wrong date specification");
223
			dt.tm_year -= 1900;
224
			dt.tm_mon -= 1;
225
			ent->ce_date = timegm(&dt);
226
			ent->ce_tag = NULL;
227
			break;
228
		case 'T':
229
60
			ent->ce_tag = fields[5] + 1;
230
60
			break;
231
		default:
232
			fatal("invalid sticky entry");
233
		}
234
60
	}
235
236
215
	return (ent);
237
215
}
238
239
struct cvs_ent *
240
cvs_ent_get(CVSENTRIES *ep, const char *name)
241
{
242
	struct cvs_ent *ent;
243
	struct cvs_ent_line *l;
244
245
210
	l = ent_get_line(ep, name);
246
105
	if (l == NULL)
247
36
		return (NULL);
248
249
69
	ent = cvs_ent_parse(l->buf);
250
69
	return (ent);
251
105
}
252
253
void
254
cvs_ent_close(CVSENTRIES *ep, int writefile)
255
{
256
	FILE *fp;
257
	struct cvs_ent_line *l;
258
	int dflag;
259
260
	dflag = 1;
261
124
	cvs_log(LP_TRACE, "cvs_ent_close(%s, %d)", ep->cef_bpath, writefile);
262
263
62
	if (cvs_cmdop == CVS_OP_EXPORT)
264
2
		writefile = 0;
265
266
	fp = NULL;
267
62
	if (writefile)
268
60
		fp = fopen(ep->cef_bpath, "w");
269
270
304
	while ((l = TAILQ_FIRST(&(ep->cef_ent))) != NULL) {
271
121
		if (fp != NULL) {
272
121
			if (l->buf[0] == 'D')
273
30
				dflag = 0;
274
275
121
			fputs(l->buf, fp);
276
121
			fputc('\n', fp);
277
121
		}
278
279
363
		TAILQ_REMOVE(&(ep->cef_ent), l, entries_list);
280
121
		free(l->buf);
281
121
		free(l);
282
	}
283
284
62
	if (fp != NULL) {
285
59
		if (dflag) {
286
29
			fputc('D', fp);
287
29
			fputc('\n', fp);
288
29
		}
289
59
		(void)fclose(fp);
290
291
59
		if (rename(ep->cef_bpath, ep->cef_path) == -1)
292
			fatal("cvs_ent_close: rename: `%s'->`%s': %s",
293
			    ep->cef_bpath, ep->cef_path, strerror(errno));
294
295
59
		(void)unlink(ep->cef_lpath);
296
59
	}
297
298
62
	free(ep->cef_path);
299
62
	free(ep->cef_bpath);
300
62
	free(ep->cef_lpath);
301
62
	free(ep);
302
62
}
303
304
void
305
cvs_ent_add(CVSENTRIES *ep, const char *line)
306
{
307
	FILE *fp;
308
	struct cvs_ent_line *l;
309
	struct cvs_ent *ent;
310
311
102
	if ((ent = cvs_ent_parse(line)) == NULL)
312
		fatal("cvs_ent_add: parsing failed '%s'", line);
313
314
51
	l = ent_get_line(ep, ent->ce_name);
315
51
	if (l != NULL)
316
23
		cvs_ent_remove(ep, ent->ce_name);
317
318
51
	cvs_ent_free(ent);
319
320
51
	if (cvs_server_active == 0)
321
51
		cvs_log(LP_TRACE, "cvs_ent_add(%s, %s)", ep->cef_path, line);
322
323
51
	if ((fp = fopen(ep->cef_lpath, "a")) == NULL)
324
		fatal("cvs_ent_add: fopen: `%s': %s",
325
		    ep->cef_lpath, strerror(errno));
326
327
51
	fputs("A ", fp);
328
51
	fputs(line, fp);
329
51
	fputc('\n', fp);
330
331
51
	(void)fclose(fp);
332
333
51
	l = xmalloc(sizeof(*l));
334
51
	l->buf = xstrdup(line);
335
51
	TAILQ_INSERT_TAIL(&(ep->cef_ent), l, entries_list);
336
51
}
337
338
void
339
cvs_ent_remove(CVSENTRIES *ep, const char *name)
340
{
341
	FILE *fp;
342
	struct cvs_ent_line *l;
343
344
46
	if (cvs_server_active == 0)
345
23
		cvs_log(LP_TRACE, "cvs_ent_remove(%s, %s)", ep->cef_path, name);
346
347
23
	l = ent_get_line(ep, name);
348
23
	if (l == NULL)
349
		return;
350
351
23
	if ((fp = fopen(ep->cef_lpath, "a")) == NULL)
352
		fatal("cvs_ent_remove: fopen: `%s': %s", ep->cef_lpath,
353
		    strerror(errno));
354
355
23
	fputs("R ", fp);
356
23
	fputs(l->buf, fp);
357
23
	fputc('\n', fp);
358
359
23
	(void)fclose(fp);
360
361
69
	TAILQ_REMOVE(&(ep->cef_ent), l, entries_list);
362
23
	free(l->buf);
363
23
	free(l);
364
46
}
365
366
/*
367
 * cvs_ent_line_str()
368
 *
369
 * Build CVS/Entries line.
370
 *
371
 */
372
void
373
cvs_ent_line_str(const char *name, char *rev, char *tstamp, char *opts,
374
    char *sticky, int isdir, int isremoved, char *buf, size_t len)
375
{
376
98
	if (isdir == 1) {
377
6
		(void)xsnprintf(buf, len, "D/%s////", name);
378
6
		return;
379
	}
380
381
43
	(void)xsnprintf(buf, len, "/%s/%s%s/%s/%s/%s",
382
43
	    name, isremoved == 1 ? "-" : "", rev, tstamp, opts, sticky);
383
92
}
384
385
void
386
cvs_ent_free(struct cvs_ent *ent)
387
{
388
394
	free(ent->ce_rev);
389
197
	free(ent->ce_time);
390
197
	free(ent->ce_buf);
391
197
	free(ent);
392
197
}
393
394
static struct cvs_ent_line *
395
ent_get_line(CVSENTRIES *ep, const char *name)
396
{
397
	char *p, *s;
398
	struct cvs_ent_line *l;
399
400
809
	TAILQ_FOREACH(l, &(ep->cef_ent), entries_list) {
401
251
		if (l->buf[0] == 'D')
402
63
			p = &(l->buf[2]);
403
		else
404
188
			p = &(l->buf[1]);
405
406
251
		if ((s = strchr(p, '/')) == NULL)
407
			fatal("ent_get_line: bad entry line '%s'", l->buf);
408
409
251
		*s = '\0';
410
411
251
		if (!strcmp(p, name)) {
412
			*s = '/';
413
115
			return (l);
414
		}
415
416
		*s = '/';
417
	}
418
419
64
	return (NULL);
420
179
}
421
422
void
423
cvs_parse_tagfile(char *dir, char **tagp, char **datep, int *nbp)
424
{
425
	FILE *fp;
426
	int i, linenum;
427
	size_t len;
428
116
	struct tm datetm;
429
58
	char linebuf[128], tagpath[PATH_MAX];
430
431
58
	cvs_directory_date = -1;
432
433
58
	if (tagp != NULL)
434
58
		*tagp = NULL;
435
436
58
	if (datep != NULL)
437
1
		*datep = NULL;
438
439
58
	if (nbp != NULL)
440
4
		*nbp = 0;
441
442
58
	i = snprintf(tagpath, PATH_MAX, "%s/%s", dir, CVS_PATH_TAG);
443
58
	if (i < 0 || i >= PATH_MAX)
444
		return;
445
446
58
	if ((fp = fopen(tagpath, "r")) == NULL) {
447
43
		if (errno != ENOENT)
448
			cvs_log(LP_NOTICE, "failed to open `%s' : %s", tagpath,
449
			    strerror(errno));
450
43
		return;
451
        }
452
453
	linenum = 0;
454
455
45
	while (fgets(linebuf, (int)sizeof(linebuf), fp) != NULL) {
456
15
		linenum++;
457
15
		if ((len = strlen(linebuf)) == 0)
458
			continue;
459
15
		if (linebuf[len - 1] != '\n') {
460
			cvs_log(LP_NOTICE, "line too long in `%s:%d'",
461
			    tagpath, linenum);
462
			break;
463
		}
464
35
		linebuf[--len] = '\0';
465
466

35
		switch (*linebuf) {
467
		case 'T':
468
9
			if (tagp != NULL)
469
9
				*tagp = xstrdup(linebuf + 1);
470
			break;
471
		case 'D':
472
			if (sscanf(linebuf + 1, "%d.%d.%d.%d.%d.%d",
473
			    &datetm.tm_year, &datetm.tm_mon, &datetm.tm_mday,
474
			    &datetm.tm_hour, &datetm.tm_min, &datetm.tm_sec) !=
475
			    6)
476
				fatal("wrong date specification");
477
			datetm.tm_year -= 1900;
478
			datetm.tm_mon -= 1;
479
480
			cvs_directory_date = timegm(&datetm);
481
482
			if (datep != NULL)
483
				*datep = xstrdup(linebuf + 1);
484
			break;
485
		case 'N':
486
6
			if (tagp != NULL)
487
6
				*tagp = xstrdup(linebuf + 1);
488
6
			if (nbp != NULL)
489
				*nbp = 1;
490
			break;
491
		default:
492
			break;
493
		}
494
	}
495

30
	if (ferror(fp))
496
		cvs_log(LP_NOTICE, "failed to read line from `%s'", tagpath);
497
498
15
	(void)fclose(fp);
499
73
}
500
501
void
502
cvs_write_tagfile(const char *dir, char *tag, char *date)
503
{
504
	FILE *fp;
505
	RCSNUM *rev;
506
58
	char tagpath[PATH_MAX];
507
29
	char sticky[CVS_REV_BUFSZ];
508
29
	struct tm datetm;
509
	int i;
510
511
29
	cvs_log(LP_TRACE, "cvs_write_tagfile(%s, %s, %s)", dir,
512
29
	    tag != NULL ? tag : "", date != NULL ? date : "");
513
514
29
	if (cvs_noexec == 1)
515
		return;
516
517
29
	i = snprintf(tagpath, PATH_MAX, "%s/%s", dir, CVS_PATH_TAG);
518
29
	if (i < 0 || i >= PATH_MAX)
519
		return;
520
521
58
	if (tag != NULL || cvs_specified_date != -1 ||
522
29
	    cvs_directory_date != -1) {
523
20
		if ((fp = fopen(tagpath, "w+")) == NULL) {
524
			if (errno != ENOENT) {
525
				cvs_log(LP_NOTICE, "failed to open `%s' : %s",
526
				    tagpath, strerror(errno));
527
			}
528
			return;
529
		}
530
531
20
		if (tag != NULL) {
532
20
			if ((rev = rcsnum_parse(tag)) != NULL) {
533
13
				(void)xsnprintf(sticky, sizeof(sticky),
534
				    "N%s", tag);
535
13
				free(rev);
536
13
			} else {
537
7
				(void)xsnprintf(sticky, sizeof(sticky),
538
				    "T%s", tag);
539
			}
540
		} else {
541
			if (cvs_specified_date != -1)
542
				gmtime_r(&cvs_specified_date, &datetm);
543
			else
544
				gmtime_r(&cvs_directory_date, &datetm);
545
			(void)strftime(sticky, sizeof(sticky),
546
			    "D"CVS_DATE_FMT, &datetm);
547
		}
548
549
20
		(void)fprintf(fp, "%s\n", sticky);
550
20
		(void)fclose(fp);
551
20
	}
552
58
}