/*
 * Copyright (c) 2003 Benedikt Meurer (benedikt.meurer@unix-ag.uni-siegen.de)
 *               2004 Jean-François Wauthy (pollux@xfce.org)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Library General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /* !HAVE_CONFIG_H */

#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif

#include <errno.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#include "filter.h"


/* create a new filter */
XfprintFilter *
xfprint_filter_new(const gchar *command)
{
	XfprintFilter *filter;

	g_return_val_if_fail(command != NULL, NULL);

	filter = g_new0(XfprintFilter, 1);

	filter->argc = 0;
	filter->argv = g_new0(gchar *, 40);
	filter->size = 40;

	filter->argv[0] = g_path_get_basename(command);
	filter->command = g_strdup(command);

	return(filter);
}

/* free a filter and all memory associated with it */
void
xfprint_filter_free(XfprintFilter *filter)
{
	gchar **p;

	g_return_if_fail(filter != NULL);

	for (p = filter->argv; *p; p++)
		g_free(*p);

	g_free(filter->argv);
	g_free(filter->command);
	g_free(filter);
}

/* add a new command line option to the filter */
void
xfprint_filter_add(XfprintFilter *filter, const gchar *format, ...)
{
	va_list ap;

	g_return_if_fail(filter != NULL);
	g_return_if_fail(format != NULL);

	if (++filter->argc < filter->size) {
		va_start(ap, format);
		filter->argv[filter->argc] = g_strdup_vprintf(format, ap);
		va_end(ap);
	}
	else {
		g_warning(
			"Unable to add another option. Dynamic resizing "
			"no implemented yet.");
	}
}

XfprintFilterList *
xfprint_filterlist_new(void)
{
	return(g_new0(XfprintFilterList, 1));
}

/* free a list of filters (frees the filters itself too) */
void
xfprint_filterlist_free(XfprintFilterList *filters)
{
	g_return_if_fail(filters != NULL);

	g_list_foreach(filters->filters, (GFunc)xfprint_filter_free, NULL);
	g_list_free(filters->filters);
	g_free(filters);
}

void
xfprint_filterlist_append(XfprintFilterList *filters, XfprintFilter *filter)
{
	g_return_if_fail(filter != NULL);
	g_return_if_fail(filters != NULL);

	filters->filters = g_list_append(filters->filters, filter);
}

void
xfprint_filterlist_prepend(XfprintFilterList *filters, XfprintFilter *filter)
{
	g_return_if_fail(filter != NULL);
	g_return_if_fail(filters != NULL);

	filters->filters = g_list_prepend(filters->filters, filter);
}

static XfprintFilter *
xfprint_filterlist_first(XfprintFilterList *filters)
{
	g_return_val_if_fail(filters != NULL, NULL);

	filters->cursor = g_list_first(filters->filters);

	return(filters->cursor ? (XfprintFilter *)filters->cursor->data : NULL);
}

static XfprintFilter *
xfprint_filterlist_next(XfprintFilterList *filters)
{
	g_return_val_if_fail(filters != NULL, NULL);

	if (filters->cursor)
		filters->cursor = g_list_next(filters->cursor);

	return(filters->cursor ? (XfprintFilter *)filters->cursor->data : NULL);
}

int
xfprint_filterlist_execute(XfprintFilterList *filters, int input, int output,
	int error)
{
	XfprintFilter *filter;
	int status;
	int fd[2];
	int outfd;
	pid_t pid;

	filter = xfprint_filterlist_first(filters);

	for (; filter; filter = xfprint_filterlist_next(filters)) {
		if (pipe(fd) < 0)
			return(-1);

		if ((pid = fork()) < 0) {
			return(-1);
		}
		else if (pid == 0) {
			(void)close(fd[0]);

			/* connect the last processes stdout to output */
			if (!g_list_next(filters->cursor))
				outfd = output;
			else
				outfd = fd[1];

			if (input != STDIN_FILENO)
				(void)dup2(input, STDIN_FILENO);

			if (outfd != STDOUT_FILENO)
				(void)dup2(outfd, STDOUT_FILENO);

			if (error != STDERR_FILENO)
				(void)dup2(error, STDERR_FILENO);

			(void)execvp(filter->command, filter->argv);
			_exit(127);
		}

		(void)close(input);
		(void)close(fd[1]);

		input = fd[0];
	}

	(void)close(input);

	while ((pid = waitpid(-1, &status, 0)) >= 0) {
		if (!WIFEXITED(status))
			return(status);
		else if (WEXITSTATUS(status))
			return(WEXITSTATUS(status));
	}

	if (pid < 0 && errno != ECHILD)
		return(-1);

	return(0);
}


