/*
 *	fhist - file history and comparison tools
 *	Copyright (C) 1992-1994, 1998-2000, 2002, 2004 Peter Miller;
 *	All rights reserved.
 *
 *	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 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, USA.
 *
 * MANIFEST: functions to pipe output through paginator
 */

#include <ac/stdio.h>
#include <ac/signal.h>
#include <ac/stdlib.h>
#include <ac/unistd.h>
#include <ac/sys/types.h>
#include <sys/wait.h>
#include <ac/libintl.h>

#include <error_intl.h>
#include <pager.h>


static	FILE	*out;
static	char	*pager;
static	int	pid;


/*
 * this stuff is to tell if we are in the background
 */

#ifdef SIGSTOP
#ifndef HAVE_TCGETPGRP

#include <sys/termio.h>

int
tcgetpgrp(int fd)
{
	int		result;

#ifdef TIOCGETPGRP
	if (ioctl(fd, TIOCGETPGRP, &result))
		result = -1;
#else
#ifdef TIOCGPGRP
        if (ioctl(fd, TIOCGPGRP, &result))
		result = -1;
#else
	result = -1;
#endif
#endif
	return result;
}

#endif /* !HAVE_TCGETPGRP */
#endif /* SIGSTOP */


/*
 *  NAME
 *	  background - test for backgroundness
 *
 *  SYNOPSIS
 *	  int background(void);
 *
 *  DESCRIPTION
 *	  The background function is used to determin e if the curent process is
 *	  in the background.
 *
 *  RETURNS
 *	  int: zero if process is not in the background, nonzero if the process
 *	  is in the background.
 *
 * CAVEAT:
 *	This function has a huge chance of being wrong for your system.
 *	If you need to modify this function, please let the author know.
 */

static int
background(void)
{
	RETSIGTYPE	(*x)(int);

	/*
	 * C shell
	 *	puts its children in a different process group.
	 *	The process group the terminal in is the forground.
	 *
	 * Only available on systems with job control.
	 */
#ifdef SIGSTOP
	if (getpgrp(CONF_getpgrp_arg) != tcgetpgrp(0))
		return 1;
#endif

	/*
	 * Bourne shell
	 *	sets its children to ignore SIGINT
	 */
	x = signal(SIGINT, SIG_IGN);
	if (x == SIG_IGN)
		return 1;
	signal(SIGINT, x);

	/*
	 * probably forground
	 */
	return 0;
}


static FILE *
pipe_open(char *prog)
{
	FILE		*fp;
	int		fd[2];
	char		*cmd[4];
	sub_context_ty	*scp;

	fp = 0;
	if (pipe(fd))
	{
		scp = sub_context_new();
		sub_errno_set(scp);
		fatal_intl(scp, i18n("pipe(): $errno"));
		/* NOTREACHED */
		sub_context_delete(scp);
	}
	switch (pid = fork())
	{
	case 0:
		cmd[0] = "sh";
		cmd[1] = "-c";
		cmd[2] = prog;
		cmd[3] = 0;
		close(fd[1]);
		close(0);
		if (dup(fd[0]) != 0)
		{
			fatal_intl(0, i18n("dup was wrong"));
		}
		close(fd[0]);
		execvp(cmd[0], cmd);
		scp = sub_context_new();
		sub_errno_set(scp);
		sub_var_set_charstar(scp, "File_Name", prog);
		fatal_intl(scp, i18n("exec \"$filename\": $errno"));
		/* NOTREACHED */
		sub_context_delete(scp);

	case -1:
		scp = sub_context_new();
		sub_errno_set(scp);
		error_intl(scp, i18n("fork(): $errno"));
		sub_context_delete(scp);
		fp = 0;
		break;

	default:
		close(fd[0]);
		fp = fdopen(fd[1], "w");
		if (!fp)
		{
			scp = sub_context_new();
			sub_errno_set(scp);
			fatal_intl(scp, i18n("fdopen: $errno"));
			/* NOTREACHED */
			sub_context_delete(scp);
		}
		break;
	}
	return fp;
}


static void
pipe_close(FILE *fp)
{
	int		status;
	int		n;

	fclose(fp);
	for (;;)
	{
		n = wait(&status);
		if (n < 0 || n == pid)
			break;
	}
	pid = 0;
}


#ifdef HAVE_ATEXIT

static void
cleanup(void)
{
	if (!out)
		return;

	/*
	 * write the last of the output
	 */
	fflush(out);

	/*
	 * close the paginator
	 */
	if (pager)
	{
		pipe_close(out);
		pager = 0;
	}
	out = 0;
}

#endif /* HAVE_ATEXIT */


FILE *
pager_open(void)
{
	/* assert(!out); */

	/*
	 * if talking to a terminal,
	 * send the output through a paginator
	 */
	if (!background() && isatty(0) && isatty(1))
	{
		pager = getenv("PAGER");
		if (!pager || !*pager)
			pager = "more";
	}
	else
		pager = 0;

#ifdef HAVE_ATEXIT
	/*
	 * register the cleanup function in case of fatal errors
	 */
	atexit(cleanup);
#endif

	/*
	 * open the paginator
	 */
	if (pager)
	{
		out = pipe_open(pager);
		if (!out)
		{
			pager = 0;
			out = stdout;
		}
	}
	else
		out = stdout;
	return out;
}


void
pager_close(FILE *fp)
{
	/* assert(out); */
	/* assert(fp == out); */

	/*
	 * write the last of the output
	 */
	fflush(out);
	if (ferror(out))
		pager_error(out);

	/*
	 * close the paginator
	 */
	if (pager)
	{
		pipe_close(out);
		pager = 0;
	}
	out = 0;
}


void
pager_error(FILE *fp)
{
	sub_context_ty	*scp;

	/* assert(out); */
	/* assert(fp == out); */

	scp = sub_context_new();
	sub_errno_set(scp);
	if (pager)
		sub_var_set_charstar(scp, "File_Name", pager);
	else
		sub_var_set_charstar(scp, "File_Name", gettext("standard input"));
	fatal_intl(scp, i18n("write \"$filename\": $errno"));
	/* NOTREACHED */
	sub_context_delete(scp);
}
