/*
 * fhist - file history and comparison tools
 * Copyright (C) 1992-1994, 1998-2000, 2002, 2004, 2008, 2010, 2012 Peter Miller
 *
 * 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 3 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, see <http://www.gnu.org/licenses/>.
 */

#include <common/ac/stdio.h>
#include <common/ac/signal.h>
#include <common/ac/stdlib.h>
#include <common/ac/unistd.h>
#include <common/ac/sys/types.h>
#include <sys/wait.h>
#include <common/ac/libintl.h>
#include <libexplain/execvp.h>
#include <libexplain/fdopen.h>
#include <libexplain/fflush.h>
#include <libexplain/fork.h>
#include <libexplain/pipe.h>

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


static FILE     *out;
static const 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(const char *prog)
{
    int             fd[2];
    const char      *cmd[4];

    explain_pipe_or_die(fd);
    pid = explain_fork_or_die();
    if (pid == 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]);
        explain_execvp_or_die(cmd[0], (char **)cmd);
        /* NOTREACHED */
    }

    close(fd[0]);
    return explain_fdopen_or_die(fd[1], "w");
}


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)
{
    (void)fp;
    if (!out)
        return;
    /* assert(out); */
    /* assert(fp == out); */

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

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


/* vim: set ts=8 sw=4 et : */
