xref: /openbsd-src/usr.bin/cvs/entries.c (revision 53ce21770636b2a876ba8b08f34797235b43ecb2)
1*53ce2177Sfcambus /*	$OpenBSD: entries.c,v 1.107 2016/10/13 20:51:25 fcambus Exp $	*/
26c121f58Sjfb /*
33ad3fb45Sjoris  * Copyright (c) 2006 Joris Vink <joris@openbsd.org>
46c121f58Sjfb  *
53ad3fb45Sjoris  * Permission to use, copy, modify, and distribute this software for any
63ad3fb45Sjoris  * purpose with or without fee is hereby granted, provided that the above
73ad3fb45Sjoris  * copyright notice and this permission notice appear in all copies.
86c121f58Sjfb  *
93ad3fb45Sjoris  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
103ad3fb45Sjoris  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
113ad3fb45Sjoris  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
123ad3fb45Sjoris  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
133ad3fb45Sjoris  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
143ad3fb45Sjoris  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
153ad3fb45Sjoris  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
166c121f58Sjfb  */
176c121f58Sjfb 
181f8531bdSotto #include <errno.h>
19397ddb8aSnicm #include <stdlib.h>
201f8531bdSotto #include <string.h>
21169ade7eSchl #include <time.h>
221f8531bdSotto #include <unistd.h>
236c121f58Sjfb 
246c121f58Sjfb #include "cvs.h"
25a1742a04Sjoris #include "remote.h"
266c121f58Sjfb 
27691ee54dSjfb #define CVS_ENTRIES_NFIELDS	6
286c121f58Sjfb #define CVS_ENTRIES_DELIM	'/'
296c121f58Sjfb 
303ad3fb45Sjoris static struct cvs_ent_line *ent_get_line(CVSENTRIES *, const char *);
316c121f58Sjfb 
327c1a09a6Sjoris CVSENTRIES *current_list = NULL;
337c1a09a6Sjoris 
346c121f58Sjfb CVSENTRIES *
cvs_ent_open(const char * dir)353ad3fb45Sjoris cvs_ent_open(const char *dir)
366c121f58Sjfb {
376c121f58Sjfb 	FILE *fp;
386c121f58Sjfb 	CVSENTRIES *ep;
39b9fc9a72Sderaadt 	char *p, buf[PATH_MAX];
403ad3fb45Sjoris 	struct cvs_ent *ent;
413ad3fb45Sjoris 	struct cvs_ent_line *line;
426c121f58Sjfb 
437c1a09a6Sjoris 	cvs_log(LP_TRACE, "cvs_ent_open(%s)", dir);
4408bd75e9Sjfb 
45e40de241Sxsa 	(void)xsnprintf(buf, sizeof(buf), "%s/%s", dir, CVS_PATH_ENTRIES);
4617681b67Sxsa 
477c1a09a6Sjoris 	if (current_list != NULL && !strcmp(current_list->cef_path, buf))
487c1a09a6Sjoris 		return (current_list);
497c1a09a6Sjoris 
507c1a09a6Sjoris 	if (current_list != NULL) {
517c1a09a6Sjoris 		cvs_ent_close(current_list, ENT_SYNC);
527c1a09a6Sjoris 		current_list = NULL;
537c1a09a6Sjoris 	}
547c1a09a6Sjoris 
55cfff592fSderaadt 	ep = xcalloc(1, sizeof(*ep));
563ad3fb45Sjoris 	ep->cef_path = xstrdup(buf);
575885ecaeSxsa 
58e40de241Sxsa 	(void)xsnprintf(buf, sizeof(buf), "%s/%s",
59e40de241Sxsa 	    dir, CVS_PATH_BACKUPENTRIES);
6017681b67Sxsa 
613ad3fb45Sjoris 	ep->cef_bpath = xstrdup(buf);
6256fd4a10Sxsa 
63e40de241Sxsa 	(void)xsnprintf(buf, sizeof(buf), "%s/%s", dir, CVS_PATH_LOGENTRIES);
6417681b67Sxsa 
653ad3fb45Sjoris 	ep->cef_lpath = xstrdup(buf);
66a3864909Sjoris 
67b96e54a5Sjfb 	TAILQ_INIT(&(ep->cef_ent));
68858fe57bSjfb 
693ad3fb45Sjoris 	if ((fp = fopen(ep->cef_path, "r")) != NULL) {
703ad3fb45Sjoris 		while (fgets(buf, sizeof(buf), fp)) {
71b625fa02Sgilles 			buf[strcspn(buf, "\n")] = '\0';
723ad3fb45Sjoris 
733ad3fb45Sjoris 			if (buf[0] == 'D' && buf[1] == '\0')
741dace6d2Sjfb 				break;
756c121f58Sjfb 
76cfff592fSderaadt 			line = xmalloc(sizeof(*line));
773ad3fb45Sjoris 			line->buf = xstrdup(buf);
783ad3fb45Sjoris 			TAILQ_INSERT_TAIL(&(ep->cef_ent), line, entries_list);
796c121f58Sjfb 		}
80a3864909Sjoris 
816c121f58Sjfb 		(void)fclose(fp);
82a3864909Sjoris 	}
83a3864909Sjoris 
843ad3fb45Sjoris 	if ((fp = fopen(ep->cef_lpath, "r")) != NULL) {
853ad3fb45Sjoris 		while (fgets(buf, sizeof(buf), fp)) {
86b625fa02Sgilles 			buf[strcspn(buf, "\n")] = '\0';
87a3864909Sjoris 
88d2442498Stobias 			if (strlen(buf) < 2)
89d2442498Stobias 				fatal("cvs_ent_open: %s: malformed line %s",
90d2442498Stobias 				    ep->cef_lpath, buf);
91d2442498Stobias 
92d2442498Stobias 			p = &buf[2];
933ad3fb45Sjoris 
943ad3fb45Sjoris 			if (buf[0] == 'A') {
953ad3fb45Sjoris 				line = xmalloc(sizeof(*line));
963ad3fb45Sjoris 				line->buf = xstrdup(p);
973ad3fb45Sjoris 				TAILQ_INSERT_TAIL(&(ep->cef_ent), line,
983ad3fb45Sjoris 				    entries_list);
993ad3fb45Sjoris 			} else if (buf[0] == 'R') {
100a3864909Sjoris 				ent = cvs_ent_parse(p);
1013ad3fb45Sjoris 				line = ent_get_line(ep, ent->ce_name);
10202553c88Sjoris 				if (line != NULL) {
1033ad3fb45Sjoris 					TAILQ_REMOVE(&(ep->cef_ent), line,
1043ad3fb45Sjoris 					    entries_list);
105397ddb8aSnicm 					free(line->buf);
106397ddb8aSnicm 					free(line);
10702553c88Sjoris 				}
1083ad3fb45Sjoris 				cvs_ent_free(ent);
109a3864909Sjoris 			}
1103ad3fb45Sjoris 		}
111a3864909Sjoris 
1123ad3fb45Sjoris 		(void)fclose(fp);
113a3864909Sjoris 	}
11475059454Sjfb 
1157c1a09a6Sjoris 	current_list = ep;
1166c121f58Sjfb 	return (ep);
1176c121f58Sjfb }
1186c121f58Sjfb 
1196c121f58Sjfb struct cvs_ent *
cvs_ent_parse(const char * entry)1206c121f58Sjfb cvs_ent_parse(const char *entry)
1216c121f58Sjfb {
1226c121f58Sjfb 	int i;
1239159bf17Sjoris 	struct tm t, dt;
124d20177feSxsa 	struct cvs_ent *ent;
12509d28507Stobias 	char *fields[CVS_ENTRIES_NFIELDS], *buf, *sp, *dp, *p;
1266c121f58Sjfb 
12738ae64d1Sjoris 	buf = sp = xstrdup(entry);
1286c121f58Sjfb 	i = 0;
1296c121f58Sjfb 	do {
1306c121f58Sjfb 		dp = strchr(sp, CVS_ENTRIES_DELIM);
1316c121f58Sjfb 		if (dp != NULL)
1326c121f58Sjfb 			*(dp++) = '\0';
1336c121f58Sjfb 		fields[i++] = sp;
1346c121f58Sjfb 		sp = dp;
135d593696fSderaadt 	} while (dp != NULL && i < CVS_ENTRIES_NFIELDS);
1366c121f58Sjfb 
1373ad3fb45Sjoris 	if (i < CVS_ENTRIES_NFIELDS)
1383ad3fb45Sjoris 		fatal("missing fields in entry line '%s'", entry);
139691ee54dSjfb 
14038ae64d1Sjoris 	ent = xmalloc(sizeof(*ent));
141d20177feSxsa 	ent->ce_buf = buf;
142691ee54dSjfb 
143691ee54dSjfb 	if (*fields[0] == '\0')
144d20177feSxsa 		ent->ce_type = CVS_ENT_FILE;
145691ee54dSjfb 	else if (*fields[0] == 'D')
146d20177feSxsa 		ent->ce_type = CVS_ENT_DIR;
147691ee54dSjfb 	else
148d20177feSxsa 		ent->ce_type = CVS_ENT_NONE;
149691ee54dSjfb 
150d20177feSxsa 	ent->ce_status = CVS_ENT_REG;
151d20177feSxsa 	ent->ce_name = fields[1];
1523ad3fb45Sjoris 	ent->ce_rev = NULL;
1537f0c79fcSjoris 	ent->ce_date = -1;
1547f0c79fcSjoris 	ent->ce_tag = NULL;
155694b1da3Sstsp 	ent->ce_time = NULL;
1566c121f58Sjfb 
157d20177feSxsa 	if (ent->ce_type == CVS_ENT_FILE) {
1589a795badSjfb 		if (*fields[2] == '-') {
159d20177feSxsa 			ent->ce_status = CVS_ENT_REMOVED;
1609a795badSjfb 			sp = fields[2] + 1;
161bf2db54cSjoris 		} else if (*fields[2] == CVS_SERVER_QUESTIONABLE) {
162bf2db54cSjoris 			sp = NULL;
163bf2db54cSjoris 			ent->ce_status = CVS_ENT_UNKNOWN;
1649a795badSjfb 		} else {
1659a795badSjfb 			sp = fields[2];
166d593696fSderaadt 			if (fields[2][0] == '0' && fields[2][1] == '\0')
167d20177feSxsa 				ent->ce_status = CVS_ENT_ADDED;
1689a795badSjfb 		}
169a8778568Sjoris 
170bf2db54cSjoris 		if (sp != NULL) {
171bf2db54cSjoris 			if ((ent->ce_rev = rcsnum_parse(sp)) == NULL) {
172bf2db54cSjoris 				fatal("failed to parse entry revision '%s'",
173bf2db54cSjoris 				    entry);
174bf2db54cSjoris 			}
175bf2db54cSjoris 		}
1769a795badSjfb 
17738ae64d1Sjoris 		if (fields[3][0] == '\0' ||
178bf2db54cSjoris 		    strncmp(fields[3], CVS_DATE_DUMMY,
179bf2db54cSjoris 		    sizeof(CVS_DATE_DUMMY) - 1) == 0 ||
1803ad3fb45Sjoris 		    strncmp(fields[3], "Initial ", 8) == 0 ||
18109d28507Stobias 		    strcmp(fields[3], "Result of merge") == 0) {
182d20177feSxsa 			ent->ce_mtime = CVS_DATE_DMSEC;
183a1742a04Sjoris 		} else if (cvs_server_active == 1 &&
184a1742a04Sjoris 		    strncmp(fields[3], CVS_SERVER_UNCHANGED,
185a1742a04Sjoris 		    strlen(CVS_SERVER_UNCHANGED)) == 0) {
186a1742a04Sjoris 			ent->ce_mtime = CVS_SERVER_UPTODATE;
187a1742a04Sjoris 		} else {
18809d28507Stobias 			p = fields[3];
18909d28507Stobias 			if (strncmp(fields[3], "Result of merge+", 16) == 0)
19009d28507Stobias 				p += 16;
19109d28507Stobias 
192694b1da3Sstsp 			ent->ce_time = xstrdup(p);
193694b1da3Sstsp 
19420303d3dSniallo 			/* Date field can be a '+=' with remote to indicate
19520303d3dSniallo 			 * conflict.  In this case do nothing. */
19609d28507Stobias 			if (strptime(p, "%a %b %d %T %Y", &t) != NULL) {
197bf3ff189Sxsa 				t.tm_isdst = -1;	/* Figure out DST. */
19847e5fe63Sjoris 				t.tm_gmtoff = 0;
19947e5fe63Sjoris 				ent->ce_mtime = mktime(&t);
200597deb30Sotto 				ent->ce_mtime += t.tm_gmtoff;
20147e5fe63Sjoris 			}
20219624ea1Sjfb 		}
20320303d3dSniallo 	}
2049a795badSjfb 
2053ad3fb45Sjoris 	ent->ce_conflict = fields[3];
2063ad3fb45Sjoris 	if ((dp = strchr(ent->ce_conflict, '+')) != NULL)
2073ad3fb45Sjoris 		*dp = '\0';
2083ad3fb45Sjoris 	else
2093ad3fb45Sjoris 		ent->ce_conflict = NULL;
2103ad3fb45Sjoris 
2113ad3fb45Sjoris 	if (strcmp(fields[4], ""))
212d20177feSxsa 		ent->ce_opts = fields[4];
2133ad3fb45Sjoris 	else
2143ad3fb45Sjoris 		ent->ce_opts = NULL;
2153ad3fb45Sjoris 
2169159bf17Sjoris 	if (strcmp(fields[5], "")) {
2179159bf17Sjoris 		switch (*fields[5]) {
2189159bf17Sjoris 		case 'D':
2199159bf17Sjoris 			if (sscanf(fields[5] + 1, "%d.%d.%d.%d.%d.%d",
2209159bf17Sjoris 			    &dt.tm_year, &dt.tm_mon, &dt.tm_mday,
2219159bf17Sjoris 			    &dt.tm_hour, &dt.tm_min, &dt.tm_sec) != 6)
2229159bf17Sjoris 				fatal("wrong date specification");
2239159bf17Sjoris 			dt.tm_year -= 1900;
2249159bf17Sjoris 			dt.tm_mon -= 1;
2259159bf17Sjoris 			ent->ce_date = timegm(&dt);
2269159bf17Sjoris 			ent->ce_tag = NULL;
2279159bf17Sjoris 			break;
2289159bf17Sjoris 		case 'T':
229aa3964e7Sjoris 			ent->ce_tag = fields[5] + 1;
2309159bf17Sjoris 			break;
2319159bf17Sjoris 		default:
2329159bf17Sjoris 			fatal("invalid sticky entry");
2339159bf17Sjoris 		}
2347f0c79fcSjoris 	}
2353ad3fb45Sjoris 
236d20177feSxsa 	return (ent);
2376c121f58Sjfb }
238bd5748ebSjfb 
2393ad3fb45Sjoris struct cvs_ent *
cvs_ent_get(CVSENTRIES * ep,const char * name)2403ad3fb45Sjoris cvs_ent_get(CVSENTRIES *ep, const char *name)
2413ad3fb45Sjoris {
2423ad3fb45Sjoris 	struct cvs_ent *ent;
2433ad3fb45Sjoris 	struct cvs_ent_line *l;
2443ad3fb45Sjoris 
2453ad3fb45Sjoris 	l = ent_get_line(ep, name);
2463ad3fb45Sjoris 	if (l == NULL)
2473ad3fb45Sjoris 		return (NULL);
2483ad3fb45Sjoris 
2493ad3fb45Sjoris 	ent = cvs_ent_parse(l->buf);
2503ad3fb45Sjoris 	return (ent);
2513ad3fb45Sjoris }
2523ad3fb45Sjoris 
2533ad3fb45Sjoris void
cvs_ent_close(CVSENTRIES * ep,int writefile)2543ad3fb45Sjoris cvs_ent_close(CVSENTRIES *ep, int writefile)
2553ad3fb45Sjoris {
2563ad3fb45Sjoris 	FILE *fp;
2573ad3fb45Sjoris 	struct cvs_ent_line *l;
258d9d194c0Stobias 	int dflag;
259d9d194c0Stobias 
260d9d194c0Stobias 	dflag = 1;
2617c1a09a6Sjoris 	cvs_log(LP_TRACE, "cvs_ent_close(%s, %d)", ep->cef_bpath, writefile);
2623ad3fb45Sjoris 
2637c1a09a6Sjoris 	if (cvs_cmdop == CVS_OP_EXPORT)
2647c1a09a6Sjoris 		writefile = 0;
2657c1a09a6Sjoris 
2667c1a09a6Sjoris 	fp = NULL;
2677c1a09a6Sjoris 	if (writefile)
2687c1a09a6Sjoris 		fp = fopen(ep->cef_bpath, "w");
2693ad3fb45Sjoris 
2703ad3fb45Sjoris 	while ((l = TAILQ_FIRST(&(ep->cef_ent))) != NULL) {
2717c1a09a6Sjoris 		if (fp != NULL) {
272d9d194c0Stobias 			if (l->buf[0] == 'D')
273d9d194c0Stobias 				dflag = 0;
274d9d194c0Stobias 
2753ad3fb45Sjoris 			fputs(l->buf, fp);
2763ad3fb45Sjoris 			fputc('\n', fp);
2773ad3fb45Sjoris 		}
2783ad3fb45Sjoris 
2793ad3fb45Sjoris 		TAILQ_REMOVE(&(ep->cef_ent), l, entries_list);
280397ddb8aSnicm 		free(l->buf);
281397ddb8aSnicm 		free(l);
2823ad3fb45Sjoris 	}
2833ad3fb45Sjoris 
2847c1a09a6Sjoris 	if (fp != NULL) {
285d9d194c0Stobias 		if (dflag) {
2863ad3fb45Sjoris 			fputc('D', fp);
2870cdbab72Spyr 			fputc('\n', fp);
288d9d194c0Stobias 		}
2893ad3fb45Sjoris 		(void)fclose(fp);
2903ad3fb45Sjoris 
2913ad3fb45Sjoris 		if (rename(ep->cef_bpath, ep->cef_path) == -1)
2927d5f2f73Sxsa 			fatal("cvs_ent_close: rename: `%s'->`%s': %s",
2937d5f2f73Sxsa 			    ep->cef_bpath, ep->cef_path, strerror(errno));
2943ad3fb45Sjoris 
2953ad3fb45Sjoris 		(void)unlink(ep->cef_lpath);
2963ad3fb45Sjoris 	}
2973ad3fb45Sjoris 
298397ddb8aSnicm 	free(ep->cef_path);
299397ddb8aSnicm 	free(ep->cef_bpath);
300397ddb8aSnicm 	free(ep->cef_lpath);
301397ddb8aSnicm 	free(ep);
3023ad3fb45Sjoris }
3033ad3fb45Sjoris 
3043ad3fb45Sjoris void
cvs_ent_add(CVSENTRIES * ep,const char * line)3053ad3fb45Sjoris cvs_ent_add(CVSENTRIES *ep, const char *line)
3063ad3fb45Sjoris {
3073ad3fb45Sjoris 	FILE *fp;
3083ad3fb45Sjoris 	struct cvs_ent_line *l;
3093ad3fb45Sjoris 	struct cvs_ent *ent;
3103ad3fb45Sjoris 
3113ad3fb45Sjoris 	if ((ent = cvs_ent_parse(line)) == NULL)
3123ad3fb45Sjoris 		fatal("cvs_ent_add: parsing failed '%s'", line);
3133ad3fb45Sjoris 
3143ad3fb45Sjoris 	l = ent_get_line(ep, ent->ce_name);
3153ad3fb45Sjoris 	if (l != NULL)
3163ad3fb45Sjoris 		cvs_ent_remove(ep, ent->ce_name);
3173ad3fb45Sjoris 
3183ad3fb45Sjoris 	cvs_ent_free(ent);
3193ad3fb45Sjoris 
320d0a063c4Sjoris 	if (cvs_server_active == 0)
3213ad3fb45Sjoris 		cvs_log(LP_TRACE, "cvs_ent_add(%s, %s)", ep->cef_path, line);
3223ad3fb45Sjoris 
3233ad3fb45Sjoris 	if ((fp = fopen(ep->cef_lpath, "a")) == NULL)
3247d5f2f73Sxsa 		fatal("cvs_ent_add: fopen: `%s': %s",
3257d5f2f73Sxsa 		    ep->cef_lpath, strerror(errno));
3263ad3fb45Sjoris 
327d2442498Stobias 	fputs("A ", fp);
3283ad3fb45Sjoris 	fputs(line, fp);
3293ad3fb45Sjoris 	fputc('\n', fp);
3303ad3fb45Sjoris 
3313ad3fb45Sjoris 	(void)fclose(fp);
3323ad3fb45Sjoris 
333cfff592fSderaadt 	l = xmalloc(sizeof(*l));
3343ad3fb45Sjoris 	l->buf = xstrdup(line);
3353ad3fb45Sjoris 	TAILQ_INSERT_TAIL(&(ep->cef_ent), l, entries_list);
3363ad3fb45Sjoris }
3373ad3fb45Sjoris 
3383ad3fb45Sjoris void
cvs_ent_remove(CVSENTRIES * ep,const char * name)3393ad3fb45Sjoris cvs_ent_remove(CVSENTRIES *ep, const char *name)
3403ad3fb45Sjoris {
3413ad3fb45Sjoris 	FILE *fp;
3423ad3fb45Sjoris 	struct cvs_ent_line *l;
3433ad3fb45Sjoris 
344d0a063c4Sjoris 	if (cvs_server_active == 0)
3453ad3fb45Sjoris 		cvs_log(LP_TRACE, "cvs_ent_remove(%s, %s)", ep->cef_path, name);
3463ad3fb45Sjoris 
3473ad3fb45Sjoris 	l = ent_get_line(ep, name);
3483ad3fb45Sjoris 	if (l == NULL)
3493ad3fb45Sjoris 		return;
3503ad3fb45Sjoris 
3513ad3fb45Sjoris 	if ((fp = fopen(ep->cef_lpath, "a")) == NULL)
3527d5f2f73Sxsa 		fatal("cvs_ent_remove: fopen: `%s': %s", ep->cef_lpath,
3537d5f2f73Sxsa 		    strerror(errno));
3543ad3fb45Sjoris 
355d2442498Stobias 	fputs("R ", fp);
3563ad3fb45Sjoris 	fputs(l->buf, fp);
3573ad3fb45Sjoris 	fputc('\n', fp);
3583ad3fb45Sjoris 
3593ad3fb45Sjoris 	(void)fclose(fp);
3603ad3fb45Sjoris 
3613ad3fb45Sjoris 	TAILQ_REMOVE(&(ep->cef_ent), l, entries_list);
362397ddb8aSnicm 	free(l->buf);
363397ddb8aSnicm 	free(l);
3643ad3fb45Sjoris }
3653ad3fb45Sjoris 
366ae83823aSxsa /*
367ae83823aSxsa  * cvs_ent_line_str()
368ae83823aSxsa  *
369ae83823aSxsa  * Build CVS/Entries line.
370ae83823aSxsa  *
371ae83823aSxsa  */
372ae83823aSxsa void
cvs_ent_line_str(const char * name,char * rev,char * tstamp,char * opts,char * sticky,int isdir,int isremoved,char * buf,size_t len)373ae83823aSxsa cvs_ent_line_str(const char *name, char *rev, char *tstamp, char *opts,
374ae83823aSxsa     char *sticky, int isdir, int isremoved, char *buf, size_t len)
375ae83823aSxsa {
376ae83823aSxsa 	if (isdir == 1) {
377ae83823aSxsa 		(void)xsnprintf(buf, len, "D/%s////", name);
378ae83823aSxsa 		return;
379ae83823aSxsa 	}
380ae83823aSxsa 
381ae83823aSxsa 	(void)xsnprintf(buf, len, "/%s/%s%s/%s/%s/%s",
382ae83823aSxsa 	    name, isremoved == 1 ? "-" : "", rev, tstamp, opts, sticky);
383ae83823aSxsa }
384ae83823aSxsa 
385bd5748ebSjfb void
cvs_ent_free(struct cvs_ent * ent)386bd5748ebSjfb cvs_ent_free(struct cvs_ent *ent)
387bd5748ebSjfb {
388*53ce2177Sfcambus 	free(ent->ce_rev);
389397ddb8aSnicm 	free(ent->ce_time);
390397ddb8aSnicm 	free(ent->ce_buf);
391397ddb8aSnicm 	free(ent);
392bd5748ebSjfb }
393bd5748ebSjfb 
3943ad3fb45Sjoris static struct cvs_ent_line *
ent_get_line(CVSENTRIES * ep,const char * name)3953ad3fb45Sjoris ent_get_line(CVSENTRIES *ep, const char *name)
39608bd75e9Sjfb {
3973ad3fb45Sjoris 	char *p, *s;
3983ad3fb45Sjoris 	struct cvs_ent_line *l;
39908bd75e9Sjfb 
4003ad3fb45Sjoris 	TAILQ_FOREACH(l, &(ep->cef_ent), entries_list) {
4013ad3fb45Sjoris 		if (l->buf[0] == 'D')
4023ad3fb45Sjoris 			p = &(l->buf[2]);
403a8778568Sjoris 		else
4043ad3fb45Sjoris 			p = &(l->buf[1]);
4053ad3fb45Sjoris 
4063ad3fb45Sjoris 		if ((s = strchr(p, '/')) == NULL)
4073ad3fb45Sjoris 			fatal("ent_get_line: bad entry line '%s'", l->buf);
4083ad3fb45Sjoris 
4093ad3fb45Sjoris 		*s = '\0';
4103ad3fb45Sjoris 
4113ad3fb45Sjoris 		if (!strcmp(p, name)) {
4123ad3fb45Sjoris 			*s = '/';
4133ad3fb45Sjoris 			return (l);
414a8778568Sjoris 		}
415a8778568Sjoris 
4163ad3fb45Sjoris 		*s = '/';
41708bd75e9Sjfb 	}
41808bd75e9Sjfb 
4193ad3fb45Sjoris 	return (NULL);
42008bd75e9Sjfb }
421890ecb5aSxsa 
422890ecb5aSxsa void
cvs_parse_tagfile(char * dir,char ** tagp,char ** datep,int * nbp)423890ecb5aSxsa cvs_parse_tagfile(char *dir, char **tagp, char **datep, int *nbp)
424890ecb5aSxsa {
425890ecb5aSxsa 	FILE *fp;
426e40de241Sxsa 	int i, linenum;
427890ecb5aSxsa 	size_t len;
4289159bf17Sjoris 	struct tm datetm;
429b9fc9a72Sderaadt 	char linebuf[128], tagpath[PATH_MAX];
430890ecb5aSxsa 
431b87788a5Stobias 	cvs_directory_date = -1;
432b87788a5Stobias 
433890ecb5aSxsa 	if (tagp != NULL)
4343bd1ab07Sjoris 		*tagp = NULL;
435890ecb5aSxsa 
436890ecb5aSxsa 	if (datep != NULL)
4373bd1ab07Sjoris 		*datep = NULL;
438890ecb5aSxsa 
439890ecb5aSxsa 	if (nbp != NULL)
440890ecb5aSxsa 		*nbp = 0;
441890ecb5aSxsa 
442b9fc9a72Sderaadt 	i = snprintf(tagpath, PATH_MAX, "%s/%s", dir, CVS_PATH_TAG);
443b9fc9a72Sderaadt 	if (i < 0 || i >= PATH_MAX)
444ba7b4b60Sotto 		return;
445890ecb5aSxsa 
446890ecb5aSxsa 	if ((fp = fopen(tagpath, "r")) == NULL) {
447890ecb5aSxsa 		if (errno != ENOENT)
448890ecb5aSxsa 			cvs_log(LP_NOTICE, "failed to open `%s' : %s", tagpath,
449890ecb5aSxsa 			    strerror(errno));
450ba7b4b60Sotto 		return;
451890ecb5aSxsa         }
452890ecb5aSxsa 
453890ecb5aSxsa 	linenum = 0;
454890ecb5aSxsa 
455890ecb5aSxsa 	while (fgets(linebuf, (int)sizeof(linebuf), fp) != NULL) {
456890ecb5aSxsa 		linenum++;
457890ecb5aSxsa 		if ((len = strlen(linebuf)) == 0)
458890ecb5aSxsa 			continue;
459890ecb5aSxsa 		if (linebuf[len - 1] != '\n') {
460890ecb5aSxsa 			cvs_log(LP_NOTICE, "line too long in `%s:%d'",
461890ecb5aSxsa 			    tagpath, linenum);
462890ecb5aSxsa 			break;
463890ecb5aSxsa 		}
464890ecb5aSxsa 		linebuf[--len] = '\0';
465890ecb5aSxsa 
466890ecb5aSxsa 		switch (*linebuf) {
467890ecb5aSxsa 		case 'T':
468890ecb5aSxsa 			if (tagp != NULL)
469890ecb5aSxsa 				*tagp = xstrdup(linebuf + 1);
470890ecb5aSxsa 			break;
471890ecb5aSxsa 		case 'D':
4729159bf17Sjoris 			if (sscanf(linebuf + 1, "%d.%d.%d.%d.%d.%d",
4739159bf17Sjoris 			    &datetm.tm_year, &datetm.tm_mon, &datetm.tm_mday,
4749159bf17Sjoris 			    &datetm.tm_hour, &datetm.tm_min, &datetm.tm_sec) !=
4759159bf17Sjoris 			    6)
4769159bf17Sjoris 				fatal("wrong date specification");
4779159bf17Sjoris 			datetm.tm_year -= 1900;
4789159bf17Sjoris 			datetm.tm_mon -= 1;
4799159bf17Sjoris 
480b87788a5Stobias 			cvs_directory_date = timegm(&datetm);
4819159bf17Sjoris 
482890ecb5aSxsa 			if (datep != NULL)
483890ecb5aSxsa 				*datep = xstrdup(linebuf + 1);
484890ecb5aSxsa 			break;
485890ecb5aSxsa 		case 'N':
486890ecb5aSxsa 			if (tagp != NULL)
487890ecb5aSxsa 				*tagp = xstrdup(linebuf + 1);
488890ecb5aSxsa 			if (nbp != NULL)
489890ecb5aSxsa 				*nbp = 1;
490890ecb5aSxsa 			break;
491890ecb5aSxsa 		default:
492890ecb5aSxsa 			break;
493890ecb5aSxsa 		}
494890ecb5aSxsa 	}
495890ecb5aSxsa 	if (ferror(fp))
496890ecb5aSxsa 		cvs_log(LP_NOTICE, "failed to read line from `%s'", tagpath);
497890ecb5aSxsa 
498890ecb5aSxsa 	(void)fclose(fp);
499890ecb5aSxsa }
500890ecb5aSxsa 
501890ecb5aSxsa void
cvs_write_tagfile(const char * dir,char * tag,char * date)5022dec954eStobias cvs_write_tagfile(const char *dir, char *tag, char *date)
503890ecb5aSxsa {
504890ecb5aSxsa 	FILE *fp;
5052dec954eStobias 	RCSNUM *rev;
506b9fc9a72Sderaadt 	char tagpath[PATH_MAX];
50751ef6581Sjoris 	char sticky[CVS_REV_BUFSZ];
5086534056aStobias 	struct tm datetm;
509e40de241Sxsa 	int i;
510890ecb5aSxsa 
5112dec954eStobias 	cvs_log(LP_TRACE, "cvs_write_tagfile(%s, %s, %s)", dir,
5122dec954eStobias 	    tag != NULL ? tag : "", date != NULL ? date : "");
5132dec954eStobias 
514890ecb5aSxsa 	if (cvs_noexec == 1)
515890ecb5aSxsa 		return;
516890ecb5aSxsa 
517b9fc9a72Sderaadt 	i = snprintf(tagpath, PATH_MAX, "%s/%s", dir, CVS_PATH_TAG);
518b9fc9a72Sderaadt 	if (i < 0 || i >= PATH_MAX)
519ba7b4b60Sotto 		return;
520890ecb5aSxsa 
521b87788a5Stobias 	if (tag != NULL || cvs_specified_date != -1 ||
522d88ab732Sjoris 	    cvs_directory_date != -1) {
523890ecb5aSxsa 		if ((fp = fopen(tagpath, "w+")) == NULL) {
524890ecb5aSxsa 			if (errno != ENOENT) {
525890ecb5aSxsa 				cvs_log(LP_NOTICE, "failed to open `%s' : %s",
526890ecb5aSxsa 				    tagpath, strerror(errno));
527890ecb5aSxsa 			}
528ba7b4b60Sotto 			return;
529890ecb5aSxsa 		}
5305dd120b0Sjoris 
531890ecb5aSxsa 		if (tag != NULL) {
5322dec954eStobias 			if ((rev = rcsnum_parse(tag)) != NULL) {
53351ef6581Sjoris 				(void)xsnprintf(sticky, sizeof(sticky),
53451ef6581Sjoris 				    "N%s", tag);
535*53ce2177Sfcambus 				free(rev);
53651ef6581Sjoris 			} else {
53751ef6581Sjoris 				(void)xsnprintf(sticky, sizeof(sticky),
53851ef6581Sjoris 				    "T%s", tag);
53951ef6581Sjoris 			}
54051ef6581Sjoris 		} else {
541b87788a5Stobias 			if (cvs_specified_date != -1)
5426534056aStobias 				gmtime_r(&cvs_specified_date, &datetm);
543b87788a5Stobias 			else
544b87788a5Stobias 				gmtime_r(&cvs_directory_date, &datetm);
54525d9b074Sxsa 			(void)strftime(sticky, sizeof(sticky),
5466534056aStobias 			    "D"CVS_DATE_FMT, &datetm);
54751ef6581Sjoris 		}
548890ecb5aSxsa 
54951ef6581Sjoris 		(void)fprintf(fp, "%s\n", sticky);
550890ecb5aSxsa 		(void)fclose(fp);
551890ecb5aSxsa 	}
552890ecb5aSxsa }
553