/*
 * Copyright 2005-2006 Vasil Dimov
 * All rights reserved
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted providing that the following conditions 
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 <sys/cdefs.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>

#include <err.h>
#include <stdio.h>
#include <string.h>
#include <sysexits.h>
#if __FreeBSD_version < 500000
/* we use chdir(2) on 4.x */
#include <unistd.h>
#endif

#include "execcmd.h"
#include "exhaust_fp.h"
#include "logmsg.h"
#include "mkdb.h"
#include "parse_indexln.h"
#include "portdef.h"
#include "portsearch.h"
#include "store.h"
#include "vector.h"
#include "xlibc.h"

__RCSID("$Id: mkdb.c,v 1.21 2006/11/02 16:38:50 dd Exp $");

/* process_indexline parameter */
struct pi_arg_t {
	const struct options_t	*opts;
	struct store_t		*store;
	int			s_exists;
	char			*category;
};

static char	portsindex[PATH_MAX];

/*
 * Set index's filename using make -V INDEXFILE
 */
static void set_portsindex(const char *portsdir);
static void _set_portsindex(char *line, void *arg);

/*
 * Process single line from ports' INDEX file
 */
static void process_indexline(char *line, void *arg_void);

/*
 * port->path must be initialized
 * Initialize:
 * port->id using internal counter
 * port->plist by either generating it or retrieving it from
 * the old store (if one exists and port has not been modified recently)
 */
static void set_port_data(struct port_t *port, const struct pi_arg_t *arg);

/*
 * Create the packing list for a given port using make generate-plist
 * port->path must be initialized
 */
static void mkplist(struct port_t *port, const struct pi_arg_t *arg);

/*
 * Add file to port's plist
 */
static void add_pfile(char *file, void *port_void);

/*
 * Return pointer inside portpath, that points after portsdir
 */
static const char *mk_port_short_path(const char *portsdir,
				      const char *portpath);

/*
 * Return pointer inside pkgname - after the last `-'
 */
static const char *mk_pkgversion(const char *pkgname);

/***/

void
mkdb(const struct options_t *opts)
{
	struct pi_arg_t	arg;
	FILE		*portsindex_fp;

	arg.opts = opts;

	set_portsindex(opts->portsdir);

	logmsg(L_NOTICE, opts->verbose, "Creating store\n");

	alloc_store(&arg.store);

	arg.s_exists = s_exists(NULL);
	if (arg.s_exists)
	{
		s_read_start(arg.store);
		logmsg(L_NOTICE, opts->verbose,
		       "Using data from existent store\n");
	}
	else
		logmsg(L_NOTICE, opts->verbose,
		       "Previous store does not exist, creating from scratch\n");

	s_new_start(arg.store);

	portsindex_fp = xfopen(portsindex, "r");

	exhaust_fp(portsindex_fp, process_indexline, &arg);

	xfclose(portsindex_fp, portsindex);

	s_new_end(arg.store);

	if (arg.s_exists)
		s_read_end(arg.store);

	free_store(arg.store);
}

static void
set_portsindex(const char *portsdir)
{
	char		*cmd = "make";
#if __FreeBSD_version >= 500000
	char *const	args[] = {cmd,
		"-C", (char *)portsdir, "-V", "INDEXFILE", NULL};
#else  /* 4.x handles -C differently */
	char		makefile[PATH_MAX];
	char *const	args[] = {cmd,
		"-f", makefile, "-V", "INDEXFILE", NULL};
	snprintf(makefile, sizeof(makefile), "%s/Makefile", portsdir);
#endif

	execcmd(cmd, args, _set_portsindex, (void *)portsdir);
}

static void
_set_portsindex(char *line, void *arg)
{
	snprintf(portsindex, sizeof(portsindex), "%s/%s", (char *)arg, line);
}

static void
process_indexline(char *line, void *arg_void)
{
	struct pi_arg_t	*arg = (struct pi_arg_t *)arg_void;
	struct port_t	addport;

	addport.indexln_raw = line;

	v_start(&addport.plist, 256);

	parse_indexln(&addport);

#define TEST	0

#if TEST
	if (strncmp("/usr/ports/archivers", addport.path, 20) == 0)
	{
#endif
	logmsg(L_INFO, arg->opts->verbose, "==> %s\n",
	       mk_port_short_path(arg->opts->portsdir, addport.path));

	set_port_data(&addport, arg);

	s_add_port(arg->store, &addport);
#if TEST
	}
#endif

	v_destroy(&addport.plist);
}

static void
set_port_data(struct port_t *port, const struct pi_arg_t *arg)
{
	static unsigned	portid = 1;

	const char	*spath;  /* short path: /usr/ports/a/b -> a/b */
	const char	*pkgver_index;
	const char	*pkgver_store;

	struct port_t	*store_port;
	int		gen_plist;

	spath = mk_port_short_path(arg->opts->portsdir, port->path);

	pkgver_index = mk_pkgversion(port->pkgname);

	logmsg(L_DEBUG, arg->opts->verbose, "===> %s INDEX version: %s\n",
	       spath, pkgver_index);

	if (arg->s_exists)
	{
		if (s_load_port_by_path(arg->store, port->path, &store_port)
		    != -1)
		{
			pkgver_store = mk_pkgversion(store_port->pkgname);

			logmsg(L_DEBUG, arg->opts->verbose,
			       "===> %s STORE version: %s\n", spath,
			       pkgver_store);

			if (strcmp(pkgver_store, pkgver_index) != 0)
			{
				logmsg(L_INFO, arg->opts->verbose,
				       "===> %s versions differ, recreating data\n",
				       spath);
				gen_plist = 1;
			}
			else
			{
				logmsg(L_INFO, arg->opts->verbose,
				       "===> %s using stored data\n",
				       spath);
				gen_plist = 0;
			}
		}
		else
		{
			logmsg(L_INFO, arg->opts->verbose,
			       "===> %s not found in store, recreating data\n",
			       spath);
			gen_plist = 1;
		}

	}
	else  /* store does not exist */
		gen_plist = 1;

	if (gen_plist)
		mkplist(port, arg);
	else
	{
		port->id = store_port->id;  /* temporary set to the old id */
		s_load_port_plist(arg->store, port);
	}

	port->id = portid;

	portid++;
}

static void
mkplist(struct port_t *port, const struct pi_arg_t *arg)
{
	char		our_makefile[PATH_MAX];
	char		port_makefile[PATH_MAX];
	char		*cmd = "make";
#if __FreeBSD_version >= 500000
	char *const	args[] = {cmd,
		"-C", port->path, "-f", our_makefile, "-f", port_makefile,
		"show-plist", NULL};
#else  /* 4.x handles -C differently */
	char		curdir_arg[PATH_MAX];
	char *const	args[] = {cmd,
		"-C", port->path, curdir_arg,
		"-f", our_makefile, "-f", port_makefile,
		"show-plist", NULL};
	snprintf(curdir_arg, sizeof(curdir_arg), ".CURDIR=%s", port->path);

	/* math/vecfem does .include <Makefile.inc>, no hope for this on 4.x */
	chdir(port->path);
#endif

	snprintf(our_makefile, sizeof(our_makefile), "%s/Makefile", DATADIR);
	snprintf(port_makefile, sizeof(port_makefile), "%s/Makefile", port->path);

	execcmd(cmd, args, add_pfile, port);
}

static void
add_pfile(char *file, void *port_void)
{
	struct port_t	*port = (struct port_t *)port_void;

	if (file[0] != '@')
		v_add(&port->plist, file, strlen(file) + 1);
}

static const char *
mk_port_short_path(const char *portsdir, const char *portpath)
{
	const char	*spath;

	spath = portpath + strlen(portsdir);

	if (spath[0] == '/')
		spath++;

	return spath;
}

static const char *
mk_pkgversion(const char *pkgname)
{
	const char	*ret;

	if ((ret = strrchr(pkgname, '-')) == NULL)
		/* should never happen */
		return pkgname;

	return ++ret;
}

/* EOF */


syntax highlighted by Code2HTML, v. 0.9.1