	Viewer for calling external viewers

	This W3A compliant applet is meant a viewer for formats that
	are not handled by other W3A viewers. It saves the data to a
	temporary file and then calls the [[metamail]] program to
	display it.

	[[metamail]] uses the personal or system-wide mailcap file to
	find the program that can display this particaular data
	format.

<<*>>=
static char copyright[] = "Copyright NBBI, Den Haag, 1995";
#include <config.h>
#include <signal.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <Xm/Xm.h>
#include <Xm/ScrolledW.h>
#include <Xm/Text.h>
#include <w3a.h>
#include <str.h>

#define METAMAIL_CMD "metamail -d -b -c '%s'"

typedef struct {
    Widget w;
    pid_t child;
    int fin, fout;				/* Child's input & output */
    XtInputId id;
} *Info;

typedef struct _Assoc {
    long id;
    Info b;
    struct _Assoc *next;
} *Assoc;

static Assoc assoclist = NULL;

/* store -- store an ID/Buffer combination */
static void store(Info b, long id)
{
    Assoc h;
    new(h); h->id = id; h->b = b; h->next = assoclist; assoclist = h;
}

/* delete -- delete an ID/Buffer combination */
static void delete(long id)
{
    Assoc g, h;
    assert(assoclist);
    if (assoclist->id == id) {
	h = assoclist; assoclist = assoclist->next; dispose(h);
    } else {
	assert(h->next);
	for (h = assoclist; h->next->id != id; h = h->next) assert(h->next);
	g = h->next; h->next = g->next; dispose(g);
    }
}

/* find -- find the Buffer associated with an ID */
static Info find(long id)
{
    Assoc h;
    assert(assoclist);
    for (h = assoclist; h->id != id; h = h->next) assert(h->next);
    return h->b;
}


static void syserr(char *s)
{
    perror(s);
    exit(1);
}
@

	The out_cb function is called when there is data available
	from the supprocess. It reads the data and passes it to the
	AnsiTerm widget.

	client_data holds the ID of the AnsiTerm widget to which the
	data must be sent.

<<*>>=
static void out_cb(XtPointer client_data, int *fid, XtInputId *id)
{
    Widget w = (Widget) client_data;
    char buf[BUFSIZ];
    int n;
  
    while ((n = read(*fid, buf, sizeof(buf) - 1)) == -1 && errno == EINTR) ;
    if (n == -1)
	XtAppWarning(XtWidgetToApplicationContext(w), "Read error");
/*    else if (n == 0) {
	XtRemoveInput(*id);
	XmTextSetString(w, "Program has stopped");
    } */
    else {
	buf[n] = '\0';
	XmTextInsert(w, XmTextGetLastPosition(w), buf);
    }
}



/* resize_cb -- callback for when the AnsiTerm changes size */
static void resize_cb(w, client_data, call_data)
    Widget w;
    XtPointer client_data, call_data;
{
    /* XfwfResizeInfo *size = (XfwfResizeInfo *) call_data; */
    pid_t *child = (pid_t *) client_data;

    kill(*child, SIGWINCH);
}


/* initExtern -- initialize class variables for the Extern viewer */
EXPORT Bool initExtern(char ***mime_types, int *nrtypes, float **qual)
{
    static char *types[] = {"*/*"};
    static float pref[] = {0.1};

    *mime_types = types;
    *nrtypes = 1;
    *qual = pref;
    return TRUE;				/* Nothing to initialize */
}


/* openExtern -- open an external viewer */
EXPORT Bool openExtern(const W3ADocumentInfo doc, W3AWindow area, long id)
{
    int pfdout[2], pfdin[2];
    long c, tblsiz;
    pid_t child;
    Info info;
    char s[BUFSIZ];
    int n;
    static Arg args[] = {
	{XmNeditable, FALSE},
	{XmNeditMode, XmMULTI_LINE_EDIT},
    };

    if (pipe(pfdout) == -1 || pipe(pfdin) == -1) return FALSE;

    switch ((child = fork())) {
    case -1:
	return FALSE;
	/* NOTREACHED */
    case 0:
#if 0
	setsid();				/* Dissociate from terminal */
#endif
	if (close(0) == -1) syserr("close");
	if (dup(pfdin[0]) != 0) syserr("dup failed\n");
	if (close(1) == -1) syserr("close");
	if (dup(pfdout[1]) != 1) syserr("dup failed\n");
	if (close(2) == -1) syserr("close");
	if (dup(pfdout[1]) != 2) syserr("dup failed\n");
	/* Close all other files */
	tblsiz = sysconf(_SC_OPEN_MAX);
	for (c = 3; c < tblsiz; c++) (void) close(c);
	sprintf(s, METAMAIL_CMD, doc.mime_type);
	printf("The document is shown by another program:\n");
	printf("  %s\n\n", s);
	fflush(stdin);
	putenv("TERM=ansi");
	execl(SHELL_PATH, SHELL_NAME, "-c", s, 0);
	syserr("execl failed");
	/* NOTREACHED */
    }
    if (close(pfdout[1]) == -1 || close(pfdin[0]) == -1) return FALSE;
    signal(SIGPIPE, SIG_IGN);

    new(info);
    store(info, id);
    info->child = child;
    info->fin = pfdin[1];			/* Child's input */
    info->fout = pfdout[0];			/* Child's output */
    info->w = XmCreateScrolledText(area, "extern", args, XtNumber(args));

    /* Install input handler to read child's output pipe */
    info->id = XtAppAddInput(XtWidgetToApplicationContext(info->w),
			     info->fout, (XtPointer) XtInputReadMask,
			     out_cb, info->w);
    return TRUE;
}


/* writeExtern -- copy data to external viewer */
EXPORT int writeExtern(long id, const char *buf, size_t nchars)
{
    Info info = find(id);
    int n;

    if (nchars != 0) {
	while ((n = write(info->fin, buf, nchars)) == -1 && errno == EINTR) ;
	return n;
    } else					/* End of input */
	return close(info->fin);
}


/* closeExtern -- close external viewer */
EXPORT Bool closeExtern(long id)
{
    Info info = find(id);

    XtRemoveInput(info->id);
    (void) close(info->fin);
    (void) close(info->fout);
    XtDestroyWidget(info->w);
    kill(info->child, SIGKILL);
    dispose(info);
    return TRUE;
}


/* infoExtern -- change document info, if needed */
EXPORT Bool infoExtern(long id, W3ADocumentInfo *info)
{
    return TRUE;				/* No changes */
}


EXPORT void eventExtern(long id, long sourceid, long eventtype, void *params)
{
    /* Doesn't handle W3A events */
}
