jtb@cs.edinburgh.ac.uk (Jo Blishen) (02/22/91)
We're having a problem with xmh on our HP9000/375s. We are running the X11R4 distribution. Instead of running the MH commands xmh generates lots of output of the form: Warning: couldn't inquire bytes in pipe; errno = 25 Not a typewriter Warning: couldn't inquire bytes in pipe; errno = 25 Not a typewriter Warning: couldn't inquire bytes in pipe; errno = 25 Not a typewriter which seems to come from a FIONREAD ioctl on a pipe. Does anyone have a patch for this? Has anyone else seen this? Or is it a configuration problem? Any input gratefully received. -- Jo Blishen JANET: jtb@uk.ac.ed.lfcs LFCS, Dept. of Computer Science UUCP: ..!mcvax!ukc!lfcs!jtb University of Edinburgh ARPA: jtb%lfcs.ed.ac.uk@nsfnet-relay.ac.uk Edinburgh EH9 3JZ, UK. Tel: 031-650-5192
hardy@golem.ps.uci.edu (Meinhard E. Mayer (Hardy)) (02/25/91)
Same problem here on a 9000/370. Something may be missing in the app-defaults file, but I haven't figured it out -- just stopped using Xmh except for moving messages between folders. Hardy -------****------- Meinhard E. Mayer (Prof.) Department of Physics, University of California Irvine CA 92717;(714) 856 5543; hardy@golem.ps.uci.edu or MMAYER@UCI.BITNET
andreas@hpcvlx.cv.hp.com (Jim Andreas) (02/26/91)
The problem is that the FIONREAD ioctl call is broken when
applied to pipes when running HP-UX 7.0 on S300 systems.
I have added a workaround to the xmh code that *mostly* fixes
the problem. You will get xmh dialog boxes popping up now and
then with obscure messages from the underlying mh utilities;
but I have been sucessful at ignoring these :-) I have not had
the cycles to go back and ferret out the reason the underlying
utilities have things to say.
If you prefer a compiled version, I have placed a compressed
executable file on hpcvaaz (15.255.72.15) that is accessable to
anyone on the Internet. You can get the file via anonymous
ftp in:
./pub/MitX11R4/xmh.3.7.0.Z
-----------------------------------------------------------------------
Jim Andreas | andreas@cv.hp.com | INTERNET
Hewlett-Packard Company | {backbone}!hplabs!hp-pcd!andreas | UUCP
1000 N.E. Circle | (USA) (503) 750-2860 | VOICE
Corvallis, OR 97330 | (USA) (503) 750-3788 | FAX
-----------------------------------------------------------------------
This response does not represent the official position of, or statement by,
the Hewlett-Packard Company. This response is provided for informational
purposes only. It is supplied without warranty of any kind.
-----------------------------------------------------------------------
Here is the fix:
=============
xmh/command.c
=============
To save (my) time I will post the fixed module in its entirety,
it is only a 15K module (based upon MIT R4 ./mit/clients/xmh
sources).
#if !defined(lint) && !defined(SABER)
static char rcs_id[] =
"$XConsortium: command.c,v 2.32 89/12/16 21:54:01 rws Exp $";
#endif
/*
* COPYRIGHT 1987, 1989
* DIGITAL EQUIPMENT CORPORATION
* MAYNARD, MASSACHUSETTS
* ALL RIGHTS RESERVED.
*
* THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE AND
* SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT CORPORATION.
* DIGITAL MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR
* ANY PURPOSE. IT IS SUPPLIED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
*
* IF THE SOFTWARE IS MODIFIED IN A MANNER CREATING DERIVATIVE COPYRIGHT
* RIGHTS, APPROPRIATE LEGENDS MAY BE PLACED ON THE DERIVATIVE WORK IN
* ADDITION TO THAT SET FORTH ABOVE.
*
*
* 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 and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of Digital Equipment Corporation not be
* used in advertising or publicity pertaining to distribution of the software
* without specific, written prior permission.
*/
/* command.c -- interface to exec mh commands. */
#include "xmh.h"
#include <sys/ioctl.h>
#include <sys/signal.h>
#ifndef SYSV
#include <sys/wait.h>
#endif /* SYSV */
/* number of user input events to queue before malloc */
#define TYPEAHEADSIZE 20
#ifdef macII
#define vfork() fork()
#endif /* macII */
#if defined(SYSV) && !defined(hpux)
#define vfork() fork()
#endif /* SYSV and not hpux */
#ifndef FD_SET
#define NFDBITS (8*sizeof(fd_set))
#define FD_SETSIZE NFDBITS
#define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
#define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
#define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
#define FD_ZERO(p) bzero((char *)(p), sizeof(*(p)))
#endif /* FD_SET */
typedef struct _CommandStatus {
Widget popup; /* must be first; see PopupStatus */
struct _LastInput lastInput; /* must be second; ditto */
int child_pid;
XtInputId output_inputId;
XtInputId error_inputId;
int output_pipe[2];
int error_pipe[2];
char* output_buffer;
int output_buf_size;
char* error_buffer;
int error_buf_size;
} CommandStatusRec, *CommandStatus;
typedef char* Pointer;
static void FreeStatus();
static CheckReadFromPipe();
static void SystemError(text)
char* text;
{
extern int sys_nerr;
extern char* sys_errlist[];
char msg[BUFSIZ];
sprintf( msg, "%s; errno = %d %s", text, errno,
(errno < sys_nerr) ? sys_errlist[errno] : NULL );
XtWarning( msg );
}
/* Return the full path name of the given mh command. */
static char *FullPathOfCommand(str)
char *str;
{
static char result[100];
(void) sprintf(result, "%s/%s", app_resources.mh_path, str);
return result;
}
/*ARGSUSED*/
static void ReadStdout(closure, fd, id)
XtPointer closure;
int *fd;
XtInputId *id; /* unused */
{
register CommandStatus status = (CommandStatus)closure;
CheckReadFromPipe(*fd, &status->output_buffer, &status->output_buf_size);
}
/*ARGSUSED*/
static void ReadStderr(closure, fd, id)
XtPointer closure;
int *fd;
XtInputId *id; /* unused */
{
register CommandStatus status = (CommandStatus)closure;
CheckReadFromPipe(*fd, &status->error_buffer, &status->error_buf_size);
}
static int childdone; /* Gets nonzero when the child process
finishes. */
ChildDone()
{
childdone++;
}
/* Execute the given command, and wait until it has finished. While the
command is executing, watch the X socket and cause Xlib to read in any
incoming data. This will prevent the socket from overflowing during
long commands. Returns 0 if stderr empty, -1 otherwise. */
static int _DoCommandToFileOrPipe(argv, inputfd, outputfd, bufP, lenP)
char **argv; /* The command to execute, and its args. */
int inputfd; /* Input stream for command. */
int outputfd; /* Output stream; /dev/null if == -1 */
char **bufP; /* output buffer ptr if outputfd == -2 */
int *lenP; /* output length ptr if outputfd == -2 */
{
int return_status;
int old_stdin, old_stdout, old_stderr;
int pid;
fd_set readfds, fds;
Boolean output_to_pipe = False;
CommandStatus status = XtNew(CommandStatusRec);
FD_ZERO(&fds);
FD_SET(ConnectionNumber(theDisplay), &fds);
DEBUG1("Executing %s ...", argv[0])
if (inputfd != -1) {
old_stdin = dup(fileno(stdin));
(void) dup2(inputfd, fileno(stdin));
}
if (outputfd == -1) {
if (!app_resources.debug) { /* Throw away stdout. */
outputfd = open( "/dev/null", O_WRONLY, 0 );
old_stdout = dup(fileno(stdout));
(void) dup2(outputfd, fileno(stdout));
close(outputfd);
}
}
else if (outputfd == -2) { /* make pipe */
if (pipe(status->output_pipe) /*failed*/) {
SystemError( "couldn't re-direct standard output" );
status->output_pipe[0]=0;
}
else {
outputfd = status->output_pipe[1];
old_stdout = dup(fileno(stdout));
(void) dup2(status->output_pipe[1], fileno(stdout));
FD_SET(status->output_pipe[0], &fds);
status->output_inputId =
XtAddInput( status->output_pipe[0], (XtPointer)XtInputReadMask,
ReadStdout, (XtPointer)status
);
status->output_buffer = NULL;
status->output_buf_size = 0;
output_to_pipe = True;
}
}
else {
old_stdout = dup(fileno(stdout));
(void) dup2(outputfd, fileno(stdout));
}
if (pipe(status->error_pipe) /*failed*/) {
SystemError( "couldn't re-direct standard error" );
status->error_pipe[0]=0;
}
else {
old_stderr = dup(fileno(stderr));
(void) dup2(status->error_pipe[1], fileno(stderr));
FD_SET(status->error_pipe[0], &fds);
status->error_inputId =
XtAddInput( status->error_pipe[0], (XtPointer)XtInputReadMask,
ReadStderr, (XtPointer)status
);
}
childdone = FALSE;
status->popup = (Widget)NULL;
status->lastInput = lastInput;
status->error_buffer = NULL;
status->error_buf_size = 0;
(void) signal(SIGCHLD, ChildDone);
pid = vfork();
if (inputfd != -1) {
if (pid != 0) dup2(old_stdin, fileno(stdin));
close(old_stdin);
}
if (outputfd != -1) {
if (pid != 0) dup2(old_stdout, fileno(stdout));
close(old_stdout);
}
if (status->error_pipe[0]) {
if (pid != 0) dup2(old_stderr, fileno(stderr));
close(old_stderr);
}
if (pid == -1) Punt("Couldn't fork!");
if (pid) { /* We're the parent process. */
XEvent typeAheadQueue[TYPEAHEADSIZE], *eventP = typeAheadQueue;
XEvent *altQueue = NULL;
int type_ahead_count = 0, alt_queue_size = 0, alt_queue_count = 0;
XtAppContext app = XtWidgetToApplicationContext(toplevel);
int num_fds = ConnectionNumber(theDisplay)+1;
if (output_to_pipe && status->output_pipe[0] >= num_fds)
num_fds = status->output_pipe[0]+1;
if (status->error_pipe[0] >= num_fds)
num_fds = status->error_pipe[0]+1;
status->child_pid = pid;
DEBUG1( " pid=%d ", pid )
subProcessRunning = True;
while (!childdone) {
while (!(XtAppPending(app) & XtIMXEvent)) {
/* this is gross, but the only other way is by
* polling on timers or an extra pipe, since we're not
* guaranteed to be able to malloc in a signal handler.
*/
readfds = fds;
if (childdone) break;
DEBUG("blocking.\n")
(void) select(num_fds, (int *) &readfds,
(int *) NULL, (int *) NULL, (struct timeval *) NULL);
DEBUG1("unblocked; child%s done.\n", childdone ? "" : " not")
if (childdone) break;
if (!FD_ISSET(ConnectionNumber(theDisplay), &readfds))
{DEBUG("reading alternate input...")
XtProcessEvent((unsigned) XtIMAlternateInput);
DEBUG("read.\n")}
}
if (childdone) break;
XtAppNextEvent(app, eventP);
switch(eventP->type) {
case LeaveNotify:
if (type_ahead_count) {
/* do compress_enterleave here to save memory */
XEvent *prevEvent;
if (alt_queue_size && (alt_queue_count == 0))
prevEvent = &typeAheadQueue[type_ahead_count-1];
else
prevEvent = eventP - 1;
if (prevEvent->type == EnterNotify
&& prevEvent->xany.display == eventP->xany.display
&& prevEvent->xany.window == eventP->xany.window) {
eventP = prevEvent;
if (alt_queue_count > 0)
alt_queue_count--;
else
type_ahead_count--;
break;
}
}
/* fall through */
case KeyPress:
case KeyRelease:
case EnterNotify:
case ButtonPress:
case ButtonRelease:
case MotionNotify:
if (type_ahead_count < TYPEAHEADSIZE) {
if (++type_ahead_count == TYPEAHEADSIZE) {
altQueue = (XEvent*)XtMalloc(
(Cardinal)TYPEAHEADSIZE*sizeof(XEvent) );
alt_queue_size = TYPEAHEADSIZE;
eventP = altQueue;
}
else
eventP++;
}
else {
if (++alt_queue_count == alt_queue_size) {
alt_queue_size += TYPEAHEADSIZE;
altQueue = (XEvent*)XtRealloc(
(char*)altQueue,
(Cardinal)alt_queue_size*sizeof(XEvent) );
eventP = &altQueue[alt_queue_count];
}
else
eventP++;
}
break;
default:
XtDispatchEvent(eventP);
}
}
#ifdef SYSV
(void) wait((int *) NULL);
#else /* !SYSV */
(void) wait((union wait *) NULL);
#endif /* !SYSV */
DEBUG("done\n")
subProcessRunning = False;
if (output_to_pipe) {
CheckReadFromPipe( status->output_pipe[0],
&status->output_buffer,
&status->output_buf_size
);
*bufP = status->output_buffer;
*lenP = status->output_buf_size;
close( status->output_pipe[0] );
close( status->output_pipe[1] );
XtRemoveInput( status->output_inputId );
}
if (status->error_pipe[0]) {
CheckReadFromPipe( status->error_pipe[0],
&status->error_buffer,
&status->error_buf_size
);
close( status->error_pipe[0] );
close( status->error_pipe[1] );
XtRemoveInput( status->error_inputId );
}
if (status->error_buffer != NULL) {
while (status->error_buffer[status->error_buf_size-1] == '\0')
status->error_buf_size--;
while (status->error_buffer[status->error_buf_size-1] == '\n')
status->error_buffer[--status->error_buf_size] = '\0';
DEBUG1( "stderr = \"%s\"\n", status->error_buffer )
PopupNotice( status->error_buffer, FreeStatus, (Pointer)status );
return_status = -1;
}
else {
XtFree( (Pointer)status );
return_status = 0;
}
for (;alt_queue_count;alt_queue_count--) {
XPutBackEvent(theDisplay, --eventP);
}
if (type_ahead_count) {
if (alt_queue_size) eventP = &typeAheadQueue[type_ahead_count];
for (;type_ahead_count;type_ahead_count--) {
XPutBackEvent(theDisplay, --eventP);
}
}
} else { /* We're the child process. */
(void) execv(FullPathOfCommand(argv[0]), argv);
(void) execvp(argv[0], argv);
Punt("Execvp failed!");
return_status = -1;
}
return return_status;
}
static /*void*/
CheckReadFromPipe( fd, bufP, lenP )
int fd;
char **bufP;
int *lenP;
{
#ifdef hpux
int oldflags;
int newflags;
char c;
/*
* The FIONREAD ioctl is broken on HP-UX, so substitute
* the following workaround. This code sets the O_NDELAY
* mode on the pipe, so that we can read from it without
* blocking. The code then reads bytes from the pipe
* until the read returns with zero bytes.
*/
if (( oldflags = fcntl( fd, F_GETFL, 0 )) < 0 )
SystemError( "CheckReadFromPipe: error on fcntl F_GETFL" );
newflags = oldflags | O_NDELAY;
if ( fcntl( fd, F_SETFL, newflags ) < 0 )
SystemError( "CheckReadFromPipe: error on fcntl F_SETFL" );
if (read( fd, &c, 1 ) == 1 )
{
char * pChar;
int ByteCount = 1;
int old_len = *lenP;
*bufP = XtRealloc( *bufP, (Cardinal) ((*lenP += BUFSIZ) + 1) );
pChar = *bufP + old_len;
*pChar++ = c;
for ( ;; )
{
int RetCode;
if (( RetCode = read( fd, &c, 1 )) == 0 )
{
*pChar = '\0';
break;
}
if ( RetCode < 0 )
{
SystemError( "CheckReadFromPipe: error on read from pipe" );
*pChar = '\0';
break;
}
if ( ByteCount == BUFSIZ )
{
old_len = *lenP;
*bufP = XtRealloc( *bufP, (Cardinal) ((*lenP += BUFSIZ) + 1) );
ByteCount = 0;
pChar = *bufP + old_len;
}
*pChar++ = c;
ByteCount++;
}
}
if ( fcntl( fd, F_SETFL, oldflags ) < 0 )
SystemError( "CheckReadFromPipe: error on fcntl F_SETFL" );
#else /* hpux */
long nread;
if (ioctl( fd, FIONREAD, &nread ) /*failed*/) {
SystemError( "couldn't inquire bytes in pipe" );
}
else if (nread) {
char buf[BUFSIZ];
int old_end = *lenP;
*bufP = XtRealloc( *bufP, (Cardinal) ((*lenP += nread) + 1) );
while (nread > BUFSIZ) {
read( fd, buf, BUFSIZ );
bcopy( buf, *bufP+old_end, BUFSIZ );
nread -= BUFSIZ;
old_end += BUFSIZ;
}
read( fd, buf, (int) nread );
bcopy( buf, *bufP+old_end, (int) nread );
(*bufP)[old_end+nread] = '\0';
}
#endif /* hpux */
}
/* ARGSUSED */
static void FreeStatus( w, closure, call_data )
Widget w; /* unused */
Pointer closure;
Pointer call_data; /* unused */
{
CommandStatus status = (CommandStatus)closure;
if (status->popup != (Widget)NULL) {
XtPopdown( status->popup );
XtDestroyWidget( status->popup );
}
if (status->error_buffer != NULL) XtFree(status->error_buffer);
XtFree( closure );
}
/* Execute the given command, waiting until it's finished. Put the output
in the specified file path. Returns 0 if stderr empty, -1 otherwise */
DoCommand(argv, inputfile, outputfile)
char **argv; /* The command to execute, and its args. */
char *inputfile; /* Input file for command. */
char *outputfile; /* Output file for command. */
{
int fd_in, fd_out;
int status;
if (inputfile != NULL) {
FILEPTR file = FOpenAndCheck(inputfile, "r");
fd_in = dup(fileno(file));
myfclose(file);
}
else
fd_in = -1;
if (outputfile) {
FILEPTR file = FOpenAndCheck(outputfile, "w");
fd_out = dup(fileno(file));
myfclose(file);
}
else
fd_out = -1;
status = _DoCommandToFileOrPipe( argv, fd_in, fd_out, (char **) NULL,
(int *) NULL );
if (fd_in != -1) close(fd_in);
if (fd_out != -1) close(fd_out);
return status;
}
/* Execute the given command, waiting until it's finished. Put the output
in a newly mallocced string, and return a pointer to that string. */
char *DoCommandToString(argv)
char ** argv;
{
char *result = NULL;
int len = 0;
_DoCommandToFileOrPipe( argv, -1, -2, &result, &len );
if (result == NULL) result = XtMalloc((Cardinal) 1);
result[len] = '\0';
DEBUG1("('%s')\n", result)
return result;
}
/* Execute the command to a temporary file, and return the name of the file. */
char *DoCommandToFile(argv)
char **argv;
{
char *name;
FILEPTR file;
int fd;
name = MakeNewTempFileName();
file = FOpenAndCheck(name, "w");
fd = dup(fileno(file));
myfclose(file);
_DoCommandToFileOrPipe(argv, -1, fd, (char **) NULL, (int *) NULL);
close(fd);
return name;
}
/* EOF */