/*
 * fragment.c
 * Management of fragment lists.
 *
 * Copyright (c) 2012, 2013, 2014 pkgconf authors (see AUTHORS).
 *
 * Permission to use, copy, modify, and/or 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.
 *
 * This software is provided 'as is' and without any warranty, express or
 * implied.  In no event shall the authors be liable for any damages arising
 * from the use of this software.
 */

#include "pkg.h"
#include "bsdstubs.h"

static inline char *
pkg_fragment_copy_munged(const char *source, unsigned int flags)
{
	char mungebuf[PKG_BUFSIZE];
	char *sysroot_dir;

	if (!(flags & PKGF_MUNGE_SYSROOT_PREFIX))
		return strdup(source);

	sysroot_dir = pkg_tuple_find_global("pc_sysrootdir");

	if (*source != '/' ||
		(sysroot_dir != NULL && !strncmp(sysroot_dir, source, strlen(sysroot_dir))))
		return strdup(source);

	strlcpy(mungebuf, sysroot_dir, sizeof mungebuf);
	strlcat(mungebuf, source, sizeof mungebuf);

	return strdup(mungebuf);
}

void
pkg_fragment_add(pkg_list_t *list, const char *string, unsigned int flags)
{
	pkg_fragment_t *frag;

	if (*string == '-' && strncmp(string, "-lib:", 5) && strncmp(string, "-framework", 10))
	{
		frag = calloc(sizeof(pkg_fragment_t), 1);

		frag->type = *(string + 1);
		frag->data = pkg_fragment_copy_munged(string + 2, flags);
	}
	else
	{
		if (list->tail != NULL && list->tail->data != NULL)
		{
			pkg_fragment_t *parent = list->tail->data;

			if (!strncmp(parent->data, "-framework", 10))
			{
				size_t len = strlen(parent->data) + strlen(string) + 2;
				char *newdata;

				newdata = malloc(len);
				strlcpy(newdata, parent->data, len);
				strlcat(newdata, " ", len);
				strlcat(newdata, string, len);

				free(parent->data);
				parent->data = newdata;

				return;
			}
		}

		frag = calloc(sizeof(pkg_fragment_t), 1);

		frag->type = 0;
		frag->data = strdup(string);
	}

	pkg_node_insert_tail(&frag->iter, frag, list);
}

static inline pkg_fragment_t *
pkg_fragment_lookup(pkg_list_t *list, pkg_fragment_t *base)
{
	pkg_node_t *node;

	PKG_FOREACH_LIST_ENTRY_REVERSE(list->tail, node)
	{
		pkg_fragment_t *frag = node->data;

		if (base->type != frag->type)
			continue;

		if (!strcmp(base->data, frag->data))
			return frag;
	}

	return NULL;
}

static inline bool
pkg_fragment_can_merge_back(pkg_fragment_t *base, unsigned int flags, bool is_private)
{
	(void) flags;

	if (base->type == 'l')
	{
		if (is_private)
			return false;

		return true;
	}

	if (base->type == 'F')
		return false;

	return true;
}

static inline bool
pkg_fragment_can_merge(pkg_fragment_t *base, unsigned int flags, bool is_private)
{
	(void) flags;

	if (is_private)
		return false;

	if (!strncmp(base->data, "-framework", 10))
		return false;

	return true;
}

static inline pkg_fragment_t *
pkg_fragment_exists(pkg_list_t *list, pkg_fragment_t *base, unsigned int flags, bool is_private)
{
	if (!pkg_fragment_can_merge_back(base, flags, is_private))
		return NULL;

	if (!pkg_fragment_can_merge(base, flags, is_private))
		return NULL;

	return pkg_fragment_lookup(list, base);
}

void
pkg_fragment_copy(pkg_list_t *list, pkg_fragment_t *base, unsigned int flags, bool is_private)
{
	pkg_fragment_t *frag;

	if ((frag = pkg_fragment_exists(list, base, flags, is_private)) != NULL)
		pkg_fragment_delete(list, frag);
	else if (!is_private && !pkg_fragment_can_merge_back(base, flags, is_private) && (pkg_fragment_lookup(list, base) != NULL))
		return;

	frag = calloc(sizeof(pkg_fragment_t), 1);

	frag->type = base->type;
	frag->data = strdup(base->data);

	pkg_node_insert_tail(&frag->iter, frag, list);
}

void
pkg_fragment_delete(pkg_list_t *list, pkg_fragment_t *node)
{
	pkg_node_delete(&node->iter, list);

	free(node->data);
	free(node);
}

void
pkg_fragment_free(pkg_list_t *list)
{
	pkg_node_t *node, *next;

	PKG_FOREACH_LIST_ENTRY_SAFE(list->head, next, node)
	{
		pkg_fragment_t *frag = node->data;

		free(frag->data);
		free(frag);
	}
}

void
pkg_fragment_parse(pkg_list_t *list, pkg_list_t *vars, const char *value, unsigned int flags)
{
	int i, argc;
	char **argv;
	char *repstr = pkg_tuple_parse(vars, value);

	pkg_argv_split(repstr, &argc, &argv);

	for (i = 0; i < argc; i++)
		pkg_fragment_add(list, argv[i], flags);

	pkg_argv_free(argv);
	free(repstr);
}
