[BACK]Return to entries.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / cvs

File: [local] / src / usr.bin / cvs / entries.c (download)

Revision 1.57, Sat May 27 03:30:30 2006 UTC (18 years ago) by joris
Branch: MAIN
Changes since 1.56: +230 -411 lines

commit the new opencvs code, i have been hacking on
this for the past 2 weeks now and it should go in at
the start of the hackathon so others can help out.

this code is a lot safer, smarter, faster and best of
all it is actually doing what it is suppose to do!

basic checkout, update, status, diff and commit are
working in local mode only.
there is no support for any remote setups now.

/*	$OpenBSD: entries.c,v 1.57 2006/05/27 03:30:30 joris Exp $	*/
/*
 * Copyright (c) 2006 Joris Vink <joris@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include "includes.h"

#include "cvs.h"
#include "log.h"

#define CVS_ENTRIES_NFIELDS	6
#define CVS_ENTRIES_DELIM	'/'

static struct cvs_ent_line *ent_get_line(CVSENTRIES *, const char *);

CVSENTRIES *
cvs_ent_open(const char *dir)
{
	FILE *fp;
	size_t len;
	CVSENTRIES *ep;
	char *p, buf[MAXPATHLEN];
	struct cvs_ent *ent;
	struct cvs_ent_line *line;

	ep = (CVSENTRIES *)xmalloc(sizeof(*ep));
	memset(ep, 0, sizeof(*ep));

	cvs_path_cat(dir, CVS_PATH_ENTRIES, buf, sizeof(buf));
	ep->cef_path = xstrdup(buf);

	cvs_path_cat(dir, CVS_PATH_BACKUPENTRIES, buf, sizeof(buf));
	ep->cef_bpath = xstrdup(buf);

	cvs_path_cat(dir, CVS_PATH_LOGENTRIES, buf, sizeof(buf));
	ep->cef_lpath = xstrdup(buf);

	TAILQ_INIT(&(ep->cef_ent));

	if ((fp = fopen(ep->cef_path, "r")) != NULL) {
		while (fgets(buf, sizeof(buf), fp)) {
			len = strlen(buf);
			if (len > 0 && buf[len - 1] == '\n')
				buf[len - 1] = '\0';

			if (buf[0] == 'D' && buf[1] == '\0')
				break;

			line = (struct cvs_ent_line *)xmalloc(sizeof(*line));
			line->buf = xstrdup(buf);
			TAILQ_INSERT_TAIL(&(ep->cef_ent), line, entries_list);
		}

		(void)fclose(fp);
	}

	if ((fp = fopen(ep->cef_lpath, "r")) != NULL) {
		while (fgets(buf, sizeof(buf), fp)) {
			len = strlen(buf);
			if (len > 0 && buf[strlen(buf) - 1] == '\n')
				buf[strlen(buf) - 1] = '\0';

			p = &buf[1];

			if (buf[0] == 'A') {
				line = xmalloc(sizeof(*line));
				line->buf = xstrdup(p);
				TAILQ_INSERT_TAIL(&(ep->cef_ent), line,
				    entries_list);
			} else if (buf[0] == 'R') {
				ent = cvs_ent_parse(p);
				line = ent_get_line(ep, ent->ce_name);
				if (line != NULL)
					TAILQ_REMOVE(&(ep->cef_ent), line,
					    entries_list);
				cvs_ent_free(ent);
			}
		}

		(void)fclose(fp);
	}

	return (ep);
}

struct cvs_ent *
cvs_ent_parse(const char *entry)
{
	int i;
	struct cvs_ent *ent;
	char *fields[CVS_ENTRIES_NFIELDS], *buf, *sp, *dp;

	buf = xstrdup(entry);
	sp = buf;
	i = 0;
	do {
		dp = strchr(sp, CVS_ENTRIES_DELIM);
		if (dp != NULL)
			*(dp++) = '\0';
		fields[i++] = sp;
		sp = dp;
	} while (dp != NULL && i < CVS_ENTRIES_NFIELDS);

	if (i < CVS_ENTRIES_NFIELDS)
		fatal("missing fields in entry line '%s'", entry);

	ent = (struct cvs_ent *)xmalloc(sizeof(*ent));
	ent->ce_buf = buf;

	if (*fields[0] == '\0')
		ent->ce_type = CVS_ENT_FILE;
	else if (*fields[0] == 'D')
		ent->ce_type = CVS_ENT_DIR;
	else
		ent->ce_type = CVS_ENT_NONE;

	ent->ce_status = CVS_ENT_REG;
	ent->ce_name = fields[1];
	ent->ce_rev = NULL;

	if (ent->ce_type == CVS_ENT_FILE) {
		if (*fields[2] == '-') {
			ent->ce_status = CVS_ENT_REMOVED;
			sp = fields[2] + 1;
		} else {
			sp = fields[2];
			if (fields[2][0] == '0' && fields[2][1] == '\0')
				ent->ce_status = CVS_ENT_ADDED;
		}

		if ((ent->ce_rev = rcsnum_parse(sp)) == NULL)
			fatal("failed to parse entry revision '%s'", entry);

		if (strcmp(fields[3], CVS_DATE_DUMMY) == 0 ||
		    strncmp(fields[3], "Initial ", 8) == 0 ||
		    strncmp(fields[3], "Result of merge", 15) == 0)
			ent->ce_mtime = CVS_DATE_DMSEC;
		else
			ent->ce_mtime = cvs_date_parse(fields[3]);
	}

	ent->ce_conflict = fields[3];
	if ((dp = strchr(ent->ce_conflict, '+')) != NULL)
		*dp = '\0';
	else
		ent->ce_conflict = NULL;

	if (strcmp(fields[4], ""))
		ent->ce_opts = fields[4];
	else
		ent->ce_opts = NULL;

	if (strcmp(fields[5], ""))
		ent->ce_tag = fields[5];
	else
		ent->ce_tag = NULL;

	return (ent);
}

struct cvs_ent *
cvs_ent_get(CVSENTRIES *ep, const char *name)
{
	struct cvs_ent *ent;
	struct cvs_ent_line *l;

	l = ent_get_line(ep, name);
	if (l == NULL)
		return (NULL);

	ent = cvs_ent_parse(l->buf);
	return (ent);
}

int
cvs_ent_exists(CVSENTRIES *ep, const char *name)
{
	struct cvs_ent_line *l;

	l = ent_get_line(ep, name);
	if (l == NULL)
		return (0);

	return (1);
}

void
cvs_ent_close(CVSENTRIES *ep, int writefile)
{
	FILE *fp;
	struct cvs_ent_line *l;

	if (writefile) {
		if ((fp = fopen(ep->cef_bpath, "w")) == NULL)
			fatal("cvs_ent_close: failed to write %s",
			    ep->cef_path);
	}

	while ((l = TAILQ_FIRST(&(ep->cef_ent))) != NULL) {
		if (writefile) {
			fputs(l->buf, fp);
			fputc('\n', fp);
		}

		TAILQ_REMOVE(&(ep->cef_ent), l, entries_list);
		xfree(l->buf);
		xfree(l);
	}

	if (writefile) {
		fputc('D', fp);
		(void)fclose(fp);

		if (rename(ep->cef_bpath, ep->cef_path) == -1)
			fatal("cvs_ent_close: %s: %s", ep->cef_path,
			     strerror(errno));

		(void)unlink(ep->cef_lpath);
	}

	xfree(ep->cef_path);
	xfree(ep->cef_bpath);
	xfree(ep->cef_lpath);
	xfree(ep);
}

void
cvs_ent_add(CVSENTRIES *ep, const char *line)
{
	FILE *fp;
	struct cvs_ent_line *l;
	struct cvs_ent *ent;

	if ((ent = cvs_ent_parse(line)) == NULL)
		fatal("cvs_ent_add: parsing failed '%s'", line);

	l = ent_get_line(ep, ent->ce_name);
	if (l != NULL)
		cvs_ent_remove(ep, ent->ce_name);

	cvs_ent_free(ent);

	cvs_log(LP_TRACE, "cvs_ent_add(%s, %s)", ep->cef_path, line);

	if ((fp = fopen(ep->cef_lpath, "a")) == NULL)
		fatal("cvs_ent_add: failed to open '%s'", ep->cef_lpath);

	fputc('A', fp);
	fputs(line, fp);
	fputc('\n', fp);

	(void)fclose(fp);

	l = (struct cvs_ent_line *)xmalloc(sizeof(*l));
	l->buf = xstrdup(line);
	TAILQ_INSERT_TAIL(&(ep->cef_ent), l, entries_list);
}

void
cvs_ent_remove(CVSENTRIES *ep, const char *name)
{
	FILE *fp;
	struct cvs_ent_line *l;

	cvs_log(LP_TRACE, "cvs_ent_remove(%s, %s)", ep->cef_path, name);

	l = ent_get_line(ep, name);
	if (l == NULL)
		return;

	if ((fp = fopen(ep->cef_lpath, "a")) == NULL)
		fatal("cvs_ent_remove: failed to open '%s'",
		    ep->cef_lpath);

	fputc('R', fp);
	fputs(l->buf, fp);
	fputc('\n', fp);

	(void)fclose(fp);

	TAILQ_REMOVE(&(ep->cef_ent), l, entries_list);
	xfree(l->buf);
	xfree(l);
}

void
cvs_ent_free(struct cvs_ent *ent)
{
	if (ent->ce_rev != NULL)
		rcsnum_free(ent->ce_rev);
	xfree(ent->ce_buf);
	xfree(ent);
}

static struct cvs_ent_line *
ent_get_line(CVSENTRIES *ep, const char *name)
{
	char *p, *s;
	struct cvs_ent_line *l;

	TAILQ_FOREACH(l, &(ep->cef_ent), entries_list) {
		if (l->buf[0] == 'D')
			p = &(l->buf[2]);
		else
			p = &(l->buf[1]);

		if ((s = strchr(p, '/')) == NULL)
			fatal("ent_get_line: bad entry line '%s'", l->buf);

		*s = '\0';

		if (!strcmp(p, name)) {
			*s = '/';
			return (l);
		}

		*s = '/';
	}

	return (NULL);
}