/* RCS file object implementation */
/* $Id: file.c,v 1.24 2003/08/02 13:20:26 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 <stdlib.h>

#include <librcs/file.h>
#include <librcs/librcs.h>
#include <librev/branch.h>
#include <librev/inode.h>


static int rcs_file_xload (struct rev_inode *bj);
static int rcs_file_xcache (struct rev_inode *bj);
static void rcs_file_xdone (struct rev_inode *bj);

struct rcs_file *
rcs_file_init (struct rev_inode *inode, char *filename)
{
	struct rcs_file *obj = calloc (1, sizeof (struct rcs_file));

	if (! obj) return NULL;

	obj->inode = inode;
	if (obj->inode) {
		static struct rev_inode_dataops dataops = {
			/* load: */ rcs_file_xload,
			/* cache: */ rcs_file_xcache,
			/* done: */ rcs_file_xdone,
		};

		rcs_file_associate (obj);
		obj->inode->data = obj;
		obj->inode->dataops = &dataops;
	}

	rev_list_init (obj->accesslist);
	rev_list_init (obj->lockslist);

	rcs_file_set_filename (obj, filename);

	return obj;
}

void
rcs_file_done (struct rcs_file *obj)
{
	/* TODO: More rcsaccess / rcslock support. Perhaps really standalone
	 * objects? --pasky */

	assert (obj && ! obj->refcount);

	if (obj->comment) free (obj->comment);
	if (obj->desc) free (obj->desc);

	if (obj->file) {
		fclose (obj->file);
		obj->file = NULL;
	}

	if (obj->filename) free (obj->filename);

	free (obj);
}


/* FIXME: For now, in order to be as simple as possible, we do all the dirty
 * work in rcs_file_load() (that is, loading of the whole file), and
 * rcs_file_cache() is only dummy device doing what the world thinks it should
 * do. But rcs_file_load() is supposed to be relatively cheap, so this could
 * turn out as quite a bottleneck in the future. We will see and fix later :-).
 * Thus, even after seeing this, you are encouraged to use rcs_file_load() and
 * rcs_file_cache() as recommended in the interface part. Otherwise you could
 * get really burnt in the future versions (where the object MAY NOT yet be
 * complete after rcs_file_load()). --pasky */

extern int yyparse();

extern int yydebug;
extern FILE *yyin;

extern struct rcs_file *yyrcsfile;

static int
rcs_file_xload (struct rev_inode *bj)
{
	struct rcs_file *obj;

	assert (bj);
	obj = bj->data;
	assert (obj);

	/* TODO: Perhaps we shouldn't bother to load the file at all if it is
	 * already loaded. Then we would have to have some reload() method,
	 * though. --pasky */

	if (! obj->file) {
		if (! obj->filename)
			return 0;
		obj->file = fopen (obj->filename, "r");
		if (! obj->file)
			return -1;
	} else {
		rewind (obj->file);
	}

	/* yydebug = 1; */

	yyin = obj->file;
	yyrcsfile = obj;
	if (yyparse ()) {
		return -1;
	}

	return 1;
}

static int
rcs_file_xcache (struct rev_inode *bj)
{
	struct rcs_file *obj;

	assert (bj);
	obj = bj->data;
	assert (obj);
	if (obj->file) {
		fclose (obj->file);
		obj->file = NULL;
	}
	return 1;
}

#if 0
int
rcs_file_store (struct inode *inode, struct iofile *iofile)
{
	assert (iofile && inode);

	if (iofile_normalize (iofile) < 0)
		return -1;

	/* TODO: Do only one revisions iteration instead of two. */
	/* TODO: Branches. */

	fprintf (iofile->file, "head\t.............\n");
	fprintf (iofile->file, "access;\n");
	fprintf (iofile->file, "symbols;\n");
	fprintf (iofile->file, "locks;\n");
	fprintf (iofile->file, "comment @# @;\n");
	fprintf (iofile->file, "\n");

	{
		struct revision *rev;

		foreachback (rev, inode->trunk->revisions) {
			fprintf (iofile->file, "\n");
			fprintf (iofile->file, "%s\n",
				revision_number_string (rev));
			fprintf (iofile->file, "%04d.%02d.%02d.%02d.%02d.%02d;"
					"\tauthor %s;\tstate Exp;\n",
				rev->datetime.year, rev->datetime.month,
				rev->datetime.day, rev->datetime.hour,
				rev->datetime.minute, rev->datetime.second,
				rev->author.email);
			fprintf (iofile->file, "branches;\n");
			fprintf (iofile->file, "next\t");
			if (! list_item_first (rev, branchrev))
				fprintf (iofile->file, "%s",
					revision_number_string (
						list_item_prev (rev, branchrev)
						));
			fprintf (iofile->file, ";\n");
		}
	}

	fprintf (iofile->file, "\n\n");
	fprintf (iofile->file, "desc\n@\n@\n"); /* TODO: fileid */

	{
		struct revision *rev;

		foreachback (rev, inode->trunk->revisions) {
			int res = primitive_revision_ops.store (rev, iofile);

			if (res < 0)
				return res;
		}
	}

	return 1;
}
#endif

static void
rcs_file_xdone (struct rev_inode *bj)
{
	struct rcs_file *obj;

	assert (bj);
	obj = bj->data;
	assert (obj);

	rcs_file_dissociate (obj);
}
