        Commands

        The commands that the browser implements (through menus and
        buttons) are listed in this module.

	Question: should [[W3Aprocess]] try to `clean up' the URL, by
	removing spaces and tabs?

<<*>>=
#include "config.h"
#include <fcntl.h>
#include <limits.h>
#include <Xm/Xm.h>
#include <Xm/PanedW.h>
#include <Xm/Frame.h>
#include "w3a.h"
#include <errno.h>
#include "str.h"
#include "url.h"
#include "w3alib.h"
#include "rcfile.e"
#include "globals.e"
#include "dynload.e"
#include "requests.e"
#include "input.e"
#include "viewers.e"
#include "cache.e"
#include "tabs.e"



static process(W3ADocumentInfo *doc, int method, const char
	       *data, size_t nbytes, Bool no_events)
{
    /* W3ADocumentInfo info; */
    URI uri;
    int fd = -1, doc_nr, i, a = -1, tab;
    ViewerInfo *info = NULL;
    Bool use_cache;
    char *url;
    const char *where;
    size_t howmuch;

    if (! check_url(doc, &uri)) {errno = EURL; return FALSE;}
    url = newstring(doc->url);

    if (method == GET_METHOD && (i = find_among_tabs(url)) >= 0) {
	switch_to_tabbed_doc(i);
	goto success;
    }

    if (method != GET_METHOD) {
	a = find_agent(strip2str(uri.scheme));
	if (a == -1) {errno = ETYPE; goto fail;}
	if (method == DELETE_METHOD)
	    if (agent_delete(a, url)) goto success; else goto fail;
	if (method == PUT_METHOD || method == POST_METHOD) {
	    int n;
	    char s[256];
	    /*
	     * For POST, the connection is made immediately,
	     * without waiting for other connections to close.
	     * The request queue in `input.c' will notice
	     * that fd >= 0 and will use the already opened
	     * connection.
	     */
	    fd = agent_open(a, url, method, O_NONBLOCK, doc->referer);
	    if (fd == -1) goto fail;
	    n = sprintf(s, "Content-Length: %d\r\n", nbytes);
	    n = agent_write(a, fd, s, n);
	    n = sprintf(s, "Content-Type: \
application/x-www-form-urlencoded\r\n\r\n");
	    n = agent_write(a, fd, s, n);
	    if (n == -1 && errno != EAGAIN) goto fail;
	    while (nbytes > 0) {
		n = agent_write(a, fd, data, nbytes);
		if (n == -1 && errno != EAGAIN) goto fail;
		data += n;
		nbytes -= n;
	    }
	}
    }

    /* Initialize info */
    info = get_request_rec();
    info->method = method;
    info->doc = new_doc();
    copy_doc(info->doc, *doc);
    info->agent = a;
    info->fd = fd;
    info->seqnr = seqnr++;			/* Unique request ID */
    info->high_prio = FALSE;
    info->w = XtVaCreateManagedWidget
	("viewer", xmFrameWidgetClass, area,
	 XmNmarginWidth, 0, XmNmarginHeight, 0,
	 XmNbottomAttachment, XmATTACH_FORM,
	 XmNrightAttachment, XmATTACH_FORM,
	 XmNleftAttachment, XmATTACH_FORM,
	 XmNtopAttachment, XmATTACH_FORM,
	 NULL);
    if (XtIsRealized(info->w))
	XDefineCursor(XtDisplay(info->w), XtWindow(info->w), waitcursor);
    info->need_event = (method == GET_METHOD) && ! no_events;
    info->toplevel = TRUE;

    /* Set tabs */
    tab = get_oldest_tab();			/* See tabs.c */
    create_tab(tab, info);
    XFlush(XtDisplay(toplevel));		/* Map window */

#if 0
    if (! use_cache) {
#endif
	/* Start request in the background */
	assert(fd == -1 || method != GET_METHOD);
	start_request(uri, info, TRUE);		/* Prioritized request */
#if 0
    } else {
	/* Copy from the cache to a viewer */
	info->viewer = find_viewer(doc->mime_type);
	if (! viewer_open(info->viewer, *doc, info->w, (long) info))
	    goto fail;
	if (viewer_write(info->viewer, (long) info, where, howmuch) != howmuch
	    || viewer_write(info->viewer, (long) info, NULL, 0) != 0)
	    goto fail;
	set_tab_titles();
    }
#endif

    (void) XmProcessTraversal(info->w, XmTRAVERSE_CURRENT);

success:
    dispose(url);
    return TRUE;

fail:
    if (info) (void) free_request_rec(info);
    dispose(url);
    return FALSE;
}
@   
    
        The [[goto_url]] command is used to jump directly to a URL that
        the user typed.

        TO DO: handle errors specified in the [[status]] field.

        TO DO: deallocate heap memory.
        
<<*>>=
EXPORT void goto_url(const String url, Bool no_events)
{
    W3ADocumentInfo *doc;
    char pwd[1060];

    if (url == NULL || url[0] == '\0') return;

    doc = new_doc();
    doc->url = newstring(url);
    strcpy(pwd, "file://localhost");
    (void) getcwd(pwd + strlen(pwd), 1024);
    strcat(pwd, "/dummy");
    doc->referer = newstring(pwd);
    if (! process(doc, GET_METHOD, NULL, 0, no_events))
	show_error("goto_url", *doc);
    dispose_doc(doc);
}



EXPORT void clone_argo(void)
{
    ViewerInfo *cur;
    String *argv, *argv2;
    int argc, i;
    long c, tblsiz;

    cur = get_cur_doc();
    XtVaGetValues(toplevel, XmNargc, &argc, XmNargv, &argv, NULL);
    newarray(argv2, argc + 2);
    for (i = 0; i < argc; i++) argv2[i] = argv[i];
    if (argc == 1 || argv[argc-1][0] == '-') {	/* Last arg is option */
	argv2[argc] = cur->doc->url;		/* Add Home Page */
	argv2[argc+1] = NULL;
    } else {					/* Last arg was Home Page */
	argv2[argc-1] = cur->doc->url;		/* Replace it */
	argv2[argc] = NULL;
    }
    switch (fork()) {
    case -1:
	XtAppWarning(XtWidgetToApplicationContext(toplevel),
		     "Failed to start new process; system full?");
	break;
    case 0:
	tblsiz = sysconf(_SC_OPEN_MAX);		/* Close all files */
	for (c = 3; c < tblsiz; c++) (void) close(c);
	exit(execv(argv2[0], argv2));
	/* NOTREACHED */
    }
    dispose(argv2);
}



EXPORT Bool W3Aprocess(W3ADocumentInfo *doc, int method, const char
		       *data, size_t nbytes)
{
    return process(doc, method, data, nbytes, FALSE);
}



EXPORT int W3AresolveURN(const char *URN, char ***URLs)
{
    errno = ENYI;
    return -1;
}


EXPORT void W3Aevent(long id, long eventtype, void *param)
{
    int i;
    ViewerInfo *v;

    assert(param !=NULL && ((W3ADocumentInfo *) param)->url != NULL);
#if 0
    enter("W3Aevent(id=%ld, eventtype=%ld,...)\n", id, eventtype);
#ifdef DEBUG
    if (eventtype == NEW_DOCUMENT)
	debug("URL=%s\n", ((W3ADocumentInfo *) param)->url);
#endif    
#endif

    /* Send the event to all open viewers */
    v = get_first_request_rec();		/* The struct is also the ID */
    while (v) {
	if (v->viewer >= 0)
	    viewer_event(v->viewer, (long) v, id, eventtype, param);
	v = get_next_request_rec(v);
    }

    /* Send the event to all viewer classes (id = -1) */
    for (i = 0; i < nrviewers; i++)
	viewer_event(i, -1, id, eventtype, param);

    /* Send the event to all user functions */
    for (i = 0; i < nrfunctions; i++)
	if (id != userfns[i].id)
	    function_event(i, id, eventtype, param);

#if 0
    leave("W3Aevent\n");
#endif
}

EXPORT W3AWindow W3Atoplevel(void)
{
    return toplevel;
}
