#line 13 "extern.c-nw"
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);
}
#line 90 "extern.c-nw"
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 */
}