496 lines
10 KiB
C
496 lines
10 KiB
C
/*
|
|
* 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[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/textio/txOutput.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $";
|
|
#endif /* not lint */
|
|
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <stdlib.h> /* getenv() */
|
|
#include <string.h>
|
|
|
|
#ifndef SYSV
|
|
#include <sys/wait.h>
|
|
#endif /* SYSV */
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
#include "utils/magsgtty.h"
|
|
#include "utils/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 "utils/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 vfprintf() 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.
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
txFprintfBasic(FILE *f, const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
Vfprintf(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.
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
TxPrintf(const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
FILE *f;
|
|
|
|
if (txPrintFlag)
|
|
{
|
|
if (TxMoreFile != NULL)
|
|
{
|
|
f = TxMoreFile;
|
|
}
|
|
else
|
|
{
|
|
f = stdout;
|
|
}
|
|
|
|
if (txHavePrompt)
|
|
{
|
|
TxUnPrompt();
|
|
va_start(args, fmt);
|
|
Vfprintf(f, fmt, args);
|
|
va_end(args);
|
|
TxPrompt();
|
|
}
|
|
else
|
|
{
|
|
va_start(args, fmt);
|
|
Vfprintf(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.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
char *
|
|
TxPrintString(const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
static char *outstr = NULL;
|
|
static int outlen;
|
|
int nchars;
|
|
|
|
if (outstr == NULL)
|
|
{
|
|
outlen = 100;
|
|
outstr = (char *) mallocMagic((unsigned) (outlen + 1));
|
|
}
|
|
|
|
va_start(args, fmt);
|
|
nchars = vsnprintf(outstr, outlen, fmt, args);
|
|
va_end(args);
|
|
|
|
if (nchars >= outlen)
|
|
{
|
|
outlen = nchars + 1;
|
|
freeMagic(outstr);
|
|
outstr = (char *) mallocMagic((unsigned) (outlen + 1));
|
|
va_start(args, fmt);
|
|
vsnprintf(outstr, outlen, fmt, args);
|
|
va_end(args);
|
|
}
|
|
if (nchars == -1)
|
|
return NULL;
|
|
|
|
return outstr;
|
|
}
|
|
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
* TxPrintOn --
|
|
*
|
|
* Enables TxPrintf() output.
|
|
*
|
|
* Results:
|
|
* Previous value of flag.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
bool
|
|
TxPrintOn(void)
|
|
{
|
|
bool oldValue = txPrintFlag;
|
|
|
|
txPrintFlag = TRUE;
|
|
|
|
return oldValue;
|
|
}
|
|
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
* TxPrintOff --
|
|
*
|
|
* Disables TxPrintf() output.
|
|
*
|
|
* Results:
|
|
* Previous value of flag.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
bool
|
|
TxPrintOff(void)
|
|
{
|
|
bool oldValue = txPrintFlag;
|
|
|
|
txPrintFlag = FALSE;
|
|
|
|
return oldValue;
|
|
}
|
|
|
|
#ifndef MAGIC_WRAPPER
|
|
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
* TxFlush --
|
|
*
|
|
* Flush the standard out and error out.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
TxFlushErr(void)
|
|
{
|
|
(void) fflush(stderr);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
void
|
|
TxFlushOut(void)
|
|
{
|
|
(void) fflush(stdout);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
void
|
|
TxFlush(void)
|
|
{
|
|
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.
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
TxError(const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
TxErrorV(fmt, args);
|
|
va_end(args);
|
|
}
|
|
void
|
|
TxErrorV(const char *fmt, va_list args)
|
|
{
|
|
FILE *f;
|
|
|
|
TxFlushOut();
|
|
if (TxMoreFile != NULL)
|
|
f = TxMoreFile;
|
|
else
|
|
f = stderr;
|
|
if (txHavePrompt)
|
|
{
|
|
TxUnPrompt();
|
|
Vfprintf(f, fmt, args);
|
|
TxPrompt();
|
|
}
|
|
else {
|
|
Vfprintf(f, fmt, 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(void)
|
|
{
|
|
int pipeEnds[2];
|
|
int moreRunning = TRUE;
|
|
static int moreMsg = FALSE;
|
|
const char *pagername, *pagerpath, *useenv = NULL;
|
|
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 utils/paths.h). */
|
|
|
|
if ((useenv = getenv("PAGER")) == NULL)
|
|
{
|
|
pagerpath = (char *) mallocMagic((unsigned) (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 = strrchr(pagerpath, '/')) != (char *) 0)
|
|
pagername++;
|
|
else
|
|
pagername = pagerpath;
|
|
execl(pagerpath, pagername, NULL);
|
|
|
|
/* 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) freeMagic(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(void)
|
|
{
|
|
/* 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;
|
|
}
|
|
|
|
#endif /* !MAGIC_WRAPPER */
|
|
|
|
#ifdef NEED_VFPRINTF
|
|
|
|
int
|
|
vfprintf(FILE *iop, const char *fmt, va_list args_in)
|
|
{
|
|
va_list ap;
|
|
int len;
|
|
#if defined(MIPSEB) && defined(SYSTYPE_BSD43)
|
|
unsigned char localbuf[BUFSIZ];
|
|
#else
|
|
char localbuf[BUFSIZ];
|
|
#endif
|
|
|
|
va_copy(ap, args_in);
|
|
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);
|
|
|
|
va_end(ap);
|
|
return (ferror(iop) ? EOF : len);
|
|
}
|
|
#endif /* NEED_VFPRINTF */
|
|
|