GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/cvs/entries.c Lines: 0 276 0.0 %
Date: 2017-11-13 Branches: 0 175 0.0 %

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
	char *p, buf[PATH_MAX];
40
	struct cvs_ent *ent;
41
	struct cvs_ent_line *line;
42
43
	cvs_log(LP_TRACE, "cvs_ent_open(%s)", dir);
44
45
	(void)xsnprintf(buf, sizeof(buf), "%s/%s", dir, CVS_PATH_ENTRIES);
46
47
	if (current_list != NULL && !strcmp(current_list->cef_path, buf))
48
		return (current_list);
49
50
	if (current_list != NULL) {
51
		cvs_ent_close(current_list, ENT_SYNC);
52
		current_list = NULL;
53
	}
54
55
	ep = xcalloc(1, sizeof(*ep));
56
	ep->cef_path = xstrdup(buf);
57
58
	(void)xsnprintf(buf, sizeof(buf), "%s/%s",
59
	    dir, CVS_PATH_BACKUPENTRIES);
60
61
	ep->cef_bpath = xstrdup(buf);
62
63
	(void)xsnprintf(buf, sizeof(buf), "%s/%s", dir, CVS_PATH_LOGENTRIES);
64
65
	ep->cef_lpath = xstrdup(buf);
66
67
	TAILQ_INIT(&(ep->cef_ent));
68
69
	if ((fp = fopen(ep->cef_path, "r")) != NULL) {
70
		while (fgets(buf, sizeof(buf), fp)) {
71
			buf[strcspn(buf, "\n")] = '\0';
72
73
			if (buf[0] == 'D' && buf[1] == '\0')
74
				break;
75
76
			line = xmalloc(sizeof(*line));
77
			line->buf = xstrdup(buf);
78
			TAILQ_INSERT_TAIL(&(ep->cef_ent), line, entries_list);
79
		}
80
81
		(void)fclose(fp);
82
	}
83
84
	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
	current_list = ep;
116
	return (ep);
117
}
118
119
struct cvs_ent *
120
cvs_ent_parse(const char *entry)
121
{
122
	int i;
123
	struct tm t, dt;
124
	struct cvs_ent *ent;
125
	char *fields[CVS_ENTRIES_NFIELDS], *buf, *sp, *dp, *p;
126
127
	buf = sp = xstrdup(entry);
128
	i = 0;
129
	do {
130
		dp = strchr(sp, CVS_ENTRIES_DELIM);
131
		if (dp != NULL)
132
			*(dp++) = '\0';
133
		fields[i++] = sp;
134
		sp = dp;
135
	} while (dp != NULL && i < CVS_ENTRIES_NFIELDS);
136
137
	if (i < CVS_ENTRIES_NFIELDS)
138
		fatal("missing fields in entry line '%s'", entry);
139
140
	ent = xmalloc(sizeof(*ent));
141
	ent->ce_buf = buf;
142
143
	if (*fields[0] == '\0')
144
		ent->ce_type = CVS_ENT_FILE;
145
	else if (*fields[0] == 'D')
146
		ent->ce_type = CVS_ENT_DIR;
147
	else
148
		ent->ce_type = CVS_ENT_NONE;
149
150
	ent->ce_status = CVS_ENT_REG;
151
	ent->ce_name = fields[1];
152
	ent->ce_rev = NULL;
153
	ent->ce_date = -1;
154
	ent->ce_tag = NULL;
155
	ent->ce_time = NULL;
156
157
	if (ent->ce_type == CVS_ENT_FILE) {
158
		if (*fields[2] == '-') {
159
			ent->ce_status = CVS_ENT_REMOVED;
160
			sp = fields[2] + 1;
161
		} else if (*fields[2] == CVS_SERVER_QUESTIONABLE) {
162
			sp = NULL;
163
			ent->ce_status = CVS_ENT_UNKNOWN;
164
		} else {
165
			sp = fields[2];
166
			if (fields[2][0] == '0' && fields[2][1] == '\0')
167
				ent->ce_status = CVS_ENT_ADDED;
168
		}
169
170
		if (sp != NULL) {
171
			if ((ent->ce_rev = rcsnum_parse(sp)) == NULL) {
172
				fatal("failed to parse entry revision '%s'",
173
				    entry);
174
			}
175
		}
176
177
		if (fields[3][0] == '\0' ||
178
		    strncmp(fields[3], CVS_DATE_DUMMY,
179
		    sizeof(CVS_DATE_DUMMY) - 1) == 0 ||
180
		    strncmp(fields[3], "Initial ", 8) == 0 ||
181
		    strcmp(fields[3], "Result of merge") == 0) {
182
			ent->ce_mtime = CVS_DATE_DMSEC;
183
		} 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
			p = fields[3];
189
			if (strncmp(fields[3], "Result of merge+", 16) == 0)
190
				p += 16;
191
192
			ent->ce_time = xstrdup(p);
193
194
			/* Date field can be a '+=' with remote to indicate
195
			 * conflict.  In this case do nothing. */
196
			if (strptime(p, "%a %b %d %T %Y", &t) != NULL) {
197
				t.tm_isdst = -1;	/* Figure out DST. */
198
				t.tm_gmtoff = 0;
199
				ent->ce_mtime = mktime(&t);
200
				ent->ce_mtime += t.tm_gmtoff;
201
			}
202
		}
203
	}
204
205
	ent->ce_conflict = fields[3];
206
	if ((dp = strchr(ent->ce_conflict, '+')) != NULL)
207
		*dp = '\0';
208
	else
209
		ent->ce_conflict = NULL;
210
211
	if (strcmp(fields[4], ""))
212
		ent->ce_opts = fields[4];
213
	else
214
		ent->ce_opts = NULL;
215
216
	if (strcmp(fields[5], "")) {
217
		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
			ent->ce_tag = fields[5] + 1;
230
			break;
231
		default:
232
			fatal("invalid sticky entry");
233
		}
234
	}
235
236
	return (ent);
237
}
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
	l = ent_get_line(ep, name);
246
	if (l == NULL)
247
		return (NULL);
248
249
	ent = cvs_ent_parse(l->buf);
250
	return (ent);
251
}
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
	cvs_log(LP_TRACE, "cvs_ent_close(%s, %d)", ep->cef_bpath, writefile);
262
263
	if (cvs_cmdop == CVS_OP_EXPORT)
264
		writefile = 0;
265
266
	fp = NULL;
267
	if (writefile)
268
		fp = fopen(ep->cef_bpath, "w");
269
270
	while ((l = TAILQ_FIRST(&(ep->cef_ent))) != NULL) {
271
		if (fp != NULL) {
272
			if (l->buf[0] == 'D')
273
				dflag = 0;
274
275
			fputs(l->buf, fp);
276
			fputc('\n', fp);
277
		}
278
279
		TAILQ_REMOVE(&(ep->cef_ent), l, entries_list);
280
		free(l->buf);
281
		free(l);
282
	}
283
284
	if (fp != NULL) {
285
		if (dflag) {
286
			fputc('D', fp);
287
			fputc('\n', fp);
288
		}
289
		(void)fclose(fp);
290
291
		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
		(void)unlink(ep->cef_lpath);
296
	}
297
298
	free(ep->cef_path);
299
	free(ep->cef_bpath);
300
	free(ep->cef_lpath);
301
	free(ep);
302
}
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
	if ((ent = cvs_ent_parse(line)) == NULL)
312
		fatal("cvs_ent_add: parsing failed '%s'", line);
313
314
	l = ent_get_line(ep, ent->ce_name);
315
	if (l != NULL)
316
		cvs_ent_remove(ep, ent->ce_name);
317
318
	cvs_ent_free(ent);
319
320
	if (cvs_server_active == 0)
321
		cvs_log(LP_TRACE, "cvs_ent_add(%s, %s)", ep->cef_path, line);
322
323
	if ((fp = fopen(ep->cef_lpath, "a")) == NULL)
324
		fatal("cvs_ent_add: fopen: `%s': %s",
325
		    ep->cef_lpath, strerror(errno));
326
327
	fputs("A ", fp);
328
	fputs(line, fp);
329
	fputc('\n', fp);
330
331
	(void)fclose(fp);
332
333
	l = xmalloc(sizeof(*l));
334
	l->buf = xstrdup(line);
335
	TAILQ_INSERT_TAIL(&(ep->cef_ent), l, entries_list);
336
}
337
338
void
339
cvs_ent_remove(CVSENTRIES *ep, const char *name)
340
{
341
	FILE *fp;
342
	struct cvs_ent_line *l;
343
344
	if (cvs_server_active == 0)
345
		cvs_log(LP_TRACE, "cvs_ent_remove(%s, %s)", ep->cef_path, name);
346
347
	l = ent_get_line(ep, name);
348
	if (l == NULL)
349
		return;
350
351
	if ((fp = fopen(ep->cef_lpath, "a")) == NULL)
352
		fatal("cvs_ent_remove: fopen: `%s': %s", ep->cef_lpath,
353
		    strerror(errno));
354
355
	fputs("R ", fp);
356
	fputs(l->buf, fp);
357
	fputc('\n', fp);
358
359
	(void)fclose(fp);
360
361
	TAILQ_REMOVE(&(ep->cef_ent), l, entries_list);
362
	free(l->buf);
363
	free(l);
364
}
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
	if (isdir == 1) {
377
		(void)xsnprintf(buf, len, "D/%s////", name);
378
		return;
379
	}
380
381
	(void)xsnprintf(buf, len, "/%s/%s%s/%s/%s/%s",
382
	    name, isremoved == 1 ? "-" : "", rev, tstamp, opts, sticky);
383
}
384
385
void
386
cvs_ent_free(struct cvs_ent *ent)
387
{
388
	free(ent->ce_rev);
389
	free(ent->ce_time);
390
	free(ent->ce_buf);
391
	free(ent);
392
}
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
	TAILQ_FOREACH(l, &(ep->cef_ent), entries_list) {
401
		if (l->buf[0] == 'D')
402
			p = &(l->buf[2]);
403
		else
404
			p = &(l->buf[1]);
405
406
		if ((s = strchr(p, '/')) == NULL)
407
			fatal("ent_get_line: bad entry line '%s'", l->buf);
408
409
		*s = '\0';
410
411
		if (!strcmp(p, name)) {
412
			*s = '/';
413
			return (l);
414
		}
415
416
		*s = '/';
417
	}
418
419
	return (NULL);
420
}
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
	struct tm datetm;
429
	char linebuf[128], tagpath[PATH_MAX];
430
431
	cvs_directory_date = -1;
432
433
	if (tagp != NULL)
434
		*tagp = NULL;
435
436
	if (datep != NULL)
437
		*datep = NULL;
438
439
	if (nbp != NULL)
440
		*nbp = 0;
441
442
	i = snprintf(tagpath, PATH_MAX, "%s/%s", dir, CVS_PATH_TAG);
443
	if (i < 0 || i >= PATH_MAX)
444
		return;
445
446
	if ((fp = fopen(tagpath, "r")) == NULL) {
447
		if (errno != ENOENT)
448
			cvs_log(LP_NOTICE, "failed to open `%s' : %s", tagpath,
449
			    strerror(errno));
450
		return;
451
        }
452
453
	linenum = 0;
454
455
	while (fgets(linebuf, (int)sizeof(linebuf), fp) != NULL) {
456
		linenum++;
457
		if ((len = strlen(linebuf)) == 0)
458
			continue;
459
		if (linebuf[len - 1] != '\n') {
460
			cvs_log(LP_NOTICE, "line too long in `%s:%d'",
461
			    tagpath, linenum);
462
			break;
463
		}
464
		linebuf[--len] = '\0';
465
466
		switch (*linebuf) {
467
		case 'T':
468
			if (tagp != NULL)
469
				*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
			if (tagp != NULL)
487
				*tagp = xstrdup(linebuf + 1);
488
			if (nbp != NULL)
489
				*nbp = 1;
490
			break;
491
		default:
492
			break;
493
		}
494
	}
495
	if (ferror(fp))
496
		cvs_log(LP_NOTICE, "failed to read line from `%s'", tagpath);
497
498
	(void)fclose(fp);
499
}
500
501
void
502
cvs_write_tagfile(const char *dir, char *tag, char *date)
503
{
504
	FILE *fp;
505
	RCSNUM *rev;
506
	char tagpath[PATH_MAX];
507
	char sticky[CVS_REV_BUFSZ];
508
	struct tm datetm;
509
	int i;
510
511
	cvs_log(LP_TRACE, "cvs_write_tagfile(%s, %s, %s)", dir,
512
	    tag != NULL ? tag : "", date != NULL ? date : "");
513
514
	if (cvs_noexec == 1)
515
		return;
516
517
	i = snprintf(tagpath, PATH_MAX, "%s/%s", dir, CVS_PATH_TAG);
518
	if (i < 0 || i >= PATH_MAX)
519
		return;
520
521
	if (tag != NULL || cvs_specified_date != -1 ||
522
	    cvs_directory_date != -1) {
523
		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
		if (tag != NULL) {
532
			if ((rev = rcsnum_parse(tag)) != NULL) {
533
				(void)xsnprintf(sticky, sizeof(sticky),
534
				    "N%s", tag);
535
				free(rev);
536
			} else {
537
				(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
		(void)fprintf(fp, "%s\n", sticky);
550
		(void)fclose(fp);
551
	}
552
}