/*
 * txOutput.c --
 *
 * 	Handles 'stdout' and 'stderr' output.
 *
 *     ********************************************************************* 
 *     * Copyright (C) 1985, 1990 Regents of the University of California. * 
 *     * Permission to use, copy, modify, and distribute this              * 
 *     * software and its documentation for any purpose and without        * 
 *     * fee is hereby granted, provided that the above copyright          * 
 *     * notice appear in all copies.  The University of California        * 
 *     * makes no representations about the suitability of this            * 
 *     * software for any purpose.  It is provided "as is" without         * 
 *     * express or implied warranty.  Export of this software outside     * 
 *     * of the United States of America may require an export license.    * 
 *     *********************************************************************
 */

#ifndef lint
static char rcsid[] = "$Header: /ufs/repository/magic/textio/txOutput.c,v 1.4 2001/02/21 15:52:52 tim Exp $";
#endif  not lint

#include <stdio.h>
#ifndef	SYSV
#include <sys/wait.h>
#include <strings.h>
#else
#include <string.h>
#endif SYSV
#include <sys/stat.h>
#include <unistd.h>
#include <varargs.h>
#include "misc/magsgtty.h"

#include "misc/magic.h"
#include "textio/textio.h"
#include "utils/geometry.h"
#include "textio/txcommands.h"
#include "textio/textioInt.h"
#include "windows/windows.h"
#include "graphics/graphics.h"
#include "misc/paths.h"
#include "utils/utils.h"
#include "utils/malloc.h"

/* When a pipe has been opened to "more", the following variables
 * keep track of the file and process.  The "TxMoreFile" variable is
 * public so that routines like GrVfprintf() can check it to see if it
 * is NULL or not.  It is guaranteed to be NULL if we don't want to send
 * stuff through more.
 */

FILE * TxMoreFile = NULL;
static int txMorePid;
static bool txPrintFlag = TRUE;


/*
 * ----------------------------------------------------------------------------
 * txFprintfBasic:
 *
 *	Textio's own version of printf.  Not to be used outside of this module.
 *
 * Tricks:
 *	Called with a variable number of arguments -- may not be portable.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	text appears on stdout on the text terminal
 *
 * Note: 
 *	Many thanks to Paul Chow at Stanford for getting this to run on
 *	a Pyramid machine.
 * ----------------------------------------------------------------------------
 */ 

 /*VARARGS0*/

void
txFprintfBasic(va_alist)
va_dcl
{
    va_list args;
    char *fmt;
    FILE *f;

    va_start(args);
    f = va_arg(args, FILE *);
    fmt = va_arg(args, char *);
    (void) GrVfprintf(f, fmt, args);
    va_end(args);
}


/*
 * ----------------------------------------------------------------------------
 * TxPrintf:
 *
 *	Magic's own version of printf
 *
 * Tricks:
 *	Called with a variable number of arguments -- may not be portable.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	text appears on stdout on the text terminal
 *
 * Note: 
 *	Many thanks to Paul Chow at Stanford for getting this to run on
 *	a Pyramid machine.
 * ----------------------------------------------------------------------------
 */ 

 /*VARARGS0*/

void
TxPrintf(va_alist)
va_dcl
{
    va_list args;
    char *fmt;
    FILE *f;

    if (txPrintFlag)
    {
	if (TxMoreFile != NULL) 
	{
	    f = TxMoreFile;
	}
	else
	{
	    f = stdout;
	}

	if (txHavePrompt)
	{
	    TxUnPrompt();
	    va_start(args);
	    fmt = va_arg(args, char *);
	    (void) GrVfprintf(f, fmt, args);
	    va_end(args);
	    TxPrompt();
	}
	else 
	{
	    va_start(args);
	    fmt = va_arg(args, char *);
	    (void) GrVfprintf(f, fmt, args);
	    va_end(args);
	}

	return;
    }
}

/*
 * ----------------------------------------------------------------------------
 * TxPrintString --
 *
 *	A version of printf which writes output into a string.
 *
 * Results:
 *	A string which is valid until the next call to TxPrintString().
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */ 

 /*VARARGS0*/

char *
TxPrintString(va_alist)
va_dcl
{
    va_list args;
    char *fmt;
    static char *outstr = NULL;
    static int outlen;
    int nchars;

    if (outstr == NULL)
    {
	outlen = 100;
	MALLOC(char *, outstr, outlen + 1);
    }

    va_start(args);
    fmt = va_arg(args, char *);

    nchars = vsnprintf(outstr, outlen, fmt, args);
    if (nchars >= outlen)
    {
	outlen = nchars + 1;
	FREE(outstr);
	MALLOC(char *, outstr, outlen + 1);
	vsnprintf(outstr, outlen, fmt, args);
    }
    if (nchars == -1)
	return NULL;

    va_end(args);
    return outstr;
}


/*
 * ----------------------------------------------------------------------------
 * TxPrintOn --
 *
 *	Enables TxPrintf() output.
 *
 * Results:
 *	Previous value of flag.
 *
 * ----------------------------------------------------------------------------
 */ 

bool
TxPrintOn()
{
    bool oldValue = txPrintFlag;

    txPrintFlag = TRUE;
    
    return oldValue;
}


/*
 * ----------------------------------------------------------------------------
 * TxPrintOff --
 *
 *	Disables TxPrintf() output.
 *
 * Results:
 *	Previous value of flag.
 *
 * ----------------------------------------------------------------------------
 */ 

bool
TxPrintOff()
{
    bool oldValue = txPrintFlag;

    txPrintFlag = FALSE;

    return oldValue;
}

#ifndef MAGIC_WRAPPER


/*
 * ----------------------------------------------------------------------------
 * TxFlush --
 *
 *	Flush the standard out and error out.
 *
 * Results:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */ 

void
TxFlushErr()
{
    (void) fflush(stderr);
}

/*----------------------------------------------------------------------------*/

void
TxFlushOut()
{
    (void) fflush(stdout);
}

/*----------------------------------------------------------------------------*/

void
TxFlush()
{
    TxFlushOut();
    TxFlushErr();
}

#endif


/*
 * ----------------------------------------------------------------------------
 * TxError:
 *
 *	Magic's own version of printf, but it goes to stderr
 *
 * Tricks:
 *	Called with a variable number of arguments -- may not be portable.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	text appears on stderr on the text terminal
 *
 * Note: 
 *	Many thanks to Paul Chow at Stanford for getting this to run on
 *	a Pyramid machine.
 * ----------------------------------------------------------------------------
 */
 /*VARARGS0*/

void
TxError(va_alist)
va_dcl
{
    va_list args;
    char *fmt;
    FILE *f;

    TxFlushOut();
    if (TxMoreFile != NULL) 
	f = TxMoreFile;
    else
	f = stderr;
    if (txHavePrompt)
    {
	TxUnPrompt();
	va_start(args);
	fmt = va_arg(args, char *);
	(void) GrVfprintf(f, fmt, args);
	va_end(args);
	TxPrompt();
    }
    else {
	va_start(args);
	fmt = va_arg(args, char *);
	(void) GrVfprintf(f, fmt, args);
	va_end(args);
    }
    TxFlushErr();
}

#ifndef MAGIC_WRAPPER


/*
 * ----------------------------------------------------------------------------
 *
 * TxUseMore --
 *
 * 	This procedure forks a "more" process and causes TxError and TxPrintf
 *	to send output through it.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	A file is opened.  When the caller is finished with output,
 *	it must call TxStopMore to clean up the process.
 *
 * ----------------------------------------------------------------------------
 */

void
TxUseMore()
{
    int pipeEnds[2];
    int moreRunning = TRUE;
    static int moreMsg = FALSE;
    char *pagername, *pagerpath, *useenv = NULL;
    extern char *getenv();
    struct stat buf;

    ASSERT(TxMoreFile == NULL, "TxUseMore");

    /* Determine if "more" executable exists and is world executable */
    /* before attempting a fork.  Check environment variable PAGER   */
    /* first before defaulting to the built-in value PAGERDIR	     */
    /* (see misc/paths.h).					     */

    if ((useenv = getenv("PAGER")) == NULL)
    {
	MALLOC(char *, pagerpath, strlen(PAGERDIR) + 1);
	strcpy(pagerpath, PAGERDIR);
    }
    else
	pagerpath = useenv;

    if ((stat(pagerpath, &buf) < 0) || !(buf.st_mode & S_IXOTH))
    {
	if (!moreMsg)
	{
	    TxError("Couldn't execute %s to filter output.\nTry setting "
		    "environment variable PAGER to your favorite pager\n\n",
		    pagerpath);
	    moreMsg = TRUE;
	}
	goto done;
    }

    pipe(pipeEnds);
    FORK(txMorePid);

    /* In the child process, move the pipe input to standard input,
     * delete the output stream, and then run "more".
     */

    if (txMorePid == 0)
    {
	char *argv[100];
	close(pipeEnds[1]);
	dup2(pipeEnds[0], 0);
	if ((pagername = rindex(pagerpath, '/')) != (char *) 0) 
	    pagername++;
	else
	    pagername = pagerpath;
	execl(pagerpath, pagername, 0);

	/* Something went very wrong if it gets here. */

	_exit(-1);
    }

    /* This is the parent process.  Close the input descriptor and make
     * an official FILE for the output descriptor.
     */
    
    close(pipeEnds[0]);
    TxMoreFile = fdopen(pipeEnds[1], "w");

done:
    if (useenv == NULL) FREE(pagerpath);
    return;
}

/*
 * ----------------------------------------------------------------------------
 *
 * TxStopMore --
 *
 * 	Close the pipe connecting us to a "more" process and wait for
 *	the "more" process to die.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The connection to more is closed.
 *
 * ----------------------------------------------------------------------------
 */

void
TxStopMore()
{
/*
#if defined(SYSV) | defined(ALPHA)
    int status;
#else
    union wait status;
#endif SYSV
*/
    /* TxMoreFile may be NULL if the "more" executable was not found */
    if (TxMoreFile == NULL) return;

    /* Close the pipe. */
    ASSERT(txMorePid != 0, "TxStopMore");
    fclose(TxMoreFile);
    TxMoreFile = NULL;

    /* Wait until there are no child processes left.  This is a bit
     * of a kludge, and may screw up if other child processes are
     * created for other purposes at the same time, but I can't see
     * any way around it.
     */
  
    WaitPid (txMorePid, 0);
    txMorePid = 0;
/*  while (wait(&status) != txMorePid) */ /* try again */; 
}

#endif !MAGIC_WRAPPER

#ifdef	NEED_VFPRINTF

int
vfprintf(iop, fmt, ap)
    FILE *iop;
    char *fmt;
    va_list ap;
{
    int len;
#if defined(MIPSEB) && defined(SYSTYPE_BSD43)
    unsigned char localbuf[BUFSIZ];
#else
    char localbuf[BUFSIZ];
#endif

    if (iop->_flag & _IONBF) {
	iop->_flag &= ~_IONBF;
	iop->_ptr = iop->_base = localbuf;
	len = _doprnt(fmt, ap, iop);
	(void) fflush(iop);
	iop->_flag |= _IONBF;
	iop->_base = NULL;
	iop->_bufsiz = 0;
	iop->_cnt = 0;
    } else
	len = _doprnt(fmt, ap, iop);

    return (ferror(iop) ? EOF : len);
}
#endif  NEED_VFPRINTF

