/* RCS-style revision numbers tools */
/* $Id: revnum.c,v 1.9 2003/09/06 15:08:37 pasky Exp $ */

/*
 * Copyright (c) 2003  Petr Baudis.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials
 *     provided with the distribution.
 *   * Neither the name of the author nor the names of the product's
 *     contributors may be used to endorse or promote products
 *     derived from this software without specific prior written
 *     permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */


#include <assert.h>
#include <stdio.h>
#include <string.h>

#include <librcs/librcs.h>
#include <librcs/revnum.h>

#include <librev/branch.h>
#include <librev/inode.h>
#include <librev/objid.h>
#include <librev/revision.h>


static struct rev_objid *
rcs_revnum_search (struct rev_inode *inode, char *revnum, int create)
{
	struct rev_objid *rev_objid;
	struct rev_revision *rev = inode->forkrev;
	struct rev_branch *branch;
	char *dot = revnum;

	assert (inode && revnum);

	rev_objid = calloc (1, sizeof (struct rev_objid));
	if (! rev_objid) return NULL;

	do {
		int bn, rn;

		bn = strtol (dot, &dot, 10);
		dot++;
		/* If this is branch identifier, @dot points to some junk by
		 * now! */

		branch = rev_revision_find_branch (rev, bn);
		if (! branch && create) {
			branch = rev_branch_allocate (inode, rev, bn);
		}
		if (! branch) {
			free (rev_objid);
			return NULL;
		}

		if (dot [-1] != '.') {
			rev_objid->type = ROIT_BRANCH;
			rev_objid->data.branch = branch;
			rev_objid_tie (rev_objid);
			return rev_objid;
		}

		rn = strtol (dot, &dot, 10);
		dot++;

		rev = rev_branch_find_revision (branch, rn);
		if (! rev && create) {
			rev = rev_revision_allocate (branch, rn);
		}
		if (! rev) {
			free (rev_objid);
			return NULL;
		}
	} while (dot [-1] == '.');

	if (dot [-1]) {
		/* Bad revision number! */
		free (rev_objid);
		return NULL;
	}

	rev_objid->type = ROIT_REVISION;
	rev_objid->data.revision = rev;
	rev_objid_tie (rev_objid);
	return rev_objid;
}

struct rev_objid *
rcs_revnum_lookup (struct rev_inode *inode, char *revnum)
{
	return rcs_revnum_search (inode, revnum, 0);
}

struct rev_objid *
rcs_revnum_dig (struct rev_inode *inode, char *revnum)
{
	return rcs_revnum_search (inode, revnum, 1);
}

char *
rcs_revnum_str (struct rev_objid *node)
{
	struct rev_revision *rev = NULL;
	struct rev_branch *branch;
	/* FIXME: Dynamic buffer! --pasky */
	char str [256] = "";

	if (node->type == ROIT_BRANCH) {
		branch = node->data.branch;
	} else {
		rev = node->data.revision;
		assert (rev && ! rev_revision_is_dummy (rev));
		branch = rev->branch;
	}

	do {
		char tstr [256];

		if (rev) {
			snprintf (tstr, 256, "%d.%s",
				rev_revision_get_number (rev), str);
			strcpy (str, tstr);
		}

		assert (branch);
		snprintf (tstr, 256, "%d.%s",
				rev_branch_get_number (branch), str);
		strcpy (str, tstr);

		rev = branch->parent;
		assert (rev);
		branch = rev->branch;
		/* ! branch == rev_revision_is_dummy (rev) */
	} while (! rev_revision_is_dummy (rev));

	str [strlen (str) - 1] = 0; /* Trailing dot */
	return strdup (str);
}
