
/* Marker extension by okabe */
#define MAINPROGRAM
#include "fm.h"
#include <signal.h>
#include <setjmp.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#if defined(HAVE_WAITPID) || defined(HAVE_WAIT3)
#include <sys/wait.h>
#endif
#include <errno.h>
#include "terms.h"
#include "myctype.h"

#define DSTR_LEN	256

static char *config_filename = NULL;

typedef struct {
    unsigned char cmd, user_data_type;
    void *user_data;
} Event;
#define N_EVENT_QUEUE 10
static Event eventQueue[N_EVENT_QUEUE];
static int n_event_queue;

static Str errlog;
static char errlog_mode[] = "w+";

#ifdef USE_MARK
static char *MarkString = NULL;
#endif
static char *SearchString = NULL;
int (*searchRoutine) (Buffer *, char *, int);
void set_buffer_environ(Buffer*);
static void _goLine(char *);

JMP_BUF IntReturn;

static void cmd_loadfile(char *path, char *url, Phase0Env *p0env);
static void cmd_loadURL(char *url, ParsedURL *current, char *referer, Buffer *repbuf, Phase0Env *p0env);
static void cmd_loadBuffer(Buffer *buf, int prop, int linkid);
static void keyPressEventProc(int c);
int show_params_p = 0;
void show_params(FILE * fp);

static char *getCurWord(Buffer *buf, int *spos, int *epos, const char *badchars);

static void dump_source(Buffer *);
static void dump_head(Buffer *, Buffer *);
static void dump_extra(Buffer *
#ifdef MANY_CHARSET
		       , const char *
#endif
		       );
int prec_num = 0;
int prev_key = 0;

#define PREC_NUM (prec_num ? prec_num : 1)
#define PREC_LIMIT 10000

#include "gcmain.c"

void
setup_dump(int flag)
{
    main_p0env.flag &= ~(RG_HALFDUMP_MASK | RG_DUMP_MASK
#ifdef USE_IMAGE
			 | RG_IMAGE | RG_SINGLE_ROW_IMAGE
#endif
			 );

    if (flag)
	main_p0env.flag &= ~RG_PROC_MASK;

    main_p0env.flag |= flag & (RG_HALFDUMP_MASK | RG_DUMP_MASK
#ifdef USE_IMAGE
			       | RG_IMAGE | RG_SINGLE_ROW_IMAGE
#endif
			       );

    if (COLS == 0)
	COLS = 80;

    if (LINES == 0) {
	LINES = 25;
	INPUTLINE = LINES - 1;
	LASTLINE = INPUTLINE - 1;
    }
}

#define DUMP_KEYEQ(s, n, key) ((n) == sizeof(key) - 1 && !strncasecmp(s, key, n))

static int
interpret_dump(char *s)
{
    int flag;

    for (flag = 0 ;;) {
	size_t n;

	if ((n = strcspn(s, ",")) > 0) {
	    if (DUMP_KEYEQ(s, n, "head"))
		flag |= RG_DUMP_HEAD;
	    else if (DUMP_KEYEQ(s, n, "source")) {
		flag &= ~(RG_DUMP_BODY_MASK | RG_HALFDUMP_MASK);
		flag |= RG_DUMP_BODY_SOURCE;
	    }
	    else if (DUMP_KEYEQ(s, n, "decode")) {
		flag &= ~(RG_DUMP_BODY_MASK | RG_HALFDUMP_MASK);
		flag |= RG_DUMP_BODY_DECODED;
	    }
	    else if (DUMP_KEYEQ(s, n, "buffer")) {
		flag &= ~(RG_DUMP_BODY_MASK | RG_HALFDUMP_MASK);
		flag |= RG_DUMP_BODY_BUFFER;
	    }
	    else if (DUMP_KEYEQ(s, n, "extra"))
		flag |= RG_DUMP_EXTRA;
	    else if (DUMP_KEYEQ(s, n, "half")) {
		flag &= ~(RG_DUMP_BODY_MASK | RG_HALFDUMP_MASK);
		flag |= RG_HALFDUMP_OUT_PERLINE | RG_HALFDUMP_CODECONV;
	    }
	    else if (DUMP_KEYEQ(s, n, "half-buffer")) {
		flag &= ~(RG_DUMP_BODY_MASK | RG_HALFDUMP_MASK);
		flag |= RG_HALFDUMP_OUT_SAVEBUF | RG_HALFDUMP_CODECONV;
	    }
	    else if (DUMP_KEYEQ(s, n, "image"))
		flag |= RG_IMAGE;
	    else if (DUMP_KEYEQ(s, n, "single-row-image"))
		flag |= RG_SINGLE_ROW_IMAGE;
	}

	if (!s[n])
	    break;

	s += n + 1;
    }

    return flag;
}

#define help() fusage(stdout, 0)
#define usage() fusage(stderr, 1)

static void
fversion(FILE * f)
{
    fprintf(f, "w3m version %s, options %s\n", w3m_version,
#if LANG == JA
	    "lang=ja"
#ifdef KANJI_SYMBOLS
	    ",kanji-symbols"
#endif
#elif LANG == MANY
	    "lang=many"
#ifdef KANJI_SYMBOLS
	    ",kanji-symbols"
#endif
#else
	    "lang=en"
#endif
#ifdef USE_IMAGE
	    ",image"
#endif
#ifdef USE_COLOR
	    ",color"
#ifdef USE_ANSI_COLOR
	    ",ansi-color"
#endif
#endif
#ifdef USE_MOUSE
	    ",mouse"
#ifdef USE_GPM
	    ",gpm"
#endif
#ifdef USE_SYSMOUSE
	    ",sysmouse"
#endif
#endif
#ifdef USE_MENU
	    ",menu"
#endif
#ifdef USE_COOKIE
	    ",cookie"
#endif
#ifdef USE_SSL
	    ",ssl"
#ifdef USE_SSL_VERIFY
	    ",ssl-verify"
#endif
#endif
#ifdef USE_W3MMAILER
	    ",w3mmailer"
#endif
#ifdef USE_NNTP
	    ",nntp"
#endif
#ifdef USE_GOPHER
	    ",gopher"
#endif
#ifdef INET6
	    ",ipv6"
#endif
#ifdef USE_ALARM
	    ",alarm"
#endif
#ifdef USE_MARK
	    ",mark"
#endif
#ifdef USE_ROMAJI
	    ",romaji"
#endif
	    );
}

static void
fusage(FILE *f, int err)
{
    fversion(f);
    fprintf(f, "usage: w3m [options] [URL or filename]\noptions:\n");
    fprintf(f, "    -t tab           set tab width\n");
    fprintf(f, "    -r               ignore backspace effect\n");
    fprintf(f, "    -l line          # of preserved line (default 10000)\n");
#ifdef JP_CHARSET
#ifndef DEBIAN /* disabled by ukai: -s is used for squeeze multi lines */
    fprintf(f, "    -e               EUC-JP\n");
    fprintf(f, "    -s               Shift_JIS\n");
    fprintf(f, "    -j               JIS\n");
#endif
    fprintf(f, "    -I e|s           document code\n");
#endif				/* JP_CHARSET */
#ifdef MANY_CHARSET
    fprintf(f, "    -I <charset>     document charset\n");
#endif
    fprintf(f, "    -B               load bookmark\n");
    fprintf(f, "    -bookmark file   specify bookmark file\n");
    fprintf(f, "    -T type          specify content-type\n");
    fprintf(f, "    -m               internet message mode\n");
    fprintf(f, "    -m=<number>      detailed internet message mode,\n");
    fprintf(f, "                     <number>:\n");
    fprintf(f, "                       bit0: examine header,\n");
    fprintf(f, "                       bit1: don't show header\n");
    fprintf(f, "    -v               visual startup mode\n");
#ifdef USE_COLOR
    fprintf(f, "    -M               monochrome display\n");
#endif				/* USE_COLOR */
    fprintf(f, "    -F               automatically render frame\n");
    fprintf(f, "    -dump=<list>\n");
    fprintf(f, "                     dump various things specified by <list> into stdout,\n");
    fprintf(f, "                     <list> is comma separated list of:\n");
    fprintf(f, "                         head: response header,\n");
    fprintf(f, "                         source: page source,\n");
    fprintf(f, "                         decode: page source after processing of encoding,\n");
    fprintf(f, "                         buffer: formatted page,\n");
    fprintf(f, "                         extra: extra page information,\n");
    fprintf(f, "                         half: half rendered image line by line,\n");
    fprintf(f, "                         half-buffer: half rendered image at once,\n");
#ifdef USE_IMAGE
    fprintf(f, "                         image: <img ...> is rendered in the same way\n");
    fprintf(f, "                              : as the case -o display_image=1,\n");
    fprintf(f, "                         single-row-image:\n");
    fprintf(f, "                              : <img ...> is rendered in the same way\n");
    fprintf(f, "                              : as the case -o display_image=1,\n");
    fprintf(f, "                              : but with single row height,\n");
#endif
    fprintf(f, "    -dump            same as \"-dump=buffer\"\n");
    fprintf(f, "    -dump_source     same as \"-dump=source\"\n");
    fprintf(f, "    -dump_head       same as \"-dump=head\"\n");
    fprintf(f, "    -dump_both       same as \"-dump=head,source\"\n");
    fprintf(f, "    -dump_extra      same as \"-dump=extra,head,source\"\n");
    fprintf(f, "    -cols width      specify terminal width (used with -dump)\n");
    fprintf(f, "    -rows height     specify terminal height (used with -dump)\n");
    fprintf(f, "    -ppc count       specify the number of pixels per character (4.0...32.0)\n");
#ifdef USE_IMAGE
    fprintf(f, "    -ppl count       specify the number of pixels per line (4.0...64.0)\n");
#endif
    fprintf(f, "    +<num>           goto <num> line\n");
    fprintf(f, "    -num             show line number\n");
    fprintf(f, "    -no-proxy        don't use proxy\n");
    fprintf(f, "    -post <file>     use POST method with <file> content\n");
    fprintf(f, "    -post_with_head <file>\n");
    fprintf(f, "                     use POST method with <file> content\n");
    fprintf(f, "                     lines in which preceding the first empty line\n");
    fprintf(f, "                     are analyzed as header\n");
    fprintf(f, "    -header string   insert string as a header\n");
    fprintf(f, "    -header_from <file>\n");
    fprintf(f, "                     lines in <file> are analyzed as header\n");
    fprintf(f, "    -request [<method>:]<file>\n");
    fprintf(f, "                     use <method> method with <file> content\n");
    fprintf(f, "                     lines in which preceding the first empty line\n");
    fprintf(f, "                     are analyzed as header\n");
#ifdef USE_MOUSE
    fprintf(f, "    -no-mouse        don't use mouse\n");
#endif				/* USE_MOUSE */
#ifdef USE_COOKIE
    fprintf(f, "    -cookie          use cookie (-no-cookie: don't use cookie)\n");
#endif				/* USE_COOKIE */
    fprintf(f, "    -pauth user:pass proxy authentication\n");
#ifndef KANJI_SYMBOLS
    fprintf(f, "    -no-graph        don't use graphic character\n");
#endif				/* not KANJI_SYMBOLS */
#ifdef DEBIAN /* replaced by ukai: pager requires -s */
    fprintf(f, "    -s               squeeze multiple blank lines\n");
#else
    fprintf(f, "    -S               squeeze multiple blank lines\n");
#endif
    fprintf(f, "    -W               toggle wrap search mode\n");
    fprintf(f, "    -X               don't use termcap init/deinit\n");
    fprintf(f, "    -title[=TERM]    set buffer name to terminal title string\n");
    fprintf(f, "    -error_log file  specify file to log stderr\n");
    fprintf(f, "    -o opt=value     assign value to config option\n");
    fprintf(f, "    -show-option     print all config options\n");
    fprintf(f, "    -config file     specify config file\n");
    fprintf(f, "    -tmp_dir dir     specify directory for temporary files\n");
    fprintf(f, "    -help            print this usage message\n");
    fprintf(f, "    -version         print w3m version\n");
    fprintf(f, "    -debug           DO NOT USE\n");
    if (show_params_p)
	show_params(f);
    w3m_exit(err);
}

static void *
wrap_malloc(size_t size)
{
    return GC_MALLOC(size);
}

static void *
wrap_malloc_atomic(size_t size)
{
    return GC_MALLOC_ATOMIC(size);
}

static void *
wrap_realloc(void *old, size_t size)
{
    return old ? GC_REALLOC(old, size) : GC_MALLOC(size);
}

static void
call_main_func(int c)
{
    if (NStroke < K_LEN_MAX) {
	FuncList *fl;

	++NStroke;
	CurrentKey = K_GEN(CurrentKey, c);

	if ((
#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
	     (Currentbuf && Currentbuf->menu && (fl = lookupKey(CurrentKey, Currentbuf->menu->keymap))) ||
#endif
	     (fl = lookupKey(CurrentKey, w3mFuncKeyTabList))
	     ) &&
	    ((Currentbuf && Currentbuf != NO_BUFFER) ||
	     (fl - w3mFuncList) == FUNCNAME_quitfm || (fl - w3mFuncList) == FUNCNAME_qquitfm))
	    fl->func.main_func();
    }
}

static void
call_main_func_nostroke(int c)
{
    FuncList *fl;

    CurrentKey = K_OVER(CurrentKey, c);

    if (
#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
	(Currentbuf && Currentbuf->menu && (fl = lookupKey(CurrentKey, Currentbuf->menu->keymap))) ||
#endif
	(fl = lookupKey(CurrentKey, w3mFuncKeyTabList)))
	fl->func.main_func();
}

void
nextchar(void)
{
    call_main_func(getch());
}

Phase0Env main_p0env = {displayBufferMaybe, NULL, RG_ON_TARGET};

Hist HistV[HistV_size] = {{NULL}};

#ifdef	SIGCHLD

#define SIGCHLD_REPORT(pid, p_stat) \
do { \
    if ((p_stat)) \
	fprintf(stderr, "something wrong (maybe): pid=%ld, status=0x%X\n", (long)(pid), (p_stat)); \
} while (0)

static MySignalHandler
sig_chld(SIGNAL_ARG)
{
    int p_stat;
#ifdef HAVE_WAITPID
    pid_t pid;

    while ((pid = waitpid(-1, &p_stat, WNOHANG)) > 0) {
#ifdef USE_IMAGE
	termImage(pid);
#endif
#ifdef USE_ROMAJI
	init_romaji_filter(pid);
#endif
	SIGCHLD_REPORT(pid, p_stat);
    }
#elif defined(HAVE_WAIT3)
    int pid;

    while ((pid = wait3(&p_stat, WNOHANG, NULL)) > 0) {
#ifdef USE_IMAGE
	termImage(pid);
#endif
	SIGCHLD_REPORT(pid, p_stat);
    }
#else
    pid_t pid;

    pid = wait(&p_stat);
#ifdef USE_IMAGE
    termImage(pid);
#endif
    SIGCHLD_REPORT(pid, p_stat);
#endif
    signal(SIGCHLD, sig_chld);
    SIGNAL_RETURN;
}
#endif

Buffer *
loadVisualStartPage(char *title)
{
    Phase0Env p0env;
    Str s_page;
    Buffer *buf;

    p0env = main_p0env;
    p0env.flag &= ~RG_PROC_MASK;
    s_page = Sprintf("<title>%s</title><center><b>Welcome to ", title);
    Strcat_charp(s_page, "<a href='http://w3m.sourceforge.net/'>");
    Strcat_m_charp(s_page,
		   "w3m</a>!<p><p>This is w3m version ",
		   w3m_version,
		   "<br>Written by <a href='mailto:aito@fw.ipsj.or.jp'>Akinori Ito</a>",
		   NULL);
#ifdef DEBIAN
    Strcat_charp(s_page,
		 "<p>Debian package is maintained by <a href='mailto:ukai@debian.or.jp'>Fumitoshi UKAI</a>."
		 "You can read <a href='file:///usr/share/doc/w3m/'>w3m documents on your local system</a>.");
#endif				/* DEBIAN */
    Strcat_charp(s_page,
		 "<p>..., and somewhat "
		 "<a href=\"http://pub.ks-and-ks.ne.jp/prog/w3mmee/\">enhanced</a> "
		 "by <a href=\"mailto:suto@ks-and-ks.ne.jp\">Kiyokazu SUTO</a>.</p>"
		 "<p>Please look at"
		 " <a href=\"file://" HELP_DIR "/config.shtml.en\">config.shtml.en</a> (document in english), or"
		 " <a href=\"file://" HELP_DIR "/config.shtml.ja\">config.shtml.ja</a> (document in japanese).");
    if ((buf = loadHTMLString(s_page, NULL, &p0env)))
	buf->bufferprop |= BP_INTERNAL;
    return buf;
}

static void
make_optional_header_string(char *s)
{
    char *e;

    SKIP_BLANKS(s);

    for (e = s + strlen(s) ; e > s && IS_SPACE(e[-1]) ; --e)
	;

    pushText(&ExtraHTTPRequestHeaderList, s);
}

static Str
parse_http_method(char **pp)
{
    char *p;
    Str method;

    for (p = *pp ; !IS_HTTP_TOKEN_SEP(*p) ; ++p)
	;

    if (p > *pp && *p == ':') {
	method = Strnew_charp_n(*pp, p - *pp);
	Strupper(method);
	*pp = p + 1;
	return method;
    }
    else
	return NULL;
}

static void
make_optional_header_from(FILE *fp, int with_body)
{
    Str h = NULL, l;

    while ((l = Strfgets(fp))->length) {
	Strchop(l);
	if (!l->length && with_body)
	    break;
	if (h)
	    switch (l->ptr[0]) {
	    case '\t':
	    case ' ':
		Strcat_charp(h, "\r\n");
		Strcat(h, l);
		continue;
	    default:
		make_optional_header_string(h->ptr);
		break;
	    }
	h = l->length ? l : NULL;
    }
    if (h)
	make_optional_header_string(h->ptr);
}

void
main_loop(int (*test_func)(void))
{
    CookedEvent cev;
    char c;
    int i;
    Buffer *newbuf;

    prev_key = CurrentKey;
    NStroke = CurrentKey = 0;

    while (!test_func || test_func()) {
	/* event processing */
	if (Currentbuf && n_event_queue > 0) {
	    for (i = 0; i < n_event_queue; i++) {
		NStroke = CurrentKey = 0;
		CurrentKeyData = eventQueue[i].user_data;
		ForcedKeyData = NULL;
		TargetX = TargetY = PrevTargetX = PrevTargetY = -1;
		Argumentbuf = NULL;
		CurrentCmdData = NULL;
		w3mFuncList[eventQueue[i].cmd].func.main_func();
	    }
	    n_event_queue = 0;
	    ForcedKeyData = CurrentKeyData = NULL;
	    TargetX = TargetY = PrevTargetX = PrevTargetY = -1;
	    Argumentbuf = NULL;
	    CurrentCmdData = NULL;
	}
	processBufferEvents(Currentbuf);
#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
	if (Currentbuf && Currentbuf->currentLine &&
	    Currentbuf->currentLine->menu_item &&
	    Currentbuf->currentLine->menu_item->type & MENU_ONSEL)
	    Currentbuf->currentLine->menu_item->func();
#endif
	/* get keypress event */
#ifdef USE_MOUSE
	if (use_mouse)
	    mouse_active();
#endif				/* USE_MOUSE */
	if (getevent(&cev)) {
	    dont_buffername = FALSE;
	    if (!checkCurrentbuf()) {
		if ((newbuf = loadVisualStartPage(FALLBACK_PAGE_TITLE))) {
		    pushBuffer(newbuf);
		    displayBuffer(Currentbuf, B_FORCE_REDRAW);
		}
	    }
#ifdef USE_MOUSE
	    if (use_mouse) {
		mouse_inactive();
#if defined(USE_GPM) || defined(USE_SYSMOUSE)
		if (cev.type == cooked_event_mouse) {
		    process_mouse(cev.what, cev.x, cev.y);
		    goto next_event_loop;
		}
#endif
	    }	      
#endif				/* USE_MOUSE */
	    c = cev.what;
	    if (IS_ASCII(c)) {	/* Ascii */
		if (((prec_num && c == '0') || '1' <= c) && (c <= '9')) {
		    prec_num = prec_num * 10 + (int) (c - '0');
		    if (prec_num > PREC_LIMIT)
			prec_num = PREC_LIMIT;
		}
		else {
		    keyPressEventProc((int) c);
		    prec_num = 0;
		}
	    }
#if defined(USE_MOUSE) && (defined(USE_GPM) || defined(USE_SYSMOUSE))
	next_event_loop:
#endif				/* USE_MOUSE */
	    prev_key = CurrentKey;
	    NStroke = CurrentKey = 0;
	}
    }
}

int
MAIN(int argc, char **argv, char **envp)
{
    Buffer *newbuf = NULL;
    char *p;
    int i, x;
    InputStream redin;
    char *line_str = NULL;
    char **load_argv;
    FormList *request;
    int load_argc = 0;
    int load_bookmark = FALSE;
    int visual_start = FALSE;
    int nasync = 0;
    char search_header = FALSE;
    char *default_type = NULL;
    char *request_file = NULL;
    Str request_method = NULL;
    int request_with_head = FALSE;
    Str err_msg;
#ifdef JP_CHARSET
    char c;
#endif

    alt_set_allocater(wrap_malloc);
    alt_set_atomic_allocater(wrap_malloc_atomic);
    alt_set_reallocater(wrap_realloc);
    alt_set_freer(NULL);
#if !defined(JP_CHARSET) && defined(LOCALE_DIR)
    setlocale(LC_MESSAGES, "");
    textdomain(W3M_TEXTDOMAIN);
    bindtextdomain(W3M_TEXTDOMAIN, LOCALE_DIR);
#endif
#ifdef MANY_CHARSET
    mb_setup_fallback_decoder(conv_default_decoder);
    setup_locale_charset();
    w3m_version = Strnew_m_charp(w3m_version, "+moe-", mb_version_string, NULL)->ptr;
#endif
#ifndef HAVE_SYS_ERRLIST
    prepare_sys_errlist();
#endif				/* not HAVE_SYS_ERRLIST */

    NO_proxy_domains = newTextList();
    fileToDelete = newTextList();

    myname = allocStr(argv[0], -1);

    load_argv = New_N(char *, argc - 1);
    load_argc = 0;

    CurrentDir = currentdir();
    BookmarkFile = NULL;
    rc_dir = expandName(RC_DIR);
    tmp_dir = expandName(TMP_DIR);
    i = strlen(rc_dir);
    if (i > 1 && rc_dir[i - 1] == '/')
	rc_dir[i - 1] = '\0';
    config_filename = rcFile(CONFIG_FILE);

    /* argument search 1 */
    for (i = 1; i < argc; i++) {
	if (*argv[i] == '-') {
	    if (!strcmp("-config", argv[i])) {
		argv[i] = "-dummy";
		if (++i >= argc)
		    usage();
		config_filename = argv[i];
		argv[i] = "-dummy";
	    }
	    if (!strcmp("-tmp_dir", argv[i])) {
		argv[i] = "-dummy";
		if (++i >= argc)
		    usage();
		tmp_dir = expandName(argv[i]);
		argv[i] = "-dummy";
	    }
	    else if (!strcmp("-h", argv[i]) || !strcmp("-help", argv[i]))
		help();
	    else if (!strcmp("-V", argv[i]) || !strcmp("-version", argv[i])) {
		fversion(stdout);
		exit(0);
	    }
	}
    }

    /* initializations */
    init_rc(config_filename);
    newHist(LoadHist);
    newHist(SaveHist);
    newHist(ShellHist);
    newHist(TextHist);
    newHist(URLHist);
    newHist(FuncHist);
#ifdef USE_HISTORY
    loadHistory(URLHist);
#endif				/* not USE_HISTORY */

    if (!non_null(HTTP_proxy) &&
	(((p = getenv("HTTP_PROXY")) && *p) ||
	 ((p = getenv("http_proxy")) && *p) ||
	 ((p = getenv("HTTP_proxy")) && *p)))
	HTTP_proxy = p;
#ifdef USE_SSL
    if (!non_null(HTTPS_proxy) &&
	((p = getenv("HTTPS_PROXY")) ||
	 (p = getenv("https_proxy")) || (p = getenv("HTTPS_proxy"))))
	HTTPS_proxy = p;
    if (HTTPS_proxy == NULL && non_null(HTTP_proxy))
	HTTPS_proxy = HTTP_proxy;
#endif				/* USE_SSL */
#ifdef USE_GOPHER
    if (!non_null(GOPHER_proxy) &&
	(((p = getenv("GOPHER_PROXY")) && *p) ||
	 ((p = getenv("gopher_proxy")) && *p) ||
	 ((p = getenv("GOPHER_proxy")) && *p)))
	GOPHER_proxy = p;
#endif				/* USE_GOPHER */
    if (!non_null(FTP_proxy) &&
	(((p = getenv("FTP_PROXY")) && *p) ||
	 ((p = getenv("ftp_proxy")) && *p) ||
	 ((p = getenv("FTP_proxy")) && *p)))
	FTP_proxy = p;
    if (!non_null(NO_proxy) &&
	(((p = getenv("NO_PROXY")) && *p) ||
	 ((p = getenv("no_proxy")) && *p) ||
	 ((p = getenv("NO_proxy")) && *p)))
	NO_proxy = p;

    /* argument search 2 */
    i = 1;
    while (i < argc) {
	if (*argv[i] == '-') {
	    if (!strcmp("-t", argv[i])) {
		if (++i >= argc)
		    usage();
		if (atoi(argv[i]) > 0)
		    Tabstop = atoi(argv[i]);
	    }
	    else if (!strcmp("-r", argv[i]))
		ShowEffect = FALSE;
	    else if (!strcmp("-l", argv[i])) {
		if (++i >= argc)
		    usage();
		if ((x = atoi(argv[i])) > 0)
		    PagerMax = x;
	    }
#ifdef JP_CHARSET
#ifndef DEBIAN /* XXX: use -o kanjicode={S|J|E} */
	    else if (!strcmp("-s", argv[i]))
		DisplayCode = CODE_SJIS;
	    else if (!strcmp("-j", argv[i]))
		DisplayCode = CODE_JIS_n;
	    else if (!strcmp("-e", argv[i]))
		DisplayCode = CODE_EUC;
#endif
	    else if (!strncmp("-I", argv[i], 2)) {
		if (argv[i][2])
		    argv[i] += 2;
		else if (++i >= argc)
		    usage();
		c = str_to_code(argv[i]);
		switch (c) {
		case CODE_EUC:
		case CODE_SJIS:
		case CODE_INNER_EUC:
		    main_p0env.DocumentCode = c;
		    UseContentCharset = FALSE;
		    UseAutoDetect = FALSE;
		    break;
		default:
		    main_p0env.DocumentCode = '\0';
		    UseContentCharset = TRUE;
		    UseAutoDetect = TRUE;
		    break;
		}
	    }
	    else if (!strncmp("-O", argv[i], 2)) {
		if (argv[i][2])
		    argv[i] += 2;
		else if (++i >= argc)
		    usage();
		c = str_to_code(argv[i]);
		if (c != CODE_INNER_EUC && c != CODE_ASCII)
		    DisplayCode = c;
	    }
#endif				/* JP_CHARSET */
#ifdef MANY_CHARSET
	    else if (!strcmp("-I", argv[i])) {
		if (++i >= argc)
		    usage();
		main_p0env.default_content_charset = argv[i];
	    }
#endif
#ifndef KANJI_SYMBOLS
	    else if (!strcmp("-no-graph", argv[i]))
		no_graphic_char = TRUE;
#endif				/* not KANJI_SYMBOLS */
	    else if (!strcmp("-T", argv[i])) {
		if (++i >= argc)
		    usage();
		main_p0env.DefaultType = default_type = argv[i];
	    }
	    else if (!strcmp("-m", argv[i]))
		main_p0env.SearchHeader = search_header = TRUE;
	    else if (!strncmp("-m=", argv[i], sizeof("-m=") - 1))
		main_p0env.SearchHeader = search_header = atoi(&argv[i][sizeof("-m=") - 1]);
	    else if (!strcmp("-v", argv[i]))
		visual_start = TRUE;
#ifdef USE_COLOR
	    else if (!strcmp("-M", argv[i]))
		useColor = FALSE;
#endif				/* USE_COLOR */
	    else if (!strcmp("-B", argv[i]))
		load_bookmark = TRUE;
	    else if (!strcmp("-bookmark", argv[i])) {
		if (++i >= argc)
		    usage();
		BookmarkFile = argv[i];
		if (BookmarkFile[0] != '~' && BookmarkFile[0] != '/') {
		    Str tmp = Strnew_charp(CurrentDir);
		    if (Strlastchar(tmp) != '/')
			Strcat_char(tmp, '/');
		    Strcat_charp(tmp, BookmarkFile);
		    BookmarkFile = cleanupName(tmp->ptr);
		}
	    }
	    else if (!strcmp("-F", argv[i]))
		main_p0env.RenderFrame = TRUE;
	    else if (!strcmp("-W", argv[i])) {
		if (WrapDefault)
		    WrapDefault = FALSE;
		else
		    WrapDefault = TRUE;
	    }
	    else if (!strcmp("-dump", argv[i])) {
		setup_dump(RG_DUMP_BODY_BUFFER);
	    }
	    else if (!strncmp("-dump=", argv[i], sizeof("-dump=") - 1)) {
		setup_dump(interpret_dump(&argv[i][sizeof("-dump=") - 1]));
	    }
	    else if (!strcmp("-dump_source", argv[i])) {
		setup_dump(RG_DUMP_BODY_SOURCE);
	    }
	    else if (!strcmp("-dump_both", argv[i])) {
		setup_dump(RG_DUMP_HEAD | RG_DUMP_BODY_SOURCE);
	    }
	    else if (!strcmp("-dump_head", argv[i])) {
		setup_dump(RG_DUMP_HEAD);
	    }
	    else if (!strcmp("-dump_extra", argv[i])) {
		setup_dump(RG_DUMP_HEAD | RG_DUMP_BODY_SOURCE | RG_DUMP_EXTRA);
	    }
	    else if (!strcmp("-halfdump", argv[i])) {
		setup_dump(RG_HALFDUMP_OUT_PERLINE);
	    }
	    else if (!strcmp("-halfload", argv[i])) {
		w3m_halfload = TRUE;
		main_p0env.flag &= ~(RG_HALFDUMP_MASK | RG_DUMP_MASK);
	    }
	    else if (!strcmp("-cols", argv[i])) {
		if (++i >= argc)
		    usage();
		COLS = atoi(argv[i]);
	    }
	    else if (!strcmp("-rows", argv[i])) {
		if (++i >= argc)
		    usage();
		LINES = atoi(argv[i]);
		INPUTLINE = LINES - 1;
		LASTLINE = INPUTLINE - 1;
	    }
	    else if (!strcmp("-ppc", argv[i])) {
		double ppc;
		if (++i >= argc)
		    usage();
		ppc = atof(argv[i]);
		if (ppc >= MINIMUM_PIXEL_PER_CHAR &&
		    ppc <= MAXIMUM_PIXEL_PER_CHAR) {
		    pixel_per_char = ppc;
#ifdef USE_IMAGE
		    auto_pixel_per_char = FALSE;
#endif
		}
	    }
	    else if (!strcmp("-ppl", argv[i])) {
		double ppc;
		if (++i >= argc)
		    usage();
		ppc = atof(argv[i]);
		if (ppc >= MINIMUM_PIXEL_PER_CHAR &&
		    ppc <= MAXIMUM_PIXEL_PER_CHAR * 2) {
		    pixel_per_line = ppc;
#ifdef USE_IMAGE
		    auto_pixel_per_line = FALSE;
#endif
		}
	    }
	    else if (!strcmp("-num", argv[i]))
		showLineNum = TRUE;
	    else if (!strcmp("-no-proxy", argv[i]))
		Do_not_use_proxy = TRUE;
	    else if (!strcmp("-post", argv[i])) {
		if (++i >= argc)
		    usage();
		request_method = Strnew_charp("POST");
		request_file = argv[i];
		request_with_head = FALSE;
	    }
	    else if (!strcmp("-post_with_head", argv[i])) {
		if (++i >= argc)
		    usage();
		request_method = Strnew_charp("POST");
		request_file = argv[i];
		request_with_head = TRUE;
	    }
	    else if (!strcmp("-request", argv[i])) {
		if (++i >= argc)
		    usage();
		request_file = argv[i];

		if (!(request_method = parse_http_method(&request_file))) {
		    request_file = argv[i];
		    request_method = Strnew_charp("GET");
		}

		request_with_head = TRUE;
	    }
 	    else if (!strcmp("-header", argv[i])) {
 		if (++i >= argc)
 		    usage();
		make_optional_header_string(argv[i]);
 	    }
 	    else if (!strcmp("-header_from", argv[i])) {
		FILE *fp;

 		if (++i >= argc)
 		    usage();
		if (!strcmp(argv[i], "-"))
		    fp = stdin;
		else
		    fp = fopen(argv[i], "r");
		if (fp) {
		    make_optional_header_from(fp, FALSE);
		    if (fp != stdin)
			fclose(fp);
		}
		else
		    fprintf(stderr, "w3m: fopen(\"%s\", \"r\"): %s\n", argv[i], strerror(errno));
 	    }
#ifdef USE_MOUSE
	    else if (!strcmp("-no-mouse", argv[i])) {
		use_mouse = FALSE;
	    }
#endif				/* USE_MOUSE */
#ifdef USE_COOKIE
	    else if (!strcmp("-no-cookie", argv[i])) {
		use_cookie = FALSE;
		accept_cookie = FALSE;
	    }
	    else if (!strcmp("-cookie", argv[i])) {
		use_cookie = TRUE;
		accept_cookie = TRUE;
	    }
#endif				/* USE_COOKIE */
	    else if (!strcmp("-pauth", argv[i])) {
		if (++i >= argc)
		    usage();
		proxy_auth_cookie = Strnew_charp("Basic ");
		Strcat(proxy_auth_cookie, encodeB(argv[i]));
		while (argv[i][0]) {
		    argv[i][0] = '\0';
		    argv[i]++;
		}
	    }
#ifdef DEBIAN
	    else if (!strcmp("-s", argv[i]))
#else
		else if (!strcmp("-S", argv[i]))
#endif
		    squeezeBlankLine = TRUE;
	    else if (!strcmp("-X", argv[i]))
		Do_not_use_ti_te = TRUE;
	    else if (!strcmp("-title", argv[i]))
		displayTitleTerm = getenv("TERM");
	    else if (!strncmp("-title=", argv[i], 7))
		displayTitleTerm = argv[i] + 7;
	    else if (!strcmp("-error_log", argv[i])) {
		if (++i >= argc)
		    usage();
		errlog = Strnew_charp(argv[i]);
		errlog_mode[0] = 'a';
	    }
	    else if (!strcmp("-o", argv[i]) ||
		     !strcmp("-show-option", argv[i])) {
		if (!strcmp("-show-option", argv[i]) || ++i >= argc ||
		    !strcmp(argv[i], "?")) {
		    show_params(stdout);
		    exit(0);
		}
		if (!set_param_option(argv[i])) {
		    /* option set failed */
		    fprintf(stderr, "%s: bad option\n", argv[i]);
		    show_params_p = 1;
		    usage();
		}
	    }
	    else if (!strcmp("-dummy", argv[i])) {
		/* do nothing */
	    }
	    else if (!strcmp("-debug", argv[i]))
		w3m_debug = TRUE;
	    else {
		usage();
	    }
	}
	else if (*argv[i] == '+') {
	    line_str = argv[i] + 1;
	}
	else {
	    load_argv[load_argc++] = argv[i];
	}
	i++;
    }

#ifdef MANY_CHARSET
    rc_finish();
#endif
    sync_with_option();
#ifdef USE_COOKIE
    initCookie();
#endif				/* USE_COOKIE */
    setLocalCookie();		/* setup cookie for local CGI */

#ifdef	__WATT32__
    if (w3m_debug)
	dbug_init();
    sock_init();
#endif

    TopViewList.top = TopViewList.bot = NULL;
    Firstbuf = Currentbuf = NULL;
    NStroke = CurrentKey = 0;
    if (BookmarkFile == NULL)
	BookmarkFile = rcFile(BOOKMARK);

    if (!isatty(1) && !(main_p0env.flag & (RG_HALFDUMP_MASK | RG_DUMP_MASK))) {
	/* redirected output */
	setup_dump(RG_DUMP_BODY_BUFFER);
    }
#ifdef	SIGCHLD
    signal(SIGCHLD, sig_chld);
#endif
#ifdef USE_BINMODE_STREAM
    setmode(fileno(stdout), O_BINARY);
#endif
    if (!(main_p0env.flag & (RG_HALFDUMP_MASK | RG_DUMP_MASK))) {
	fmInit(0);

	if (!errlog)
	    errlog = tmpfname(TMPF_DFL, ".txt");

	if (freopen(errlog->ptr, errlog_mode, stderr)) {
	    char *newbuf;

	    newbuf = GC_MALLOC_ATOMIC(BUFSIZ);
	    setvbuf(stderr, newbuf, _IOLBF, BUFSIZ);

	    if (errlog_mode[0] == 'w')
		unlink(errlog->ptr);
	}

#ifdef SIGWINCH
	signal(SIGWINCH, resize_hook);
#else				/* not SIGWINCH */
	setlinescols();
	setupscreen();
#endif				/* not SIGWINCH */
	initKeymap(TRUE);
#ifdef USE_MENU
	initMenu();
#endif
    }
    if (main_p0env.flag & (RG_HALFDUMP_MASK | RG_DUMP_MASK)) {
	main_p0env.flag &= ~RG_PROC_MASK;
#ifdef USE_IMAGE
	main_p0env.displayImage = FALSE;
#endif
    }

    err_msg = Strnew();
    if (load_argc == 0) {
	Phase0Env p0env;

	p0env = main_p0env;
	p0env.flag &= ~RG_PROC_MASK;
	if (!isatty(0)) {
	    redin = newFileStream(fdopen(dup(0), "rb"), (void (*)()) pclose);
	    p0env.flag |= main_p0env.flag & RG_PROC_MASK;
	    newbuf = openGeneralPagerBuffer(redin, &p0env);
	    dup2(1, 0);
	}
	else if (load_bookmark) {
	    newbuf = loadGeneralFile(BookmarkFile, NULL, NO_REFERER, &p0env, NULL);
	    if (newbuf == NULL)
		Strcat_charp(err_msg, "w3m: Can't load bookmark\n");
	}
	else if (visual_start) {
	    newbuf = loadVisualStartPage(STARTUP_PAGE_TITLE);
	    if (newbuf == NULL)
		Strcat_charp(err_msg, "w3m: Can't load visual start page\n");
	    else if (newbuf != NO_BUFFER)
 		newbuf->bufferprop |= (BP_INTERNAL | BP_NO_URL);
	}
	else if (((p = getenv("HTTP_HOME")) != NULL && *p) ||
		 ((p = getenv("WWW_HOME")) != NULL && *p)) {
	    p0env.flag |= (main_p0env.flag & RG_PROC_MASK) | RG_HIST;
	    newbuf = loadGeneralFile(p, NULL, NO_REFERER, &p0env, NULL);
	    if (newbuf == NULL)
		Strcat(err_msg, Sprintf("w3m: Can't load \"%s\"\n", p));
	    else if (newbuf == NO_BUFFER || newbuf->async_buf) {
		ParsedURL pu;

		parseURL2(p, &pu, NULL);
		pushHashHist(URLHist, parsedURL2Str(&pu)->ptr);
	    }
	    else
		pushHashHist(URLHist, parsedURL2Str(&newbuf->currentURL)->ptr);
	}
	else {
	    if (fmInitialized)
		fmTerm(0);
	    usage();
	}
	if (newbuf == NULL) {
	    if (fmInitialized)
		fmTerm(0);
	    if (err_msg->length)
		Strfputs(err_msg, stderr);
	    w3m_exit(2);
	}
	i = -1;
    }
    else {
	i = 0;
    }
    for (; i < load_argc; i++) {
	if (i >= 0) {
	    main_p0env.SearchHeader = search_header;
	    main_p0env.DefaultType = default_type;
	    if ((main_p0env.flag & RG_DUMP_MASK) == RG_DUMP_HEAD) {
		request = New(FormList);
		request->method = FORM_METHOD_HEAD;
		newbuf = loadGeneralFile(load_argv[i], NULL, NO_REFERER, &main_p0env, request);
	    }
	    else {
 	        if (request_file) {
		    FILE *fp;
		    Str body;
		    if (!strcmp(request_file, "-"))
		        fp = stdin;
		    else
		        fp = fopen(request_file, "r");
		    if (fp == NULL) {
		        fprintf(stderr, "w3m: fopen(\"%s\", \"r\"): %s\n", request_file, strerror(errno));
			continue;
		    }
		    if (request_with_head) {
			make_optional_header_from(fp, TRUE);
			sync_with_header();
		    }
		    body = Strfgetall(fp);
		    if (fp != stdin)
			fclose(fp);
 		    request = newFormList(NULL, request_method->ptr, NULL, NULL, NULL, NULL, NULL);
 		    request->body = body->ptr;
 		    request->boundary = NULL;
 		    request->length = strlen (body->ptr);

		    if (request->method == FORM_METHOD_GET &&
			(request_method->length != sizeof("GET") - 1 ||
			 memcmp(request_method->ptr, "GET", sizeof("GET"))))
			request->method_str = request_method;
 		}
 		else {
		    request = NULL;
 		}

 		newbuf = loadGeneralFile(load_argv[i], NULL, NO_REFERER, &main_p0env, request);
	    }
	    if (newbuf == NULL) {
		Strcat(err_msg, Sprintf("w3m: Can't load \"%s\".\n", load_argv[i]));
		continue;
	    }
	    else if (newbuf == NO_BUFFER || newbuf->async_buf) {
		ParsedURL pu;

		parseURL2(load_argv[i], &pu, NULL);
		pushHashHist(URLHist, parsedURL2Str(&pu)->ptr);

		if (newbuf->async_buf)
		    ++nasync;

		continue;
	    }
	    switch (newbuf->currentURL.scheme) {
#ifdef USE_NNTP
	    case SCM_NNTP:
	    case SCM_NEWS:
#endif				/* USE_NNTP */
	    case SCM_MAILTO:
		break;
	    case SCM_LOCAL:
	    case SCM_LOCAL_CGI:
		unshiftHist(LoadHist, conv_from_system(load_argv[i]));
	    default:
		pushHashHist(URLHist, parsedURL2Str(&newbuf->currentURL)->ptr);
		break;
	    }
	}
	if (newbuf->async_buf) {
	    ++nasync;
	    continue;
	}
	else if (newbuf == NO_BUFFER)
	    continue;
	if (main_p0env.flag & RG_HALFDUMP_MASK) {
	    pushBuffer(newbuf);

	    if ((main_p0env.flag & RG_HALFDUMP_OUT_MASK) == RG_HALFDUMP_OUT_PERLINE) {
#ifdef MANY_CHARSET
		mb_fflush(stdout)
#endif
		    ;
	    }
	    else {
		if (main_p0env.RenderFrame && newbuf->frameset) {
		    Buffer *fbuf;

		    if ((fbuf = rFrame_internal(newbuf, &main_p0env)) && !fbuf->async_buf)
			pushBuffer(fbuf);
		}
		if (Currentbuf->view && Currentbuf->view->root->frameset) {
		    Buffer *fbuf;

		    if ((fbuf = frameViewBuffer(Currentbuf->view->root)))
			pushBuffer(fbuf);
		}
		saveBuffer(Currentbuf, stdout);
	    }
	    w3m_exit(0);
	}
	pushBuffer(newbuf);
	if (main_p0env.RenderFrame && newbuf->frameset) {
	    Buffer *fbuf;

	    if ((fbuf = rFrame_internal(newbuf, &main_p0env)) && !fbuf->async_buf)
		pushBuffer(fbuf);
	}
	if (main_p0env.flag & RG_DUMP_MASK) {
	    Buffer *src = NULL;

	    if (Currentbuf->view && Currentbuf->view->root->frameset) {
		Buffer *fbuf;

		if ((fbuf = frameViewBuffer(Currentbuf->view->root)))
		    pushBuffer(fbuf);
	    }
	    if ((main_p0env.flag & RG_DUMP_BODY_MASK) == RG_DUMP_BODY_DECODED) {
		Phase0Env p0env;

		p0env = main_p0env;
		p0env.flag |= RG_NOPROP;

		switch (vwSrc_internal(Currentbuf, &src, &p0env)) {
		default:
		    if (!(src->bufferprop & BP_LINKED)) {
			insertBuffer(Currentbuf, src);
			src->bufferprop |= BP_LINKED;
		    }
		case 0:
		case 1:
		    break;
		}
	    }

	    if (main_p0env.flag & RG_DUMP_EXTRA) {
		dump_extra(Currentbuf
#ifdef MANY_CHARSET
			   , (main_p0env.flag & RG_DUMP_BODY_MASK) == RG_DUMP_BODY_DECODED ? tty_mb_w_setup.cs : NULL
#endif
			   );

		if (main_p0env.flag & RG_DUMP_HEAD)
		    dump_head(Currentbuf, src);
		else if (main_p0env.flag & RG_DUMP_BODY_MASK)
		    putchar('\n');
	    }
	    else if (main_p0env.flag & RG_DUMP_HEAD)
		dump_head(Currentbuf, src);

	    switch ((main_p0env.flag & RG_DUMP_BODY_MASK)) {
	    case RG_DUMP_BODY_DECODED:
		if (src) {
		    saveBuffer(src, stdout);
		    break;
		}
	    case RG_DUMP_BODY_SOURCE:
		dump_source(Currentbuf);
		break;
	    case RG_DUMP_BODY_BUFFER:
		saveBuffer(Currentbuf, stdout);
		break;
	    default:
		break;
	    }

	    if (src)
		discardBuffer(src);
	}
#ifdef USE_BUFINFO
	saveBufferInfo();
#endif
    }
    if (main_p0env.flag & RG_DUMP_MASK) {
#ifdef USE_COOKIE
	save_cookies();
#endif				/* USE_COOKIE */
	w3m_exit(0);
    }
    if (!nasync && !checkCurrentbuf()) {
	if (newbuf == NO_BUFFER) {
	    inputChar("Hit any key to quit w3m:");
	}
	if (fmInitialized)
	    fmTerm(0);
	if (err_msg->length)
	    Strfputs(err_msg, stderr);
	if (newbuf == NO_BUFFER)
#ifdef USE_COOKIE
	    save_cookies();
#endif                /* USE_COOKIE */
	w3m_exit(2);
    }

    if (err_msg->length)
	disp_message_nsec(err_msg->ptr, FALSE, 0, FALSE, TRUE);

    main_p0env.SearchHeader = FALSE;
    main_p0env.DefaultType = NULL;
#ifdef JP_CHARSET
    UseContentCharset = TRUE;
    UseAutoDetect = TRUE;
#endif

    if (checkCurrentbuf()) {
	if (line_str)
	    _goLine(line_str);
	displayCurrentView(NULL);
    }
    main_loop(NULL);
    return 0;
}

static void
keyPressEventProc(int c)
{
    call_main_func(c);

    if (Currentbuf)
	displayBuffer(Currentbuf, B_NORMAL);
}

void
pushEvent(int event, void *user_data)
{
    if (n_event_queue < N_EVENT_QUEUE) {
	eventQueue[n_event_queue].cmd = event;
	eventQueue[n_event_queue].user_data = user_data;
	n_event_queue++;
    }
}

static void
dump_source(Buffer * buf)
{
    FILE *f;
    char iobuf[BUFSIZ];
    size_t n;

    if (buf->sourcefile == NULL)
	return;
    f = fopen(buf->sourcefile, "rb");
    if (f == NULL)
	return;
    while ((n = fread(iobuf, 1, sizeof(iobuf), f)) > 0)
	fwrite(iobuf, 1, n, stdout);
    fclose(f);
}

long
text_buf_len(Line *lp)
{
  int len = 0;

  for (; lp ; lp = lp->next) {
    len += lp->len;

    if (lp->lineBuf[lp->len - 1] != '\n')
      ++len;
  }

  return len;
}

static void
dump_head(Buffer *buf, Buffer *src)
{
    if (buf->document_header) {
	TextListItem *ti;
	long len;

	len = (src && src->firstLine) ? text_buf_len(src->firstLine) : -1;

	for (ti = buf->document_header->first; ti; ti = ti->next)
	    if (len >= 0 &&
		!strncasecmp(ti->ptr, "content-length:", sizeof("content-length:") - 1))
		printf("Content-Length: %ld\n", len);
	    else if (!strncasecmp(ti->ptr, "connection:", sizeof("connection:") - 1) ||
		     !strncasecmp(ti->ptr, "transfer-encoding:", sizeof("transfer-encoding:") - 1) ||
		     !strncasecmp(ti->ptr, "content-transfer-encoding:", sizeof("content-transfer-encoding:") - 1) ||
		     !strncasecmp(ti->ptr, "content-encoding:", sizeof("content-encoding:") - 1))
		printf("X-W3M-%s", ti->ptr);
	    else
		fputs(ti->ptr, stdout);
    }

    puts("");
}

static void
dump_extra(Buffer *buf
#ifdef MANY_CHARSET
	   , const char *cs
#endif
	   )
{
    printf("W3M-Current-URL: %s\n",parsedURL2Str(&buf->currentURL)->ptr);

    if (buf->baseURL)
	printf("W3M-Base-URL: %s\n",parsedURL2Str(buf->baseURL)->ptr);

#ifdef MANY_CHARSET
    if (cs)
	printf("W3M-Document-Charset: %s\n", cs);
    else if (buf->document_encoding)
	printf("W3M-Document-Charset: %s\n", buf->document_encoding);
#else
#ifdef JP_CHARSET
    printf("W3M-Document-Charset: %s\n", code_to_str(buf->document_encoding));
#endif
#endif

#ifdef USE_SSL
    if (buf->ssl_certificate) {
      char *p, *q;

      for (p = q = buf->ssl_certificate ; (q = strchr(q, '\n')) ;)
	if (*++q == '\n')
	  break;

      if (q) {
	fwrite("W3M-SSL-Session: ", 1, sizeof("W3M-SSL-Session: ") - 1, stdout);
	fwrite(p, 1, q - p, stdout);

	while (*++q == '\n')
	  ;

	fwrite("W3M-SSL-", 1, sizeof("W3M-SSL-") - 1, stdout);

	for (p = q ; (q = strchr(q, '\n')) ;)
	  if (*++q == '\n' || !*q)
	    break;

	if (q)
	  fwrite(p, 1, q - p, stdout);
	else {
	  fputs(p, stdout);
	  putchar('\n');
	}
      }
    }
#endif
}

void
nulcmd(void)
{				/* do nothing */
}

#ifdef __EMX__
void
pcmap(void)
{
    w3mFuncList[(int)PcKeymap[(int)getch()]].func.main_func();
}
#else /* not __EMX__ */
void
pcmap(void)
{
}
#endif

void
escmap(void)
{
    char c;

    c = getch();

    if (IS_ASCII(c))
	call_main_func_nostroke(K_ESC | c);
}

void
escbmap(void)
{
    char c;
    c = getch();

    if (IS_DIGIT(c))
	escdmap(c);
    else if (IS_ASCII(c))
	call_main_func_nostroke(K_ESCB | c);
}

void
escdmap(char c)
{
    int d;

    d = (int) c - (int) '0';
    c = getch();
    if (IS_DIGIT(c)) {
	d = d * 10 + (int) c - (int) '0';
	c = getch();
    }
    if (c == '~')
	call_main_func_nostroke(K_ESCD | d);
}

void
tmpClearBuffer(Buffer * buf)
{
    if (buf && !buf->async_buf && !writeBufferCache(buf)) {
	buf->firstLine = NULL;
	buf->topLine = NULL;
	buf->currentLine = NULL;
	buf->lastLine = NULL;
    }
}

static Str currentURL(void);

void
saveBufferInfo()
{
    FILE *fp;

    if (main_p0env.flag & RG_DUMP_MASK)
	return;
#ifdef MANY_CHARSET
    if ((fp = mb_fopen(rcFile("bufinfo"), "w!", &tty_mb_w_setup)) == NULL)
	return;
    mb_fprintf(fp, "%s\n", currentURL());
    mb_fclose(fp);
#else
    if ((fp = fopen(rcFile("bufinfo"), "w")) == NULL)
	return;
    fprintf(fp, "%s\n", currentURL()->ptr);
    fclose(fp);
#endif
}

void
pushBuffer(Buffer *buf)
{
    buf->bufferprop |= BP_VISIBLE;
    insertBuffer(NULL, buf);
    updateCurrentbuf(buf);
#ifdef USE_BUFINFO
    saveBufferInfo();
#endif

}

void
delBuffer(Buffer *buf)
{
    if (!buf)
	return;

    if (buf == Currentbuf)
	Currentbuf = buf->next;

    deleteBuffer(buf);
    checkCurrentbuf();
}

MySignalHandler
intTrap(SIGNAL_ARG)
{				/* Interrupt catcher */
    LONGJMP(IntReturn, 0);
    SIGNAL_RETURN;
}

/* 
 * Command functions: These functions are called with a keystroke.
 */

#define MAX(a, b)  ((a) > (b) ? (a) : (b))
#define MIN(a, b)  ((a) < (b) ? (a) : (b))

static void
nscroll(int n, int mode)
{
    BufferView *v;
    Line *nexttop;
    int lnum, lmin, lmax;
#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
    Line *cl;
#endif

#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
    cl = Currentbuf->currentLine;
#endif
    v = Currentbuf->view;

    if (!n || Currentbuf->firstLine == NULL ||
	(n > 0 && Currentbuf->lastLine->linenumber - Currentbuf->topLine->linenumber < n) ||
	(n < 0 && Currentbuf->topLine->linenumber + v->height - 1 - Currentbuf->firstLine->linenumber < -n))
	return;

    nexttop = lineSkip(Currentbuf, Currentbuf->topLine, n, 0);

    if (v->height <= 1 || n % (v->height - 1)) {
	lmin = 0;
	lmax = v->height;
    }
    else {
	lmin = 1;
	lmax = v->height - 1;
    }

    if (Currentbuf->currentLine->linenumber - nexttop->linenumber >= lmin &&
	Currentbuf->currentLine->linenumber - nexttop->linenumber < lmax)
	lnum = Currentbuf->currentLine->linenumber;
    else {
	lnum = nexttop->linenumber + Currentbuf->currentLine->linenumber - Currentbuf->topLine->linenumber;

	if (lnum < Currentbuf->firstLine->linenumber)
	    lnum = Currentbuf->firstLine->linenumber;
	else if (lnum > Currentbuf->lastLine->linenumber)
	    lnum = Currentbuf->lastLine->linenumber;
    }

    Currentbuf->topLine = nexttop;
    gotoLine(Currentbuf, lnum);
#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
    select_menu_line(Currentbuf, cl);
#endif
    arrangeLine(Currentbuf);
    displayBuffer(Currentbuf, mode);
}

/* Move page forward */
void
pgFore(void)
{
#ifdef VI_PREC_NUM
    if (vi_prec_num)
	nscroll(searchKeyNum() * (Currentbuf->view->height - 1), B_NORMAL);
    else
#endif				/* VI_PREC_NUM */
	nscroll(prec_num ? searchKeyNum() : searchKeyNum() * (Currentbuf->view->height - 1),
		prec_num ? B_SCROLL : B_NORMAL);
}

/* Move page backward */
void
pgBack(void)
{
#ifdef VI_PREC_NUM
    if (vi_prec_num)
	nscroll(-searchKeyNum() * (Currentbuf->view->height - 1), B_NORMAL);
    else
#endif				/* VI_PREC_NUM */
	nscroll(-(prec_num ? searchKeyNum() : searchKeyNum() * (Currentbuf->view->height - 1)),
		prec_num ? B_SCROLL : B_NORMAL);
}

/* 1 line up */
void
lup1(void)
{
    nscroll(searchKeyNum(), B_SCROLL);
}

/* 1 line down */
void
ldown1(void)
{
    nscroll(-searchKeyNum(), B_SCROLL);
}

/* move cursor position to the center of screen */
void
ctrCsrV(void)
{
    int offsety;
    if (Currentbuf->firstLine == NULL)
	return;
    offsety = Currentbuf->view->height / 2 - Currentbuf->cursorY;
    if (offsety != 0) {
#if 0
	Currentbuf->currentLine = lineSkip(Currentbuf,
					   Currentbuf->currentLine, offsety, FALSE);
#endif
	Currentbuf->topLine = lineSkip(Currentbuf, Currentbuf->topLine, -offsety, FALSE);
	arrangeLine(Currentbuf);
	displayBuffer(Currentbuf, B_NORMAL);
    }
}

void
ctrCsrH(void)
{
    int offsetx;
    if (Currentbuf->firstLine == NULL)
	return;
    offsetx = Currentbuf->cursorX - (Currentbuf->view->width - Currentbuf->lmargin - Currentbuf->rmargin) / 2;
    if (offsetx != 0) {
	columnSkip(Currentbuf, offsetx);
	arrangeCursor(Currentbuf);
	displayBuffer(Currentbuf, B_NORMAL);
    }
}

/* Redraw screen */
void
rdrwSc(void)
{
    arrangeCursor(Currentbuf);
    displayCurrentView(NULL);
}

Str
shell_quote_cat(Str str, char *s, int len)
{
    if (!len && s)
	len = strlen(s);

    if (!str)
	str = Strnew_size(len);

    for (; *s && len > 0 ; ++s, --len) {
	if (strchr("\\\"`$", *s))
	    Strcat_char(str, '\\');

	Strcat_char(str, *s);
    }

    return str;
}

static void
clear_mark(Line *l)
{
    short pos;

    if (!l)
	return;

    for (pos = 0; pos < l->len; pos++)
	if (l->propBuf[pos] & PE_MARK)
	    for (;;) {
		l->propBuf[pos++] &= ~PE_MARK;

		while (pos >= l->len) {
		    if (!(l = l->next))
			return;

		    pos = 0;
		}

		if (!(l->propBuf[pos] & PE_MARK))
		    return;
	    }
}

/* Search Core */
static int
srchcore(char *str, int (*func) (Buffer *, char *, int))
{
    MySignalHandler(*prevtrap) ();
    int i, n;
    int volatile result = SR_NOTFOUND;

    if (str != NULL)
	SearchString = str;
    if (SearchString == NULL)
	return SR_NOTFOUND;

    prevtrap = signal(SIGINT, intTrap);
    crmode();
    if (SETJMP(IntReturn) == 0)
	for (i = 0, n = searchKeyNum(); i < n; i++)
	    result = func(Currentbuf, SearchString, AcrossLines);
    signal(SIGINT, prevtrap);
    term_raw();

    return result;
}

/* Search Result */
static void
disp_srchresult(int result, char *str, int (*routine)(Buffer *, char *, int))
{
    if (str == NULL)
	str = "";
    if (result & SR_NOTFOUND)
	disp_message(Sprintf("Not found: %s", str)->ptr, FALSE);
    else if (result & SR_WRAPPED)
	disp_message(Sprintf("Search wrapped: %s", str)->ptr, FALSE);
    else if (routine)
	disp_message(Sprintf("%s%s",
 			     routine == forwardSearch ?
			     "Forward: " : "Backward: ",
			     str)->ptr, FALSE);
}

static int
dispincsrch(int ch, Str buf, Lineprop *prop)
{
    static Buffer sbuf;
    static Line *currentLine;
    static short pos;
    char *str;
    int do_next_search = FALSE;

    if (ch == 0 && buf == NULL) {
	SAVE_BUFPOSITION(&sbuf);	/* search starting point */
	currentLine = sbuf.currentLine;
	pos = sbuf.pos;
	return -1;
    }

    str = buf->ptr;
    switch (ch) {
    case 022:			/* C-r */
	searchRoutine = backwardSearch;
	do_next_search = TRUE;
	break;
    case 023:			/* C-s */
	searchRoutine = forwardSearch;
	do_next_search = TRUE;
	break;

#ifdef USE_ROMAJI
    case 034:
	use_romaji_search = !use_romaji_search;
	goto done;
#endif

    default:
	if (ch >= 0)
	    return ch;		/* use InputKeymap */
    }

    if (do_next_search) {
	if (*str) {
	    SAVE_BUFPOSITION(&sbuf);
	    srchcore(str, searchRoutine);
	    arrangeCursor(Currentbuf);
	    if (Currentbuf->currentLine == currentLine
		&& Currentbuf->pos == pos) {
		SAVE_BUFPOSITION(&sbuf);
		srchcore(str, searchRoutine);
		arrangeCursor(Currentbuf);
	    }
	    displayBuffer(Currentbuf, B_FORCE_REDRAW);
	    clear_mark(Currentbuf->currentLine);
	    return -1;
	}
	else
	    return 020;		/* _prev completion for C-s C-s */
    }
    else if (*str) {
	RESTORE_BUFPOSITION(&sbuf);
	arrangeCursor(Currentbuf);
	srchcore(str, searchRoutine);
	arrangeCursor(Currentbuf);
	currentLine = Currentbuf->currentLine;
	pos = Currentbuf->pos;
    }
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
    clear_mark(Currentbuf->currentLine);
#ifdef USE_ROMAJI
done:
    while (*str++ != '\0') {
	if (use_romaji_search)
	    *prop++ |= PE_UNDER;
	else
	    *prop++ &= ~PE_UNDER;
    }
#endif
    return -1;
}

void
isrch(int (*func)(Buffer *, char *, int), char *prompt)
{
    char *str;
    Buffer sbuf;
    SAVE_BUFPOSITION(&sbuf);
    dispincsrch(0, NULL, NULL);	/* initialize incremental search state */

    searchRoutine = func;
    str = inputLineHistSearch(prompt, NULL, IN_STRING, TextHist, dispincsrch);
    if (str == NULL) {
	RESTORE_BUFPOSITION(&sbuf);
    }
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

/* Search regular expression */
void
srch(int (*func)(Buffer *, char *, int), char *mes)
{
    char *str;
    int result;
#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
    Line *cl;
#endif

    str = inputStrHist(mes, NULL, TextHist);
    if (str != NULL && *str == '\0')
	str = SearchString;
    if (str == NULL)
	return;
#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
    cl = Currentbuf->currentLine;
#endif
    result = srchcore(str, func);
    if (result & SR_FOUND) {
	clear_mark(Currentbuf->currentLine);
#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
	select_menu_line(Currentbuf, cl);
#endif
    }
    displayBuffer(Currentbuf, B_NORMAL);
    disp_srchresult(result, str, NULL);
    searchRoutine = func;
}

/* Search regular expression forward */
void
srchfor(void)
{
    srch(forwardSearch,
#ifdef USE_ROMAJI
	 use_romaji_search ? "Forward (Romaji): " : "Forward: "
#else
	 "Forward: "
#endif /* USE_ROMAJI */
	 );
}

/* Search regular expression backward */
void
srchbak(void)
{
    srch(backwardSearch,
#ifdef USE_ROMAJI
	 use_romaji_search ? "Backward (Romaji): " : "Backward: "
#else
	 "Backward: "
#endif /* USE_ROMAJI */
	 );
}

/* Incremental search forward */
void
isrchfor(void)
{
    isrch(forwardSearch,
#ifdef USE_ROMAJI
	  use_romaji_search ? "Forward (Incremental+Romaji): " :
#endif
	  "Forward (Incremental): ");
}

/* Incremental search backward */
void
isrchbak(void)
{
    isrch(backwardSearch,
#ifdef USE_ROMAJI
	  use_romaji_search ? "Backward (Incremental+Romaji): " :
#endif
	  "Backward (Incremental): ");
}

static void
srch_nxtprv(int reverse)
{
    int result;
#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
    Line *cl;
#endif
    static int (*routine[2]) (Buffer *, char *, int) =
    {
	forwardSearch, backwardSearch
    };

    if (searchRoutine == NULL) {
	disp_message("No previous regular expression", TRUE);
	return;
    }
    if (reverse != 0)
	reverse = 1;
    if (searchRoutine == backwardSearch)
	reverse ^= 1;
#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
    cl = Currentbuf->currentLine;
#endif
    result = srchcore(NULL, routine[reverse]);
    if (result & SR_FOUND) {
	clear_mark(Currentbuf->currentLine);
#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
	select_menu_line(Currentbuf, cl);
#endif
    }
    displayBuffer(Currentbuf, B_NORMAL);
    disp_srchresult(result, SearchString, show_srch_str ? routine[reverse] : NULL);
}

/* Search next matching */
void
srchnxt(void)
{
    srch_nxtprv(0);
}

/* Search previous matching */
void
srchprv(void)
{
    srch_nxtprv(1);
}

static void
shiftvisualpos(Buffer * buf, int shift)
{
    buf->visualpos -= shift;
    if (buf->visualpos >= buf->view->width - buf->rmargin - buf->lmargin)
	buf->visualpos = buf->view->width - buf->rmargin - buf->lmargin - 1;
    else if (buf->visualpos < 0)
	buf->visualpos = 0;
    arrangeLine(buf);
    if (buf->visualpos == -shift && buf->cursorX == 0)
	buf->visualpos = 0;
}

/* Shift screen left */
void
shiftl(void)
{
    int column;

    if (Currentbuf->firstLine == NULL)
	return;
    column = Currentbuf->currentColumn;
    columnSkip(Currentbuf, searchKeyNum() * (-Currentbuf->view->width + Currentbuf->rmargin + Currentbuf->lmargin + 1) + 1);
    shiftvisualpos(Currentbuf, Currentbuf->currentColumn - column);
    displayBuffer(Currentbuf, B_NORMAL);
}

/* Shift screen right */
void
shiftr(void)
{
    int column;

    if (Currentbuf->firstLine == NULL)
	return;
    column = Currentbuf->currentColumn;
    columnSkip(Currentbuf, searchKeyNum() * (Currentbuf->view->width - Currentbuf->rmargin - Currentbuf->lmargin - 1) - 1);
    shiftvisualpos(Currentbuf, Currentbuf->currentColumn - column);
    displayBuffer(Currentbuf, B_NORMAL);
}

void
col1R(void)
{
    Buffer *buf = Currentbuf;
    Line *l = buf->currentLine;
    int j, column, n;

    if (l == NULL)
	return;
    for (j = 0, n = searchKeyNum(); j < n; j++) {
	column = buf->currentColumn;
	columnSkip(Currentbuf, 1);
	if (column == buf->currentColumn)
	    break;
	shiftvisualpos(Currentbuf, 1);
    }
    displayBuffer(Currentbuf, B_NORMAL);
}

void
col1L(void)
{
    Buffer *buf = Currentbuf;
    Line *l = buf->currentLine;
    int j, n;

    if (l == NULL)
	return;
    for (j = 0, n = searchKeyNum(); j < n; j++) {
	if (buf->currentColumn == 0)
	    break;
	columnSkip(Currentbuf, -1);
	shiftvisualpos(Currentbuf, -1);
    }
    displayBuffer(Currentbuf, B_NORMAL);
}

int
inputTargetXY(int range_p)
{
    char *xy, *e;
    long x, y, prev_x = -1, prev_y = -1;

    xy = inputStrHist(range_p ? "Goto (COLUMN x LINE[ -> COLUMN x LINE]): " : "Goto (COLUMN x LINE): ",
		      NULL, TextHist);

    if (!xy)
	return FALSE;

    SKIP_BLANKS(xy);

    if (!*xy || (x = strtol(xy, &e, 0)) < 0 || x >= COLS)
	return FALSE;

    if (e == xy)
	x = Currentbuf->view->rootX + Currentbuf->lmargin + Currentbuf->cursorX;

    xy = e;
    SKIP_BLANKS(xy);
    y = Currentbuf->view->rootY + Currentbuf->cursorY;

    if (*xy == 'x' || *xy == 'X') {
	++xy;
	SKIP_BLANKS(xy);

	if ((y = strtol(xy, &e, 0)) < 0 || y >= LASTLINE)
	    return FALSE;

	if (xy == e)
	    y = Currentbuf->view->rootY + Currentbuf->cursorY;
    }

    if (range_p) {
	xy = e;
	SKIP_BLANKS(xy);

	if (!strncmp(xy, "->", sizeof("->") - 1)) {
	    int relative = 0;

	    prev_x = x;
	    prev_y = y;
	    xy += sizeof("->") - 1;
	    SKIP_BLANKS(xy);

	    switch (*xy) {
	    case '\0':
		return FALSE;
	    case '+':
		relative = 1;
		goto input_x;
	    case '-':
		relative = -1;
	    input_x:
		++xy;
	    default:
		if ((x = strtol(xy, &e, 0)) < 0 || x >= COLS)
		    return FALSE;

		if (xy == e) {
		    if ((x = prev_x + relative) < 0 || x >= COLS)
			return FALSE;
		}
		else {
		    xy = e;

		    if (relative &&
			((x = prev_x + x * relative) < 0 || x >= COLS))
			return FALSE;
		}

		break;
	    }

	    SKIP_BLANKS(xy);

	    if (*xy == 'x' || *xy == 'X') {
		++xy;
		SKIP_BLANKS(xy);
		relative = 0;

		switch (*xy) {
		case '+':
		    relative = 1;
		    goto input_y;
		case '-':
		    relative = -1;
		input_y:
		    ++xy;
		default:
		    if ((y = strtol(xy, &e, 0)) < 0 || y >= LASTLINE)
			return FALSE;

		    if (xy == e) {
			if ((y = prev_y + relative) < 0 || y >= LASTLINE)
			    return FALSE;
		    }
		    else if (relative &&
			     ((y = prev_y + y * relative) < 0 || y >= LASTLINE))
			return FALSE;
		}
	    }
	}
    }

    PrevTargetX = prev_x;
    PrevTargetY = prev_y;
    TargetX = x;
    TargetY = y;
    return TRUE;
}

Buffer *
bufferAtXY(short goal_x, short goal_y)
{
    Buffer *cur;
    BufferView *v;
    short col, row, x, y, nzcol, nzrow;

    if (!(cur = Currentbuf) || goal_x < 0 || goal_y < 0)
	return NULL;

#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
    if (cur->menu) {
	Menu *m;

	for (m = cur->menu ; m ; m = m->parent)
	    if (m->owner && (v = m->owner->view) &&
		goal_x >= v->rootX && goal_x < v->rootX + v->width &&
		goal_y >= v->rootY && goal_y < v->rootY + v->height)
		return m->owner;

	cur = cur->menu->current;
    }
#endif

    for (v = cur->view->root ;;) {
	if (goal_x < v->rootX || goal_x >= v->rootX + v->width ||
	    goal_y < v->rootY || goal_y >= v->rootY + v->height)
	    return NULL;

	if (v->frameset) {
	    for (nzrow = -1, y = v->rootY, row = 0 ; row < v->nrows ; ++row) {
		if (!v->rowv[row])
		    continue;

		if (nzrow >= 0)
		    ++y;

		if (goal_y >= y && goal_y < y + v->rowv[row])
		    goto check_row;

		y += v->rowv[row];
		nzrow = row;
	    }

	    return NULL;
	check_row:
	    for (nzcol = -1, x = v->rootX, col = 0 ; col < v->ncols ; ++col) {
		if (!v->colv[col])
		    continue;

		if (nzcol >= 0)
		    ++x;

		if (goal_x >= x && goal_x < x + v->colv[col])
		    goto next_view;

		x += v->colv[col];
		nzcol = col;
	    }

	    return NULL;
	next_view:
	    if (!(v = v->subv[row * v->ncols + col].top))
		return NULL;
	}
	else if (v->top) {
	    if (goal_x >= v->rootX + v->width - v->top->rmargin - v->top->lmargin)
		return NULL;

	    return v->top;
	}
	else
	    return NULL;
    }
}

void
scrollXY(void)
{
    int x, y;
    Buffer *orig, *temp;

    if (!Currentbuf || ((TargetX < 0 || TargetY < 0) && !inputTargetXY(TRUE)) ||
	TargetX < 0 || TargetX >= COLS ||
	TargetY < 0 || TargetY >= LASTLINE ||
	PrevTargetX < 0 || PrevTargetX >= COLS ||
	PrevTargetY < 0 || PrevTargetY >= LASTLINE)
	return;

    orig = Currentbuf;

    if ((temp = bufferAtXY(TargetX, TargetY)))
	updateCurrentbuf(temp);

    if ((y = TargetY - PrevTargetY) < 0) {
	prec_num = -y;
	lup1();
    }
    else if (y > 0) {
	prec_num = y;
	ldown1();
    }

    if ((x = TargetX - PrevTargetX) < 0) {
	prec_num = -x;
	col1R();
    }
    else if (x > 0) {
	prec_num = x;
	col1L();
    }

    if (temp && temp != orig) {
	updateCurrentbuf(orig);
	displayCurrentView(NULL);
    }
}

void
setEnv(void)
{
    char *env;
    char *var, *value;

    CurrentKeyData = NULL;	/* not allowed in w3m-control: */
    TargetX = TargetY = PrevTargetX = PrevTargetY = -1;
    env = searchKeyData();
    if (env == NULL || *env == '\0' || strchr(env, '=') == NULL) {
	if (env != NULL && *env != '\0')
	    env = Sprintf("%s=", env)->ptr;
	env = inputStrHist("Set environ: ", env, TextHist);
	if (env == NULL || *env == '\0') {
	    displayBuffer(Currentbuf, B_NORMAL);
	    return;
	}
    }
    if ((value = strchr(env, '=')) != NULL && value > env) {
	var = allocStr(env, value - env);
	value++;
	if (!strcmp(var, "PATH") && DecoderSearchPath &&
	    strcmp(value, DecoderSearchPath))
	    resetAE();
	set_environ(var, value);
    }
    displayBuffer(Currentbuf, B_NORMAL);
}

void
pipeBuf(void)
{
    Buffer *buf;
    char *cmd, *tmpf;
    FILE *f;

    CurrentKeyData = NULL;	/* not allowed in w3m-control: */
    TargetX = TargetY = PrevTargetX = PrevTargetY = -1;
    cmd = searchKeyData();
    if (cmd == NULL || *cmd == '\0') {
	cmd = inputLineHist("Pipe buffer to: ", "", IN_COMMAND, ShellHist);
	if (cmd == NULL || *cmd == '\0') {
	    displayBuffer(Currentbuf, B_NORMAL);
	    return;
	}
    }
    cmd = conv_to_system(cmd);
    tmpf = tmpfname(TMPF_DFL, NULL)->ptr;
    f = fopen(tmpf, "w");
    if (f == NULL) {
	disp_message(Sprintf("Can't save buffer to %s", cmd)->ptr, TRUE);
	return;
    }
    pushText(fileToDelete, tmpf);
    saveBuffer(Currentbuf, f);
    fclose(f);
    buf = getpipe(myExtCommand(cmd, tmpf, TRUE)->ptr, &main_p0env);
    if (buf == NULL) {
	disp_message("Execution failed", FALSE);
    }
    else {
	buf->bufferprop |= (BP_INTERNAL | BP_NO_URL);
	if (buf->type == NULL)
	    buf->type = "text/plain";
	if (!buf->async_buf)
	    pushBuffer(buf);
    }
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

/* Execute shell command and read output ac pipe. */
static void
pipesh_internal(Buffer *(*func)(char *, Phase0Env *))
{
    Buffer *buf;
    char *cmd;

    CurrentKeyData = NULL;	/* not allowed in w3m-control: */
    TargetX = TargetY = PrevTargetX = PrevTargetY = -1;
    cmd = searchKeyData();
    if (cmd == NULL || *cmd == '\0') {
	cmd = inputLineHist("(read shell[pipe])!", "", IN_COMMAND, ShellHist);
	if (cmd == NULL || *cmd == '\0') {
	    displayBuffer(Currentbuf, B_NORMAL);
	    return;
	}
    }
    cmd = conv_to_system(cmd);
    buf = func(cmd, &main_p0env);
    if (buf == NULL) {
	disp_message("Execution failed", FALSE);
    }
    else {
	buf->bufferprop |= (BP_INTERNAL | BP_NO_URL);
	if (buf->type == NULL)
	    buf->type = "text/plain";
	if (!buf->async_buf) pushBuffer(buf);
    }
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

void
pipesh(void)
{
    pipesh_internal(getpipe);
}

/* Execute shell command and load entire output to buffer */
void
readsh(void)
{
    pipesh_internal(getshell);
}

/* Execute shell command */
void
execsh(void)
{
#ifdef USE_MOUSE
    int use_m = use_mouse;
#endif
    char *cmd;

    CurrentKeyData = NULL;	/* not allowed in w3m-control: */
    TargetX = TargetY = PrevTargetX = PrevTargetY = -1;
    cmd = searchKeyData();
    if (cmd == NULL || *cmd == '\0') {
	cmd = inputLineHist("(exec shell)!", "", IN_COMMAND, ShellHist);
    }
    if (cmd != NULL && *cmd != '\0') {
	fmTerm(0);
	system(cmd);
	printf("\n[Hit any key]");
	fflush(stdout);
#ifdef USE_MOUSE
	use_mouse = FALSE;
#endif
	fmInit(0);
	getch();
#ifdef USE_MOUSE
	use_mouse = use_m;
	if (use_mouse)
	    mouse_init();
#endif
    }
    displayCurrentView(NULL);
}

/* Load file */
void
ldfile(void)
{
    char *fn;

    fn = searchKeyData();
    if (fn == NULL || *fn == '\0') {
	fn = inputFilenameHist("(Load)Filename? ", NULL, LoadHist);
	if (fn == NULL || *fn == '\0') {
	    displayBuffer(Currentbuf, B_NORMAL);
	    return;
	}
    }
    fn = conv_to_system(fn);
    cmd_loadfile(fn, NULL, &main_p0env);
}

/* Load help file */
void
ldhelp(void)
{
#ifdef USE_HELP_CGI
    char *lang;
    int n;

    lang = AcceptLang;
    n = strcspn(lang, ";, 	");
    cmd_loadURL(Sprintf("file:///$LIB/" HELP_CGI CGI_EXTENSION "?version=%s&myname=%s&lang=%s",
			Str_form_quote(Strnew_charp(w3m_version))->ptr,
			Str_form_quote(Strnew_charp((char *)myname))->ptr,
			Str_form_quote(Strnew_charp_n(lang, n))->ptr)->ptr, 
		NULL, NO_REFERER, NULL, &main_p0env);
#else
    cmd_loadURL(helpFile(HELP_FILE), NULL, NO_REFERER, NULL, &main_p0env);
#endif
}

struct cmd_loadfile_post_arg {
    char *url;
    ParsedURL pu;
};

static void
cmd_loadfile_post_internal(Buffer *buf, Phase0Env *p0env, void *chain_arg)
{
    struct cmd_loadfile_post_arg *arg;

    arg = chain_arg;

    if (arg->url && buf->currentURL.scheme == arg->pu.scheme && !strcmp(buf->currentURL.file, arg->pu.file))
	parseURL2(arg->url, &buf->currentURL, NULL);
}

static void
cmd_loadfile_post(Buffer *buf, int nlines)
{
    if (nlines < 0) {
	Phase0Env *p0env;

	p0env = buf->async_buf->p2env->p1env->p0env;
	cmd_loadfile_post_internal(buf, p0env, p0env->receiver_arg);
    }

    displayBufferMaybe(buf, nlines);
}

static void
cmd_loadfile(char *fn, char *url, Phase0Env *p0env_orig)
{
    Buffer *buf;
    Phase0Env p0env;
    struct cmd_loadfile_post_arg *arg;

    p0env = *p0env_orig;
    p0env.receiver = cmd_loadfile_post;
    p0env.receiver_arg = arg = New(struct cmd_loadfile_post_arg);
    arg->url = url;
    parseURL2(fn, &arg->pu, NULL);
    buf = loadGeneralFile(file_to_url(fn), NULL, NO_REFERER, &p0env, NULL);
    if (buf == NULL) {
	char *emsg = Sprintf("%s not found", fn)->ptr;
	disp_err_message(emsg, FALSE);
    }
    else if (buf != NO_BUFFER && !buf->async_buf) {
	cmd_loadfile_post_internal(buf, &p0env, arg);
	pushBuffer(buf);

	if (p0env_orig->RenderFrame && buf->frameset) {
	    Buffer *fbuf;

	    if ((fbuf = rFrame_internal(buf, p0env_orig)) && !fbuf->async_buf) {
		pushBuffer(fbuf);
		displayCurrentView(NULL);
	    }
	}
    }
    displayBuffer(Currentbuf, B_NORMAL);
}

/* Move cursor left */
void
movL(void)
{
    if (Currentbuf->firstLine == NULL)
	return;
    cursorLeft(Currentbuf, searchKeyNum());
    displayBuffer(Currentbuf, B_NORMAL);
}

/* Move cursor downward */
void
movD(void)
{
    if (Currentbuf->firstLine == NULL)
	return;
    cursorDown(Currentbuf, searchKeyNum());
    displayBuffer(Currentbuf, B_NORMAL);
}

/* move cursor upward */
void
movU(void)
{
    if (Currentbuf->firstLine == NULL)
	return;
    cursorUp(Currentbuf, searchKeyNum());
    displayBuffer(Currentbuf, B_NORMAL);
}

/* Move cursor right */
void
movR(void)
{
    if (Currentbuf->firstLine == NULL)
	return;
    cursorRight(Currentbuf, searchKeyNum());
    displayBuffer(Currentbuf, B_NORMAL);
}



/* movLW, movRW */
/* 
 * From: Takashi Nishimoto <g96p0935@mse.waseda.ac.jp> Date: Mon, 14 Jun
 * 1999 09:29:56 +0900 */

#define IS_WORD_CHAR(c,p) (IS_ALNUM(c) && CharType(p) == PC_ASCII)

static int
prev_nonnull_line(Line *line)
{
    Line *l;

    for (l = line; l != NULL && l->len == 0; l = l->prev)
	;
    if (l == NULL || l->len == 0)
	return -1;

    Currentbuf->currentLine = l;
    if (l != line)
	Currentbuf->pos = Currentbuf->currentLine->len;
    return 0;
}

void
movLW(void)
{
    char *lb;
    Lineprop *pb;
    Line *pline;
    int ppos;
    int i, n;

    if (Currentbuf->firstLine == NULL)
	return;

    for (i = 0, n = searchKeyNum(); i < n; i++) {
	pline = Currentbuf->currentLine;
	ppos = Currentbuf->pos;

	if (prev_nonnull_line(Currentbuf->currentLine) < 0)
	    goto end;

	while (1) {
	    lb = Currentbuf->currentLine->lineBuf;
	    pb = Currentbuf->currentLine->propBuf;
	    while (Currentbuf->pos > 0 &&
		   !IS_WORD_CHAR(lb[Currentbuf->pos - 1],
				 pb[Currentbuf->pos - 1])) {
		Currentbuf->pos--;
	    }
	    if (Currentbuf->pos > 0)
		break;
	    if (prev_nonnull_line(Currentbuf->currentLine->prev) < 0) {
		Currentbuf->currentLine = pline;
		Currentbuf->pos = ppos;
		goto end;
	    }
	    Currentbuf->pos = Currentbuf->currentLine->len;
	}

	lb = Currentbuf->currentLine->lineBuf;
	pb = Currentbuf->currentLine->propBuf;
	while (Currentbuf->pos > 0 &&
	       IS_WORD_CHAR(lb[Currentbuf->pos - 1],
			    pb[Currentbuf->pos - 1])) {
	    Currentbuf->pos--;
	}
    }
end:
    arrangeCursor(Currentbuf);
    displayBuffer(Currentbuf, B_NORMAL);
}

static int
next_nonnull_line(Line *line)
{
    Line *l;

    for (l = line; l != NULL && l->len == 0; l = l->next)
	;

    if (l == NULL || l->len == 0)
	return -1;

    Currentbuf->currentLine = l;
    if (l != line)
	Currentbuf->pos = 0;
    return 0;
}

void
movRW(void)
{
    char *lb;
    Lineprop *pb;
    Line *pline;
    int ppos;
    int i, n;

    if (Currentbuf->firstLine == NULL)
	return;

    for (i = 0, n = searchKeyNum(); i < n; i++) {
	pline = Currentbuf->currentLine;
	ppos = Currentbuf->pos;

	if (next_nonnull_line(Currentbuf->currentLine) < 0)
	    goto end;

	lb = Currentbuf->currentLine->lineBuf;
	pb = Currentbuf->currentLine->propBuf;

	while (lb[Currentbuf->pos] &&
	       IS_WORD_CHAR(lb[Currentbuf->pos], pb[Currentbuf->pos]))
	    Currentbuf->pos++;

	while (1) {
	    while (lb[Currentbuf->pos] &&
		   !IS_WORD_CHAR(lb[Currentbuf->pos], pb[Currentbuf->pos]))
		Currentbuf->pos++;
	    if (lb[Currentbuf->pos])
		break;
	    if (next_nonnull_line(Currentbuf->currentLine->next) < 0) {
		Currentbuf->currentLine = pline;
		Currentbuf->pos = ppos;
		goto end;
	    }
	    Currentbuf->pos = 0;
	    lb = Currentbuf->currentLine->lineBuf;
	    pb = Currentbuf->currentLine->propBuf;
	}
    }
end:
    arrangeCursor(Currentbuf);
    displayBuffer(Currentbuf, B_NORMAL);
}

/* Question and Quit */
void
qquitfm(void)
{
    char *ans;
    int i, n;

    if (!confirm_on_quit)
	quitfm();

    for (i = n = 0 ; i < read_nfd ; ++i) {
	if (is_recorded_read_fd(i) && read_hookv[i] == readUrgentMessage && read_hook_argv[i]) {
	    AsyncRWBuf *abuf;

	    if ((abuf = ((Buffer *)read_hook_argv[i])->async_buf))
		++n;
	}
    }

    ans = (n ?
	   inputChar(Sprintf("%d processes remain, do you really want to exit w3m? (y/n)", n)->ptr) :
	   inputChar("Do you want to exit w3m? (y/n)"));
    if (ans != NULL && tolower(*ans) == 'y')
	quitfm();
    if (Currentbuf && Currentbuf != NO_BUFFER)
	displayBuffer(Currentbuf, B_NORMAL);
}

/* Quit */
void
quitfm(void)
{
    term_title("");		/* XXX */
#ifdef USE_IMAGE
    if (fmInitialized && activeImage) {
	resetImage();
	termImage(-1);
    }
#endif
    fmTerm(0);
#ifdef USE_COOKIE
    save_cookies();
#endif				/* USE_COOKIE */
#ifdef USE_HISTORY
    if (SaveURLHist)
	saveHistory(URLHist, URLHistSize);
#endif				/* USE_HISTORY */
    w3m_exit(0);
}

/* Suspend (on BSD), or run interactive shell (on SysV) */
void
susp(void)
{
#ifndef SIGSTOP
    char *shell;
#endif				/* not SIGSTOP */
    move(INPUTLINE, 0);
    clrtoeolx();
    refresh();
    fmTerm(0);
#ifndef SIGSTOP
    shell = getenv("SHELL");
    if (shell == NULL)
	shell = "/bin/sh";
    system(shell);
#else				/* SIGSTOP */
    kill((pid_t)0, SIGSTOP);
#endif				/* SIGSTOP */
    fmInit(0);
    displayCurrentView(NULL);
}

/* Go to specified line */
static void
_goLine(char *l)
{
#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
    Line *cl;
#endif

    if (l == NULL || *l == '\0' || Currentbuf->currentLine == NULL) {
	displayBuffer(Currentbuf, B_FORCE_REDRAW);
	return;
    }
#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
    cl = Currentbuf->currentLine;
#endif
    Currentbuf->pos = 0;
    if (((*l == '^') || (*l == '$') || (*l == 'T') || (*l == 'B')) && prec_num) {
	gotoRealLine(Currentbuf, prec_num);
    }
    else if (*l == '^') {
	Currentbuf->topLine = Currentbuf->currentLine = Currentbuf->firstLine;
    }
    else if (*l == '$') {
	Currentbuf->topLine = lineSkip(Currentbuf, Currentbuf->lastLine, -Currentbuf->view->height / 2,
				       TRUE);
	Currentbuf->currentLine = Currentbuf->lastLine;
    }
    else if (*l == 'T') {
	Currentbuf->currentLine = Currentbuf->topLine;
    }
    else if (*l == 'B') {
	Currentbuf->currentLine = lineSkip(Currentbuf, Currentbuf->topLine, Currentbuf->view->height - 1, FALSE);
    }
    else
	gotoRealLine(Currentbuf, atoi(l));
#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
    select_menu_line(Currentbuf, cl);
#endif
    arrangeCursor(Currentbuf);
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

void
goLine(void)
{
    if (prec_num)
	_goLine("^");
    else
	_goLine(inputStr("Goto line: ", ""));
}

void
goLineF(void)
{
    _goLine("^");
}

void
goLineL(void)
{
    _goLine("$");
}

void
goLineT(void)
{
    _goLine("T");
}

void
goLineB(void)
{
    _goLine("B");
}

/* Go to the beginning of the line */
void
linbeg(void)
{
    Line *l;

    if (Currentbuf->firstLine == NULL)
	return;
    for (l = Currentbuf->currentLine ; !l->real_linenumber && l->prev ; l = l->prev)
	;
    Currentbuf->currentLine = l;
    Currentbuf->pos = 0;
    arrangeCursor(Currentbuf);
    displayBuffer(Currentbuf, B_NORMAL);
}

/* Go to the bottom of the line */
void
linend(void)
{
    Line *l;

    if (Currentbuf->firstLine == NULL)
	return;
    for (l = Currentbuf->currentLine ; l->next && !l->next->real_linenumber ; l = l->next)
	;
    Currentbuf->currentLine = l;
    Currentbuf->pos = l->len - 1;
    arrangeCursor(Currentbuf);
    displayBuffer(Currentbuf, B_NORMAL);
}

struct system_bg_arg {
    char *cmd;
    void (*func)(void *);
    void *arg;
};

static void
system_bg_receiver(Buffer *buf, int nlines)
{
    if (nlines < 0) {
	struct system_bg_arg *arg;

	arg = buf->async_buf->p2env->p1env->p0env->receiver_arg;

	if (arg->func)
	    arg->func(arg->arg);

	if (!(buf->bufferprop & BP_LINKED))
	    discardBuffer(buf);
    }
}

static void
system_bg(char *cmd, int needsterminal,
	  void (*post_func)(void *), void *post_arg,
	  Phase0Env *p0env_org, char *msg)
{
    if ((p0env_org->flag & RG_PROC_MASK) == RG_PROC_FORK) {
	pid_t pid;
	Phase0Env p0env;
	struct system_bg_arg *arg;
	Buffer *newBuf;

	p0env = *p0env_org;
	p0env.receiver = system_bg_receiver;
	p0env.receiver_arg = arg = New(struct system_bg_arg);
	arg->cmd = cmd;
	arg->func = post_func;
	arg->arg = post_arg;
	newBuf = NULL;

	if ((pid = forkWithChannel(cmd, &p0env, &newBuf)) == -1) {
	    Str emsg;

	    emsg = Sprintf("system_bg(\"%s\"): %s", shell_quote_cat(NULL, cmd, strlen(cmd))->ptr, strerror(errno));
	    disp_err_message(emsg->ptr, FALSE);
	}
	else if (!pid) {
	    if (needsterminal) {
		fmTerm(1);
		mySystem(cmd, MYSYSTEM_TTYSTDIN | MYSYSTEM_TTYSTDOUT);
		fmInit(1);
	    }
	    else
		mySystem(cmd, MYSYSTEM_NULLSTDIN | MYSYSTEM_NULLSTDOUT);

	    flush_buffer_and_exit(NULL);
	}

	if (!needsterminal)
	    disp_message(Sprintf(msg, cmd)->ptr, FALSE);
    }
    else {
	if (needsterminal) {
	    fmTerm(0);
	    mySystem(cmd, MYSYSTEM_TTYSTDIN | MYSYSTEM_TTYSTDOUT);
	    fmInit(0);
	    displayCurrentView(NULL);
	}
	else {
	    disp_message(Sprintf(msg, cmd)->ptr, FALSE);
	    mySystem(cmd, MYSYSTEM_NULLSTDIN | MYSYSTEM_NULLSTDOUT);
	}

	if (post_func)
	    post_func(post_arg);
    }
}

/* Run editor on the current buffer */
struct editBf_arg {
    Buffer *curbuf;
    char *fn;
    int dont_reset_form;
};

static void
editBf_post(void *arg)
{
    struct editBf_arg *p;
    Phase0Env p0env;

    p = arg;
    p0env = main_p0env;
    p0env.flag &= ~(RG_PROC_MASK | RG_DONT_RESET_FORM);

    if (p->dont_reset_form)
	p0env.flag |= RG_DONT_RESET_FORM;

    p0env.curbuf = p->curbuf;
    p0env.curbuf->need_reshape = TRUE;
    p0env.curbuf->redraw_mode =
#ifdef USE_IMAGE
	activeImage && p0env.curbuf->image_flag == IMG_FLAG_AUTO && p0env.curbuf->img ? B_REDRAW_IMAGE :
#endif
	B_FORCE_REDRAW;
    reloadBuffer(p0env.curbuf, &p0env);
    displayCurrentView(NULL);
}

static struct mailcap *
searchEditor(char *ctx, btri_string_tab_t *attr, ParsedURL *pu)
{
    Str s;

    s = Strnew_m_charp("x-w3m-edit/", ctx, NULL);
    return searchExtViewer(s->ptr, attr, pu, UserBrowsecap);
}

#ifdef JP_CHARSET
static char *
code_to_charset(char code)
{
    char *cs;
    int n;

    cs = code_to_str(code);
    n = strcspn(cs, " \t");

    while (n > 0 && IS_SPACE(cs[n - 1]))
	--n;

    return allocStr(cs, n);
}
#endif

void
editBf(void)
{
    struct editBf_arg *arg;
    Str url, cmd;
    struct mailcap *mcap;
#ifdef JP_CHARSET
    char *cs;
#endif
    btri_string_tab_t *attr;
    ParsedURL pu;

    if (Currentbuf->real_scheme != SCM_LOCAL &&
	!(Currentbuf->sourcefile && edit_remote_source)) {
	disp_err_message("Can't edit other than local file", TRUE);
	return;
    }

    arg = New(struct editBf_arg);
    arg->curbuf = Currentbuf;
    arg->fn = Currentbuf->filename;
    arg->dont_reset_form = str_to_bool(searchKeyData(), TRUE);
    attr = contentTypeAttributesToTable(Currentbuf);

#ifdef MANY_CHARSET
    if (Currentbuf->document_encoding) {
	if (!attr)
	    attr = btri_new_node(&btri_string_ci_tab_desc);

	btri_search_mem(&btri_string_ci_tab_desc, BTRI_OP_ADD | BTRI_OP_WR,
			"w3m-document-charset", sizeof("w3m-document-charset") - 1, attr, (void **)&Currentbuf->document_encoding);
    }
#endif

#ifdef JP_CHARSET
    if (!attr)
	attr = btri_new_node(&btri_string_ci_tab_desc);

    cs = code_to_charset(Currentbuf->document_encoding);
    btri_search_mem(&btri_string_ci_tab_desc, BTRI_OP_ADD | BTRI_OP_WR,
		    "w3m-document-charset", sizeof("w3m-document-charset") - 1, attr, (void **)&cs);
#endif
    url = Strnew_charp("file://");
    Strcat_charp(url, file_quote(Currentbuf->sourcefile));
    parseURL2(url->ptr, &pu, NULL);

    if ((mcap = Currentbuf->mailcap) && mcap->edit)
	cmd = unquote_mailcap(mcap->edit, Currentbuf->real_type, attr, &pu, NULL, TRUE);
    else {
	if (!(mcap = searchEditor("buffer", NULL, &pu))) {
	    disp_err_message("Editor not found", TRUE);
	    return;
	}

	cmd = unquote_mailcap(mcap->viewer, Currentbuf->real_type, attr, &pu, NULL, TRUE);
    }

    system_bg(cmd->ptr, mcap->flags & MAILCAP_NEEDSTERMINAL,
	      editBf_post, arg, &main_p0env, "Edit source with %s");
}

static Str
find_tty_editor(char *what, btri_string_tab_t *attr, Str tmpf, struct mailcap **p_mcap)
{
    ParsedURL pu;
    struct mailcap *mcap;
#ifdef JP_CHARSET
    char *cs;
#endif

    if (!attr)
	attr = btri_new_node(&btri_string_ci_tab_desc);

#ifdef JP_CHARSET
    cs = code_to_charset(DisplayCode);
    btri_search_mem(&btri_string_ci_tab_desc, BTRI_OP_ADD | BTRI_OP_WR,
		    "w3m-document-charset", sizeof("w3m-document-charset") - 1, attr, (void **)&cs);
#endif
#ifdef MANY_CHARSET
    if (tty_mb_w_setup.cs)
	btri_search_mem(&btri_string_ci_tab_desc, BTRI_OP_ADD | BTRI_OP_WR,
			"w3m-document-charset", sizeof("w3m-document-charset") - 1, attr, (void **)&tty_mb_w_setup.cs);
#endif

    parseURL2(Strnew_m_charp("file://", tmpf->ptr, NULL)->ptr, &pu, NULL);

    if (!(mcap = searchEditor(what, attr, &pu)))
	return NULL;

    *p_mcap = mcap;
    return unquote_mailcap(mcap->viewer, Currentbuf->real_type, attr, &pu, NULL, TRUE);
}

/* Run editor on the current screen */
struct editScr_arg {
    Buffer *curbuf;
    Str tmpf;
};

static void
editScr_post(void *arg)
{
    struct editScr_arg *p;

    p = arg;
    unlink(p->tmpf->ptr);

    if (Currentbuf == p->curbuf)
	displayBuffer(Currentbuf, B_NORMAL);
}

void
editScr(void)
{
    Str cmd;
    FILE *f;
    int lnum;
    btri_string_tab_t *attr;
    Str lnum_str;
    struct editScr_arg *arg;
    struct mailcap *mcap;

    arg = New(struct editScr_arg);
    arg->curbuf = Currentbuf;
    arg->tmpf = tmpfname(TMPF_DFL, NULL);

    if (!(f = fopen(arg->tmpf->ptr, "w"))) {
	cmd = Sprintf("Can't open %s", arg->tmpf->ptr);
	disp_err_message(cmd->ptr, TRUE);
	return;
    }

    pushText(fileToDelete, arg->tmpf->ptr);
    saveBuffer(Currentbuf, f);
    fclose(f);

    if (Currentbuf->currentLine)
	lnum = Currentbuf->currentLine->linenumber;
    else
	lnum = 1;

    attr = btri_new_node(&btri_string_ci_tab_desc);
    lnum_str = Sprintf("%d", lnum);
    btri_search_mem(&btri_string_ci_tab_desc, BTRI_OP_ADD | BTRI_OP_WR,
		    "lnum", sizeof("lnum") - 1, attr, (void **)&lnum_str->ptr);

    if (!(cmd = find_tty_editor("screen", attr, arg->tmpf, &mcap)) || !*cmd->ptr) {
	disp_err_message("Editor not found", TRUE);
	return;
    }

    system_bg(cmd->ptr, mcap->flags & MAILCAP_NEEDSTERMINAL,
	      editScr_post, arg, &main_p0env, "Edit screen image with %s");
}

#ifdef USE_MARK

/* Set / unset mark */
void
_mark(void)
{
    Line *l;
    if (!use_mark)
	return;
    if (Currentbuf->firstLine == NULL)
	return;
    l = Currentbuf->currentLine;
    l->propBuf[Currentbuf->pos] ^= PE_MARK;
    redrawLine(Currentbuf, l, l->linenumber - Currentbuf->topLine->linenumber);
}

/* Go to next mark */
void
nextMk(void)
{
    Line *l;
    int i;

    if (!use_mark)
	return;
    if (Currentbuf->firstLine == NULL)
	return;
    i = Currentbuf->pos + 1;
    l = Currentbuf->currentLine;
    if (i >= l->len) {
	i = 0;
	l = l->next;
    }
    while (l != NULL) {
	for (; i < l->len; i++) {
	    if (l->propBuf[i] & PE_MARK) {
#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
		Line *cl;

		cl = Currentbuf->currentLine;
#endif
		Currentbuf->currentLine = l;
		Currentbuf->pos = i;
#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
		select_menu_line(Currentbuf, cl);
#endif
		arrangeCursor(Currentbuf);
		displayBuffer(Currentbuf, B_NORMAL);
		return;
	    }
	}
	l = l->next;
	i = 0;
    }
    disp_message("No mark exist after here", TRUE);
}

/* Go to previous mark */
void
prevMk(void)
{
    Line *l;
    int i;

    if (!use_mark)
	return;
    if (Currentbuf->firstLine == NULL)
	return;
    i = Currentbuf->pos - 1;
    l = Currentbuf->currentLine;
    if (i < 0) {
	l = l->prev;
	if (l != NULL)
	    i = l->len - 1;
    }
    while (l != NULL) {
	for (; i >= 0; i--) {
	    if (l->propBuf[i] & PE_MARK) {
#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
		Line *cl;

		cl = Currentbuf->currentLine;
#endif
		Currentbuf->currentLine = l;
		Currentbuf->pos = i;
#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
		select_menu_line(Currentbuf, cl);
#endif
		arrangeCursor(Currentbuf);
		displayBuffer(Currentbuf, B_NORMAL);
		return;
	    }
	}
	l = l->prev;
	if (l != NULL)
	    i = l->len - 1;
    }
    disp_message("No mark exist before here", TRUE);
}

/* Mark place to which the regular expression matches */
void
reMark(void)
{
    Line *l, *tl;
    char *str;
    char *p, *ep, *mb, *p1, *p2;
    int pos, tpos, how, res;
#ifdef MANY_CHARSET
    int e;
    mb_wchar_t wc;
#endif

    if (!use_mark)
	return;

    if (!(str = inputStrHist("(Mark)Regexp: ", MarkString, TextHist))
	|| !*str) {
	displayBuffer(Currentbuf, B_NORMAL);
	return;
    }

    if ((p = regexCompile(str, 1))) {
	disp_message(p, TRUE);
	return;
    }

    MarkString = str;
    how = RE_FLAG_BOF;

    for (l = Currentbuf->firstLine ; l ; l = l->next, how &= ~RE_FLAG_BOF) {
	pos = 0;
    loop:
	how &= ~RE_FLAG_REUSE;

	for (tl = l, tpos = pos, mb = NULL ;; how |= RE_FLAG_REUSE, how &= ~RE_FLAG_BOF, tl = tl->next, tpos = 0) {
	    if (tl->next && !tl->next->real_linenumber)
		how &= ~RE_FLAG_EOL;
	    else
		how |= RE_FLAG_EOL;

	    p = tl->lineBuf + tpos;
	    ep = tl->lineBuf + tl->len;
	    res = regexSearch(&p, &ep, how | (tl->next ? 0 : RE_FLAG_EOF));

	    if (!mb)
		mb = p;

	    if (!res || ep < tl->lineBuf + tl->len || !tl->next)
		break;
	}

	matchedPosition(&p1, &p2);

	if (p1 && p2) {
	    while (!(p1 >= l->lineBuf && p1 <= l->lineBuf + l->len))
		l = l->next;

	    while (p1 >= l->lineBuf + l->len && l->next) {
		l = l->next;
		p1 = l->lineBuf;
	    }

	    if (p1 - l->lineBuf < l->len)
		l->propBuf[p1 - l->lineBuf] |= PE_MARK;

	    while (!(p2 >= tl->lineBuf && p2 <= tl->lineBuf + tl->len))
		tl = tl->prev;

	    l = tl;

	    if ((pos = p2 - l->lineBuf) < l->len)
		goto loop;

	    break;
	}
	else {
	    if ((pos = p - l->lineBuf) < l->len) {
#ifdef MANY_CHARSET
		e = mb_mem_to_wchar_internal(&l->lineBuf[pos], l->len - pos, wc);
		pos += e > 0 ? e : 1;
#else
		++pos;
#ifdef JP_CHARSET
		if (l->propBuf[pos] & PC_KANJI2)
		    ++pos;
#endif
#endif
	    }

	    if (pos >= l->len) {
		if (!(l = l->next))
		    break;

		pos = 0;
	    }
	}
    }

    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}
#endif				/* USE_MARK */

struct receiver_chain {
    void (*func)(Buffer *, Phase0Env *, void *);
    void *arg;
    int dont_display;
};

static void
generic_receiver(Buffer *buf, int nlines)
{
    if (!(buf->bufferprop & BP_DISCARDED) && nlines < 0) {
	Phase0Env *p0env;
	struct receiver_chain *arg;

	p0env = buf->async_buf->p2env->p1env->p0env;
	arg = p0env->receiver_arg;

	if (arg->func)
	    arg->func(buf, p0env, arg->arg);

	if (arg->dont_display)
	    return;
    }

    displayBufferMaybe(buf, nlines);
}

static struct receiver_chain *
new_receiver_chain(void (*func)(Buffer *, Phase0Env *, void *), void *arg)
{
    struct receiver_chain *chain;

    chain = New(struct receiver_chain);
    chain->func = func;
    chain->arg = arg;
    chain->dont_display = 0;
    return chain;
}

static void
loadLink(char *url, char *target, char *referer, FormList *request,
	 void (*receiver)(Buffer *, Phase0Env *, void *), void *receiver_arg, Phase0Env *p0env_orig)
{
    Buffer *buf;
    ParsedURL *base;
    Phase0Env p0env;

    message(Sprintf("loading %s", url)->ptr);
    refresh();

    if (!(base = baseURL(Currentbuf)) || base->scheme == SCM_LOCAL || base->scheme == SCM_LOCAL_CGI)
	referer = NO_REFERER;
    else if (!referer)
	referer = parsedURL2Str(&Currentbuf->currentURL)->ptr;

    p0env = *p0env_orig;

    if (!(p0env.flag & RG_ON_TARGET) /* open link as an indivisual page */
	|| p0env.flag & RG_DO_DOWNLOAD /* download (thus no need to render frame) */
	)
	target = "_top";

    p0env.receiver = generic_receiver;
    p0env.receiver_arg = new_receiver_chain(receiver, receiver_arg);
    p0env.view = searchBufferView(Currentbuf->view, target);
    p0env.curbuf = Currentbuf;

    if (!(buf = loadGeneralFile(url, baseURL(Currentbuf), referer, &p0env, request))) {
	char *emsg = Sprintf("Can't load %s", url)->ptr;
	disp_err_message(emsg, FALSE);
	return;
    }

    if (buf == NO_BUFFER || buf->async_buf) {
	ParsedURL pu;

	parseURL2(url, &pu, base);
	pushHashHist(URLHist, parsedURL2Str(&pu)->ptr);
	return;
    }

    if (receiver)
	receiver(buf, &p0env, receiver_arg);

    pushBuffer(buf);

    if (main_p0env.RenderFrame && buf->frameset) {
	Buffer *fbuf;
	Phase0Env p0env_frame;

	p0env_frame = main_p0env;
	p0env_frame.view = p0env.view;
	p0env_frame.curbuf = Currentbuf;

	if ((fbuf = rFrame_internal(buf, &p0env_frame)) &&
	    !fbuf->async_buf) {
	    pushBuffer(fbuf);
	    displayCurrentView(NULL);
	}
    }

    displayBuffer(Currentbuf, B_NORMAL);
}

static void
gotoLabel(char *label, Buffer *curbuf)
{
    Buffer *buf;
    AnchorList *al;
    Anchor *a;
#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
    Line *cl;
#endif

    a = searchURLLabel(curbuf, label);
    if (a == NULL) {
	char *emsg;

	emsg = Sprintf("%s is not found", label)->ptr;
	disp_err_message(emsg, TRUE);
	return;
    }
    buf = newBuffer(curbuf->view);
    COPY_BUFPOSITION(buf, curbuf);
    buf->posHist = curbuf->posHist;
    curbuf->posHist = buf;
    copyParsedURL(&buf->currentURL, &curbuf->currentURL);
    curbuf->currentURL.label = allocStr(label, -1);
    pushHashHist(URLHist, parsedURL2Str(&curbuf->currentURL)->ptr);
retry:
#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
    cl = curbuf->currentLine;
#endif
    gotoLine(curbuf, a->start.line);
    curbuf->pos = a->start.pos;
#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
    select_menu_line(curbuf, cl);
#endif
    arrangeCursor(curbuf);
    al = curbuf->name;
    displayBuffer(curbuf, B_FORCE_REDRAW);
    if (curbuf->name != al &&
	(a = searchURLLabel(curbuf, label)))
	goto retry;
    return;
}

/* follow HREF link */
static void followForm_internal(Phase0Env *p0env, int submit);
static void followI_internal(Phase0Env *p0env);

static void
followA_internal(Phase0Env *p0env)
{
    Line *l;
    Anchor *a;
    Href *ref;
    ParsedURL u;
#ifdef USE_IMAGE
    int x = 0, y = 0, map = 0;
#endif
    char *url;

    if (Currentbuf->firstLine == NULL)
	return;
    l = Currentbuf->currentLine;

#ifdef USE_IMAGE
    a = retrieveCurrentImg(Currentbuf);
    if (a && !strncasecmp(Currentbuf->real_type, "image/", sizeof("image/") - 1)) {
	followI_internal(p0env);
	return;
    }
    if (a && a->link->img && a->link->img->map) {
	followForm_internal(p0env, FALSE);
	return;
    }
    if (a && a->link->img && a->link->img->ismap) {
	getMapXY(Currentbuf, a, &x, &y);
	map = 1;
    }
#else
    a = retrieveCurrentMap(Currentbuf);
    if (a) {
	followForm_internal(p0env, FALSE);
	return;
    }
#endif
    a = retrieveCurrentAnchor(Currentbuf);
    if (a == NULL) {
	followForm_internal(p0env, FALSE);
	return;
    }
    ref = a->link->href;
    if (*ref->url == '#') {	/* index within this buffer */
	gotoLabel(ref->url + 1, Currentbuf);
	return;
    }
    parseURL2(ref->url, &u, baseURL(Currentbuf));
    if (same_url_p(&u, &Currentbuf->currentURL)) {
	/* index within this buffer */
	if (u.label) {
	    gotoLabel(u.label, Currentbuf);
	    return;
	}
    }
    url = ref->url;
#ifdef USE_IMAGE
    if (map)
	url = Sprintf("%s?%d,%d", url, x, y)->ptr;
#endif
    loadLink(url, ref->target, ref->referer, NULL, NULL, NULL, p0env);
    displayBuffer(Currentbuf, B_NORMAL);
}

void
followA(void)
{
    followA_internal(&main_p0env);
}

/* follow HREF link in the buffer */
void
bufferA(void)
{
    Phase0Env p0env;

    p0env = main_p0env;
    p0env.flag &= ~RG_ON_TARGET;
    followA_internal(&p0env);
}

/* view inline image */
#ifdef USE_IMAGE
static int
copyImageFile(Image *img, Phase0Env *p0env)
{
    struct stat stbuf;

    if (img && img->cache && img->cache->file &&
	!stat(img->cache->file, &stbuf)) {
	ParsedURL pu;
	Phase0Env p0env;

	parseURL2(img->cache->url, &pu, img->cache->current);
	p0env = main_p0env;
	doFileCopy(img->cache->file,
		   conv_from_system(guess_save_name(pu.file)),
		   &p0env);
	return TRUE;
    }

    return FALSE;
}
#endif

static void
followI_internal(Phase0Env *p0env)
{
    Line *l;
    Anchor *a;
    Image *img;
    Buffer *buf;

    if (Currentbuf->firstLine == NULL)
	return;
    l = Currentbuf->currentLine;

    a = retrieveCurrentImg(Currentbuf);
    if (a == NULL)
	return;
    img = a->link->img;
#ifdef USE_IMAGE
    if (p0env->flag & RG_DO_DOWNLOAD && copyImageFile(img, p0env))
	return;
#endif
    message(Sprintf("loading %s", img->url)->ptr);
    refresh();
    buf = loadGeneralFile(img->url, baseURL(Currentbuf), NULL, p0env, NULL);
    if (buf == NULL) {
	char *emsg = Sprintf("Can't load %s", img->url)->ptr;
	disp_err_message(emsg, FALSE);
    }
    else if (buf != NO_BUFFER && !buf->async_buf) {
	pushBuffer(buf);
    }
    displayBuffer(Currentbuf, B_NORMAL);
}

void
followI(void)
{
    followI_internal(&main_p0env);
}

static FormItemList *
save_submit_formlist(FormItemList *src)
{
    FormList *list;
    FormList *srclist;
    FormItemList *srcitem;
    FormItemList *item;
    FormItemList *ret = NULL;
#ifdef MENU_SELECT
    FormSelectOptionItem *opt;
    FormSelectOptionItem *curopt;
    FormSelectOptionItem *srcopt;
#endif				/* MENU_SELECT */

    if (src == NULL)
	return NULL;
    srclist = src->parent;
    list = New(FormList);
    list->method = srclist->method;
    list->action = Strdup(srclist->action);
    list->charset = srclist->charset;
    list->enctype = srclist->enctype;
    list->nitems = srclist->nitems;
    list->body = srclist->body;
    list->boundary = srclist->boundary;
    list->length = srclist->length;

    for (srcitem = srclist->item; srcitem; srcitem = srcitem->next) {
	item = New(FormItemList);
	item->type = srcitem->type;
	item->name = Strdup(srcitem->name);
	item->value = Strdup(srcitem->value);
	item->checked = srcitem->checked;
	item->accept = srcitem->accept;
	item->size = srcitem->size;
	item->rows = srcitem->rows;
	item->maxlength = srcitem->maxlength;
	item->readonly = srcitem->readonly;
#ifdef MENU_SELECT
	opt = curopt = NULL;
	for (srcopt = srcitem->select_option; srcopt; srcopt = srcopt->next) {
	    if (!srcopt->checked)
		continue;
	    opt = New(FormSelectOptionItem);
	    opt->value = Strdup(srcopt->value);
	    opt->label = Strdup(srcopt->label);
	    opt->checked = srcopt->checked;
	    if (item->select_option == NULL) {
		item->select_option = curopt = opt;
	    }
	    else {
		curopt->next = opt;
		curopt = curopt->next;
	    }
	}
	item->select_option = opt;
	if (srcitem->label)
	    item->label = Strdup(srcitem->label);
#endif				/* MENU_SELECT */
	item->parent = list;
	item->next = NULL;

	if (list->lastitem == NULL) {
	    list->item = list->lastitem = item;
	}
	else {
	    list->lastitem->next = item;
	    list->lastitem = item;
	}

	if (srcitem == src)
	    ret = item;
    }

    return ret;
}

#if defined(MANY_CHARSET) || defined(JP_CHARSET)
static Str
conv_form_encoding(Str val, FormItemList *fi, Buffer *buf, int dup_p)
{
    return (
#ifdef MANY_CHARSET
	    fi->parent->charset ?
	    conv_mem2isoStr(val->ptr, val->length, "@|", fi->parent->charset, MB_FLAG_DISCARD_NOTPREFERED_CHAR)
	    :
	    buf->document_encoding ?
	    conv_mem2isoStr(val->ptr, val->length, "@|", buf->document_encoding, MB_FLAG_DISCARD_NOTPREFERED_CHAR)
	    :
	    conv_mem2isoStr(val->ptr, val->length, "|", MB_FLAG_DISCARD_NOTPREFERED_CHAR)
#else
	    fi->parent->charset ?
	    conv_str(val, InnerCode, fi->parent->charset)
	    :
	    buf->document_encoding ?
	    conv_str(val, InnerCode, buf->document_encoding)
	    :
	    dup_p ? Strdup(val) : val
#endif
	    );
}
#else
#define conv_form_encoding(val, fi, buf, dup_p) ((dup_p) ? Strdup((val)) : (val))
#endif

static void
query_from_followform(Str *query, FormItemList *fi, int multipart)
{
    FormItemList *f2;
    FILE *body = NULL;

    if (multipart) {
	*query = tmpfname(TMPF_DFL, NULL);
	body = fopen((*query)->ptr, "w");
	if (body == NULL) {
	    return;
	}
	fi->parent->body = (*query)->ptr;
	fi->parent->boundary = Sprintf("------------------------------%d%ld%ld%ld",
				       getpid(), fi->parent, fi->parent->body, fi->parent->boundary)->ptr;
    }
    *query = Strnew();
    for (f2 = fi->parent->item; f2; f2 = f2->next) {
	if (f2->name == NULL)
	    continue;
	/* <ISINDEX> is translated into single text form */
	if (f2->name->length == 0 &&
	    (multipart || f2->type != FORM_INPUT_TEXT))
	    continue;
	switch (f2->type) {
	case FORM_INPUT_RESET:
	    /* do nothing */
	    continue;
	case FORM_INPUT_SUBMIT:
	case FORM_INPUT_IMAGE:
	    if (f2 != fi || f2->value == NULL)
		continue;
	    break;
	case FORM_INPUT_RADIO:
	case FORM_INPUT_CHECKBOX:
	    if (!f2->checked)
		continue;
	}
	if (multipart) {
	    if (f2->type == FORM_INPUT_IMAGE) {
		int x = 0, y = 0;
#ifdef USE_IMAGE
		getMapXY(Currentbuf, retrieveCurrentImg(Currentbuf), &x, &y);
#endif
		*query = conv_form_encoding(f2->name, fi, Currentbuf, 1);
		Strcat_charp(*query, ".x");
		form_write_data(body, fi->parent->boundary, (*query)->ptr,
				Sprintf("%d", x)->ptr);
		*query = conv_form_encoding(f2->name, fi, Currentbuf, 1);
		Strcat_charp(*query, ".y");
		form_write_data(body, fi->parent->boundary, (*query)->ptr,
				Sprintf("%d", y)->ptr);
	    }
	    else if (f2->name && f2->name->length > 0 && f2->value) {
		*query = conv_form_encoding(f2->value, fi, Currentbuf, 0);
		if (f2->type == FORM_INPUT_FILE)
		    form_write_from_file(body, fi->parent->boundary, 
					 conv_form_encoding(f2->name, fi, Currentbuf, 0)->ptr,
					 (*query)->ptr,
					 Str_conv_to_system(f2->value)->ptr);
		else
		    form_write_data(body, fi->parent->boundary,
				    conv_form_encoding(f2->name, fi, Currentbuf, 0)->ptr,
				    (*query)->ptr);
	    }
	}
	else {
	    Str t;
	    /* not multipart */
	    if (f2->type == FORM_INPUT_IMAGE) {
		int x = 0, y = 0;
#ifdef USE_IMAGE
		getMapXY(Currentbuf, retrieveCurrentImg(Currentbuf), &x, &y);
#endif
		t = conv_form_encoding(f2->name, fi, Currentbuf, 0);
		t = Str_form_quote(t);
		Strcat(*query, t);
		Strcat(*query, Sprintf(".x=%d&", x));
		Strcat(*query, t);
		Strcat(*query, Sprintf(".y=%d", y));
	    }
	    else {
		/* not IMAGE */
		if (f2->name && f2->name->length > 0) {
		    t = conv_form_encoding(f2->name, fi, Currentbuf, 0);
		    Strcat(*query, Str_form_quote(t));
		    Strcat_char(*query, '=');
		}
		if (f2->value != NULL) {
		    if (fi->parent->method == FORM_METHOD_INTERNAL)
			Strcat(*query, Str_form_quote(f2->value));
		    else {
			t = conv_form_encoding(f2->value, fi, Currentbuf, 0);
			Strcat(*query, Str_form_quote(t));
		    }
		}
	    }
	    if (f2->next)
		Strcat_char(*query, '&');
	}
    }
    if (multipart) {
	fprintf(body, "--%s--\r\n", fi->parent->boundary);
	fclose(body);
    }
    else {
	/* remove trailing & */
	while (Strlastchar(*query) == '&')
	    Strshrink(*query, 1);
    }
}

/* process form */
struct followForm_post_arg {
    FormList *fl;
    FormItemList *fi;
};

static void
followForm_post(Buffer *buf, Phase0Env *p0env, void *chain_arg)
{
    struct followForm_post_arg *arg;
    FormList *fl;
    FormItemList *fi;

    arg = chain_arg;
    fl = arg->fl;
    fi = arg->fi;

    if (fl->method == FORM_METHOD_POST &&
	fl->enctype == FORM_ENCTYPE_MULTIPART)
	unlink(fl->body);

    if (buf && !(buf->bufferprop & BP_DISCARDED) &&
	buf->http_request_method == FORM_METHOD_POST)
	buf->form_submit = save_submit_formlist(fi);
}

struct textarea_arg {
    Anchor *a;
    Buffer *curbuf;
    FormItemList *fi;
    Str tmpname;
};

static void
followTextarea(void *arg)
{
    struct textarea_arg *p;
    Anchor *a;

    p = arg;
    input_textarea_read(p->fi, p->tmpname);

    if (!(p->curbuf->bufferprop & BP_DISCARDED)) {
	formItemResetBuffer(p->curbuf, p->fi);

	if (Currentbuf == p->curbuf)
	    displayBuffer(Currentbuf, B_FORCE_REDRAW);
    }

    if ((p->fi->accept || p->fi->parent->nitems == 1) &&
	(a = retrieveCurrentForm(Currentbuf)) &&
	a->hseq == p->a->hseq)
	followForm_internal(&main_p0env, TRUE);
}

static char *
inputTextarea(FormItemList *fi, Anchor *a, char *subtype)
{
    struct textarea_arg *arg;
    struct mailcap *mcap;
    Str cmd;

    arg = New(struct textarea_arg);
    arg->a = a;
    arg->curbuf = Currentbuf;
    arg->fi = fi;

    if (!(arg->tmpname = input_textarea_write(fi)))
	return strerror(errno);

    if (!(cmd = find_tty_editor(subtype, NULL, arg->tmpname, &mcap)) || !*cmd->ptr) {
	unlink(arg->tmpname->ptr);
	return "Editor not found";
    }

    system_bg(cmd->ptr, mcap->flags & MAILCAP_NEEDSTERMINAL,
	      followTextarea, arg, &main_p0env, "Edit textarea with %s");
    return NULL;
}

static void
followForm_internal(Phase0Env *p0env, int submit)
{
    Line *l;
    Anchor *a;
    char *p;
    FormItemList *fi, *f2;
    Str tmp, tmp2;
    int multipart = 0;

    if (Currentbuf->firstLine == NULL)
	return;
    l = Currentbuf->currentLine;

    a = retrieveCurrentForm(Currentbuf);
    if (a == NULL)
	return;
    fi = a->link->fi;
    switch (fi->type) {
    case FORM_INPUT_TEXT:
	if (submit)
	    goto do_submit;
	if (fi->readonly) {
	    disp_message_nsec("Read only field!", FALSE, 0, FALSE, TRUE);
	    return;
	}
	if (!inputTextarea(fi, a, "inputtext"))
	    break;
	p = inputStrHist("TEXT:", fi->value ? fi->value->ptr : NULL, TextHist);
	if (p == NULL)
	    return;
	fi->value = Strnew_charp(p);
	fi->lines = NULL;
	formItemResetBuffer(Currentbuf, fi);
	if (fi->accept || fi->parent->nitems == 1)
	    goto do_submit;
	break;
    case FORM_INPUT_FILE:
	if (submit)
	    goto do_submit;
	if (fi->readonly) {
	    disp_message_nsec("Read only field!", FALSE, 0, FALSE, TRUE);
	    return;
	}
	p = inputFilenameHist("Filename:", fi->value ? fi->value->ptr : NULL, NULL);
	if (p == NULL)
	    return;
	fi->value = Strnew_charp(p);
	fi->lines = NULL;
	formItemResetBuffer(Currentbuf, fi);
	if (fi->accept || fi->parent->nitems == 1)
	    goto do_submit;
	break;
    case FORM_INPUT_PASSWORD:
	if (submit)
	    goto do_submit;
	if (fi->readonly) {
	    disp_message_nsec("Read only field!", FALSE, 0, FALSE, TRUE);
	    return;
	}
	p = inputLine("Password::", fi->value ? fi->value->ptr : NULL, IN_PASSWORD);
	if (p == NULL)
	    return;
	fi->value = Strnew_charp(p);
	fi->lines = NULL;
	formItemResetBuffer(Currentbuf, fi);
	if (fi->accept)
	    goto do_submit;
	break;
    case FORM_TEXTAREA:
	if (submit)
	    goto do_submit;
	if (fi->readonly) {
	    disp_message_nsec("Read only field!", FALSE, 0, FALSE, TRUE);
	    return;
	}
	if ((p = inputTextarea(fi, a, "textarea")))
	    disp_err_message(p, FALSE);
	break;
    case FORM_INPUT_RADIO:
	if (submit)
	    goto do_submit;
	if (fi->readonly) {
	    disp_message_nsec("Read only field!", FALSE, 0, FALSE, TRUE);
	    return;
	}
	formRecheckRadio(a, Currentbuf, fi);
	break;
    case FORM_INPUT_CHECKBOX:
	if (submit)
	    goto do_submit;
	if (fi->readonly) {
	    disp_message_nsec("Read only field!", FALSE, 0, FALSE, TRUE);
	    return;
	}
	fi->checked = !fi->checked;
	formItemResetBuffer(Currentbuf, fi);
	break;
#ifdef MENU_SELECT
    case FORM_SELECT:
	if (submit)
	    goto do_submit;
	if (!formChooseOptionByMenu(fi,
				    Currentbuf->view->rootX + Currentbuf->lmargin + Currentbuf->cursorX - Currentbuf->pos + a->start.pos,
				    Currentbuf->view->rootY + Currentbuf->cursorY))
	    break;
	formItemResetBuffer(Currentbuf, fi);
	if (fi->parent->nitems == 1)
	    goto do_submit;
	break;
#endif				/* MENU_SELECT */
    case FORM_INPUT_IMAGE:
    case FORM_INPUT_SUBMIT:
    case FORM_INPUT_BUTTON:
    do_submit:
    tmp = Strnew();
    tmp2 = Strnew();
    multipart = (fi->parent->method == FORM_METHOD_POST &&
		 fi->parent->enctype == FORM_ENCTYPE_MULTIPART);
    query_from_followform(&tmp, fi, multipart);

    tmp2 = Strdup(fi->parent->action);
    if (!Strcmp_charp(tmp2, "!CURRENT_URL!")) {
	/* It means "current URL" */
	tmp2 = parsedURL2Str(&Currentbuf->currentURL);
	if ((p = strchr(tmp2->ptr, '?')) != NULL)
	    Strshrink(tmp2, (tmp2->ptr + tmp2->length) - p);
    }

    if (fi->parent->method == FORM_METHOD_GET) {
	Strcat_charp(tmp2, "?");
	Strcat(tmp2, tmp);
	loadLink(tmp2->ptr, fi->parent->target, NULL, NULL, NULL, NULL, p0env);
    }
    else if (fi->parent->method == FORM_METHOD_POST) {
	struct followForm_post_arg *arg;

	if (multipart) {
	    struct stat st;
	    stat(fi->parent->body, &st);
	    fi->parent->length = st.st_size;
	}
	else {
	    fi->parent->body = tmp->ptr;
	    fi->parent->length = tmp->length;
	}
	arg = New(struct followForm_post_arg);
	arg->fl = fi->parent;
	arg->fi = fi;
	loadLink(tmp2->ptr, fi->parent->target, NULL, fi->parent, followForm_post, arg, p0env);
    }
    else if ((fi->parent->method == FORM_METHOD_INTERNAL &&
	      (!Strcmp_charp(fi->parent->action,"map") || !Strcmp_charp(fi->parent->action, "none"))) ||
	     Currentbuf->bufferprop & BP_INTERNAL) {	/* internal */
	do_internal(tmp2->ptr, tmp->ptr);
    }
    else {
	disp_err_message("Can't send form because of illegal method.", FALSE);
    }
    break;
    case FORM_INPUT_RESET:
	for (f2 = fi->parent->item; f2; f2 = f2->next) {
	    if (f2->name && f2->value &&
		f2->type != FORM_INPUT_SUBMIT &&
		f2->type != FORM_INPUT_HIDDEN &&
		f2->type != FORM_INPUT_RESET) {
		if (f2->value != f2->init_value) {
		    f2->value = f2->init_value;
		    f2->lines = NULL;
		}

		f2->checked = f2->init_checked;
#ifdef MENU_SELECT
		f2->label = f2->init_label;
		f2->selected = f2->init_selected;
#endif				/* MENU_SELECT */
	    }
	}
	break;
    case FORM_INPUT_HIDDEN:
    default:
	break;
    }
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

void
submitForm(void)
{
    followForm_internal(&main_p0env, FALSE);
}

void
followForm(void)
{
    followForm_internal(&main_p0env, FALSE);
}

/* go to the top anchor */
void
topA(void)
{
    HmarkerList *hl = Currentbuf->hmarklist;
    BufferPoint *po;
    Anchor *an;
    int hseq, n;

    if (Currentbuf->firstLine == NULL)
	return;
    if (!hl || hl->firstseq < 0)
	return;

    for (hseq = hl->firstseq, n = prec_num ; n > 1 ; --n)
	if ((hseq = hl->marks[hseq].next) < 0) {
	    hseq = hl->firstseq;
	    break;
	}
    do {
	po = &hl->marks[hseq].po;
	an = retrieveAnchor(Currentbuf->href, po->line, po->pos);
	if (an == NULL)
	    an = retrieveAnchor(Currentbuf->formitem, po->line, po->pos);
	hseq = hl->marks[hseq].next;
	if (hseq < 0)
	    return;
    } while (an == NULL);

    gotoLine(Currentbuf, po->line);
    Currentbuf->pos = po->pos;
    arrangeCursor(Currentbuf);
    displayBuffer(Currentbuf, B_NORMAL);
}

/* go to the last anchor */
void
lastA(void)
{
    HmarkerList *hl = Currentbuf->hmarklist;
    BufferPoint *po;
    Anchor *an;
    int hseq, n;

    if (Currentbuf->firstLine == NULL)
	return;
    if (!hl || hl->lastseq < 0)
	return;

    for (hseq = hl->lastseq, n = prec_num ; n > 1 ; --n)
	if ((hseq = hl->marks[hseq].prev) < 0) {
	    hseq = hl->lastseq;
	    break;
	}
    do {
	po = &hl->marks[hseq].po;
	an = retrieveAnchor(Currentbuf->href, po->line, po->pos);
	if (an == NULL)
	    an = retrieveAnchor(Currentbuf->formitem, po->line, po->pos);
	hseq = hl->marks[hseq].prev;
	if (hseq < 0)
	    return;
    } while (an == NULL);

    gotoLine(Currentbuf, po->line);
    Currentbuf->pos = po->pos;
    arrangeCursor(Currentbuf);
    displayBuffer(Currentbuf, B_NORMAL);
}

/* go to the next anchor */
void
nextA(void)
{
    HmarkerList *hl;
    BufferPoint *po;
    Anchor *an, *pan;
    int i, n, x, y;

    if (Currentbuf->firstLine == NULL)
	return;
    if (!(hl = Currentbuf->hmarklist) || hl->nmark == 0)
	return;

    an = retrieveCurrentAnchor(Currentbuf);
    if (an == NULL)
	an = retrieveCurrentForm(Currentbuf);

    y = Currentbuf->currentLine->linenumber;
    x = Currentbuf->pos;

    for (i = 0, n = searchKeyNum(); i < n; i++) {
	pan = an;
	if (an && an->hseq >= 0) {
	    int hseq = an->hseq;
	    do {
		hseq = hl->marks[hseq].next;
		if (hseq < 0) {
		    pan = an;
		    goto _end;
		}
		po = &hl->marks[hseq].po;
		an = retrieveAnchor(Currentbuf->href, po->line, po->pos);
		if (an == NULL)
		    an = retrieveAnchor(Currentbuf->formitem, po->line, po->pos);
	    } while (an == NULL || an == pan || an->hseq < 0);
	}
	else {
	    an = closest_next_anchor(Currentbuf->href, NULL, x, y);
	    an = closest_next_anchor(Currentbuf->formitem, an, x, y);
	    if (an == NULL) {
		an = pan;
		break;
	    }
	    x = an->start.pos;
	    y = an->start.line;
	}
    }

_end:
    if (an == NULL || an->hseq < 0)
	return;
    po = &hl->marks[an->hseq].po;
    gotoLine(Currentbuf, po->line);
    Currentbuf->pos = po->pos;
    arrangeCursor(Currentbuf);
    displayBuffer(Currentbuf, B_NORMAL);
}

void
physNextA(void)
{
    HmarkerList *hl;
    BufferPoint cur, cur1, *po, *po1;
    Anchor *an, *an1, *pan;
    int i, n;

    if (Currentbuf->firstLine == NULL)
	return;

    if (!(hl = Currentbuf->hmarklist) || hl->nmark == 0)
	return;

    if (!(an = retrieveCurrentAnchor(Currentbuf)))
	an = retrieveCurrentForm(Currentbuf);

    cur.line = Currentbuf->currentLine->linenumber;
    cur.pos = Currentbuf->pos;

    if ((n = searchKeyNum()) > 0)
	for (i = 0 ;;) {
	    pan = an;
	    an = closest_next_anchor(Currentbuf->href, NULL, cur.pos, cur.line);
	    an = closest_next_anchor(Currentbuf->formitem, an, cur.pos, cur.line);

	    if (!an) {
		an = pan;
		break;
	    }

	    po = (an->hseq >= 0 && an->hseq < hl->nmark) ? &hl->marks[an->hseq].po : &an->start;
	    cur1 = *po;

	    do {
		an1 = closest_prev_anchor(Currentbuf->href, NULL, cur1.pos, cur1.line);
		an1 = closest_prev_anchor(Currentbuf->formitem, an1, cur1.pos, cur1.line);

		if (!an1)
		    break;

		po1 = (an1->hseq >= 0 && an1->hseq < hl->nmark) ? &hl->marks[an1->hseq].po : &an1->start;

		if (bpcmp(*po1, cur) > 0 && bpcmp(*po1, *po) < 0) {
		    an = an1;
		    po = po1;
		}

		cur1 = an1->start;
	    } while (bpcmp(cur1, cur) > 0);

	    if (bpcmp(*po, cur) <= 0) {
		cur = an->end;
		an = pan;
	    }
	    else if (++i < n)
		cur = an->end;
	    else
		break;
	}

    if (an == NULL || an->hseq < 0 || an->hseq >= hl->nmark)
	return;

    po = &hl->marks[an->hseq].po;
    gotoLine(Currentbuf, po->line);
    Currentbuf->pos = po->pos;
    arrangeCursor(Currentbuf);
    displayBuffer(Currentbuf, B_NORMAL);
}

/* go to the previous anchor */
void
prevA(void)
{
    HmarkerList *hl = Currentbuf->hmarklist;
    BufferPoint *po;
    Anchor *an, *pan;
    int i, n, x, y;

    if (Currentbuf->firstLine == NULL)
	return;
    if (!hl || hl->nmark == 0)
	return;

    an = retrieveCurrentAnchor(Currentbuf);
    if (an == NULL)
	an = retrieveCurrentForm(Currentbuf);

    y = Currentbuf->currentLine->linenumber;
    x = Currentbuf->pos;

    for (i = 0, n = searchKeyNum(); i < n; i++) {
	pan = an;
	if (an && an->hseq >= 0) {
	    int hseq = an->hseq;
	    do {
		hseq = hl->marks[hseq].prev;
		if (hseq < 0) {
		    an = pan;
		    goto _end;
		}
		po = &hl->marks[hseq].po;
		an = retrieveAnchor(Currentbuf->href, po->line, po->pos);
		if (an == NULL)
		    an = retrieveAnchor(Currentbuf->formitem, po->line, po->pos);
	    } while (an == NULL || an == pan);
	}
	else {
	    an = closest_prev_anchor(Currentbuf->href, NULL, x, y);
	    an = closest_prev_anchor(Currentbuf->formitem, an, x, y);
	    if (an == NULL) {
		an = pan;
		break;
	    }
	    x = an->start.pos;
	    y = an->start.line;
	}
    }

_end:
    if (an == NULL || an->hseq < 0)
	return;
    po = &hl->marks[an->hseq].po;
    gotoLine(Currentbuf, po->line);
    Currentbuf->pos = po->pos;
    arrangeCursor(Currentbuf);
    displayBuffer(Currentbuf, B_NORMAL);
}

void
physPrevA(void)
{
    HmarkerList *hl;
    BufferPoint cur, cur1, *po, *po1;
    Anchor *an, *pan, *an1;
    int i, n;

    if (!Currentbuf->firstLine)
	return;

    if (!(hl = Currentbuf->hmarklist) || !hl->nmark)
	return;

    if (!(an = retrieveCurrentAnchor(Currentbuf)))
	an = retrieveCurrentForm(Currentbuf);

    cur.line = Currentbuf->currentLine->linenumber;
    cur.pos = Currentbuf->pos;

    if ((n = searchKeyNum()) > 0)
	for (i = 0 ;;) {
	    pan = an;
	    an = closest_prev_anchor(Currentbuf->href, NULL, cur.pos, cur.line);
	    an = closest_prev_anchor(Currentbuf->formitem, an, cur.pos, cur.line);

	    if (!an) {
		an = pan;
		break;
	    }

	    po = (an->hseq >= 0 && an->hseq < hl->nmark) ? &hl->marks[an->hseq].po : &an->start;
	    cur1 = *po;

	    do {
		an1 = closest_next_anchor(Currentbuf->href, NULL, cur1.pos, cur1.line);
		an1 = closest_next_anchor(Currentbuf->formitem, an1, cur1.pos, cur1.line);

		if (!an1)
		    break;

		po1 = (an1->hseq >= 0 && an1->hseq < hl->nmark) ? &hl->marks[an1->hseq].po : &an1->start;

		if (bpcmp(*po1, cur) < 0 && bpcmp(*po1, *po) > 0) {
		    an = an1;
		    po = po1;
		}

		cur1 = an1->end;
	    } while (bpcmp(cur1, cur) < 0);

	    if (bpcmp(*po, cur) >= 0) {
		cur = an->start;
		an = pan;
	    }
	    else if (++i < n)
		cur = an->start;
	    else
		break;
	}

    if (!an || an->hseq < 0 || an->hseq >= hl->nmark)
	return;

    po = &hl->marks[an->hseq].po;
    gotoLine(Currentbuf, po->line);
    Currentbuf->pos = po->pos;
    arrangeCursor(Currentbuf);
    displayBuffer(Currentbuf, B_NORMAL);
}

/* go to the next left/right anchor */
static void
nextX(int d, int dy)
{
    HmarkerList *hl = Currentbuf->hmarklist;
    Anchor *an, *pan;
    Line *l;
    int i, x, y, n;

    if (Currentbuf->firstLine == NULL)
	return;
    if (!hl || hl->nmark == 0)
	return;

    an = retrieveCurrentAnchor(Currentbuf);
    if (an == NULL)
	an = retrieveCurrentForm(Currentbuf);

    l = Currentbuf->currentLine;
    x = Currentbuf->pos;
    y = l->linenumber;
    pan = NULL;
    for (i = 0, n = searchKeyNum(); i < n; i++) {
	if (an)
	    x = (d > 0) ? an->end.pos : an->start.pos - 1;
	an = NULL;
	while (1) {
	    for (; x >= 0 && x < l->len; x += d) {
		an = retrieveAnchor(Currentbuf->href, y, x);
		if (! an)
		    an = retrieveAnchor(Currentbuf->formitem, y, x);
		if (an) {
		    pan = an;
		    break;
		}
	    }
	    if (! dy || an)
		break;
	    l = (dy > 0) ? l->next : l->prev;
	    if (! l)
		break;
	    x = (d > 0) ? 0 : l->len - 1;
	    y = l->linenumber;
	}
	if (! an)
	    break;
    }

    if (pan == NULL)
	return;
    gotoLine(Currentbuf, y);
    Currentbuf->pos = pan->start.pos;
    arrangeCursor(Currentbuf);
    displayBuffer(Currentbuf, B_NORMAL);
}

/* go to the next downward/upward anchor */
static void
nextY(int d)
{
    HmarkerList *hl = Currentbuf->hmarklist;
    Anchor *an, *pan;
    int i, x, y, n = searchKeyNum();
    int hseq;

    if (Currentbuf->firstLine == NULL)
	return;
    if (!hl || hl->nmark == 0)
	return;

    an = retrieveCurrentAnchor(Currentbuf);
    if (an == NULL)
	an = retrieveCurrentForm(Currentbuf);

    x = Currentbuf->pos;
    y = Currentbuf->currentLine->linenumber + d;
    pan = NULL;
    hseq = -1;
    for (i = 0; i < n; i++) {
	if (an)
	    hseq = abs(an->hseq);
	an = NULL;
	for (; y >= 0 && y <= Currentbuf->lastLine->linenumber; y += d) {
	    an = retrieveAnchor(Currentbuf->href, y, x);
	    if (! an)
		an = retrieveAnchor(Currentbuf->formitem, y, x);
	    if (an && hseq != abs(an->hseq)) {
		pan = an;
		break;
	    }
	}
	if (! an)
	    break;
    }

    if (pan == NULL)
	return;
    gotoLine(Currentbuf, pan->start.line);
    arrangeLine(Currentbuf);
    displayBuffer(Currentbuf, B_NORMAL);
}

/* go to the next left anchor */
void
nextL(void)
{
    nextX(-1, 0);
}

/* go to the next left-up anchor */
void
nextLU(void)
{
    nextX(-1, -1);
}

/* go to the next right anchor */
void
nextR(void)
{
    nextX(1, 0);
}

/* go to the next right-down anchor */
void
nextRD(void)
{
    nextX(1, 1);
}

/* go to the next downward anchor */
void
nextD(void)
{
    nextY(1);
}

/* go to the next upward anchor */
void
nextU(void)
{
    nextY(-1);
}

static void discardViews(BufferView *v);

static void
discardView(BufferView *v)
{
    Buffer *buf;

    if (!v)
	return;

    if (v->frameset) {
	int i;

	for (i = v->nrows * v->ncols ; i > 0 ;)
	    discardViews(v->subv[--i].top);
    }

    for (buf = v->top ; buf ; buf = buf->down)
	discardBuffer(buf);
}

static void
discardViews(BufferView *v)
{
    for (; v ; v = v->down)
	discardView(v);
}

static Buffer *
backBf_next(Buffer *cur)
{
    Buffer *next;

    for (next = cur->next ; next && !(next->bufferprop & BP_VISIBLE) ; next = next->next)
	;

    return next;
}

static BufferView *
backBf_view(Buffer *cur)
{
    BufferView *v;

    for (v = cur->view ; v && !v->down && !v->up ; v = v->sup)
	;

    return v;
}

static BufferView *
backBf_on_view(BufferView *v, Buffer *next)
{
    BufferView *nv;

    for (nv = next->view ; nv && nv != v ; nv = nv->sup)
	;

    return nv;
}

static int
backBf_internal(Buffer *cur)
{
    if (cur->posHist) {
	restorePosition(cur, cur->posHist);
	cur->posHist = cur->posHist->posHist;
	return 1;
    }
    else {
	Buffer *next;

	if ((next = backBf_next(cur))) {
	    if (cur->down || cur->up) {
		discardBuffer(cur);
		updateCurrentbuf(next);
		return 1;
	    }
	    else {
		BufferView *v;
	    find_view:
		if ((v = backBf_view(cur))) {
		    Buffer *fbuf;

		    if (backBf_on_view(v, next)) {
			if ((next = backBf_next(next)))
			    goto find_view;

			return 0;
		    }

		    if (v->frameset && (fbuf = v->frameset->origin) &&
			(fbuf->bufferprop & (BP_LINKED | BP_FRAMESET)) == (BP_LINKED | BP_FRAMESET)) {
			BufferView *fv;

			if (fbuf == next) {
			    if ((next = backBf_next(next)))
				goto find_view;

			    return 0;
			}
			else if (!fbuf->down && !fbuf->up &&
				 (fv = backBf_view(v->frameset->origin))) {
			    if (backBf_on_view(fv, next)) {
				if ((next = backBf_next(next)))
				    goto find_view;

				return 0;
			    }

			    discardView(fv);
			}
			else
			    discardBuffer(fbuf);

			goto find_view;
		    }

		    nextBufferView(v);
		    discardView(v);
		    updateCurrentbuf(next);
		    return 1;
		}
	    }
	}
    }

    return 0;
}

void
backBf(void)
{
    int n;

    if ((n = searchKeyNum()) > 0) {
	int i;

	i = n;

#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
	for (; i > 0 && CurrentMenu && CurrentMenuPopup ; --i)
	    popdown_menu(FALSE, FALSE);
#endif

	for (; i > 0 && backBf_internal(Currentbuf) ; --i)
	    ;

	if (i > 0)
	    disp_message("Can't back...", TRUE);

	if (i < n) {
	    checkCurrentbuf();
	    displayCurrentView(NULL);
	}
    }
}

/* go to the next bufferr */
void
nextBf(void)
{
    Buffer *buf;
    int i, n;

    for (i = n = PREC_NUM ; i > 0 ; --i) {
	for (buf = Currentbuf->next ; buf ; buf = buf->next)
	    if (buf->bufferprop & BP_VISIBLE)
		break;

	if (!buf)
	    break;

	Currentbuf = buf;
    }

    if (i < n)
	displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

/* go to the previous bufferr */
void
prevBf(void)
{
    Buffer *buf;
    int i, n;

    for (i = n = PREC_NUM ; i > 0 ; --i) {
	for (buf = Currentbuf->prev ; buf ; buf = buf->prev)
	    if (buf->bufferprop & BP_VISIBLE)
		break;

	if (!buf)
	    break;

	Currentbuf = buf;
    }

    if (i < n)
	displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

void
deletePrevBuf()
{
    if (Currentbuf->next)
	delBuffer(Currentbuf->next);
}

static void
cmd_loadURL(char *url, ParsedURL *current, char *referer, Buffer *repbuf, Phase0Env *p0env_org)
{
    Buffer *buf;
    Phase0Env p0env;

    p0env = *p0env_org;

    if (repbuf) {
	p0env.flag |= RG_REPBUF;
	p0env.curbuf = repbuf;
    }

    refresh();
    buf = loadGeneralFile(url, current, referer, &p0env, NULL);
    if (buf == NULL) {
	char *emsg = Sprintf("Can't load %s", conv_from_system(url))->ptr;
	disp_err_message(emsg, FALSE);
    }
    else if (buf == NO_BUFFER || buf->async_buf) {
	if (p0env.flag & RG_HIST) {
	    ParsedURL pu;

	    parseURL2(url, &pu, current);
	    pushHashHist(URLHist, parsedURL2Str(&pu)->ptr);
	}
    }
    else {
	if (p0env.flag & RG_HIST)
	    pushHashHist(URLHist, parsedURL2Str(&buf->currentURL)->ptr);

	if (repbuf && repbuf->bufferprop & BP_LINKED)
	    replaceBuffer(repbuf, buf);
	else
	    pushBuffer(buf);

	if (p0env.RenderFrame && buf->frameset) {
	    Buffer *fbuf;

	    if ((fbuf = rFrame_internal(buf, p0env_org)) && !fbuf->async_buf) {
		pushBuffer(fbuf);
		displayCurrentView(NULL);
	    }
	}
    }
    displayBuffer(Currentbuf, B_NORMAL);
}


static void
goURL_internal(char *prompt, int relative, Buffer *repbuf, Phase0Env *p0env_orig)
{
    char *url, *referer;
    ParsedURL p_url, *current;
    Phase0Env p0env;
    Buffer *curbuf;

    p0env = *p0env_orig;
    p0env.flag |= RG_HIST;
    if (repbuf) {
	if (repbuf->view)
	    p0env.view = repbuf->view;

	curbuf = repbuf;
    }
    else
	curbuf = Currentbuf;
    url = searchKeyData();
    if (url == NULL) {
	Hist *hist = copyHist(URLHist);
	Anchor *a;

	current = baseURL(curbuf);
	if (current) {
	    char *c_url = parsedURL2Str(current)->ptr;
	    if (DefaultURLString == DEFAULT_URL_CURRENT)
		url = c_url;
	    else
		pushHist(hist, c_url);
	}
	a = retrieveCurrentAnchor(curbuf);
	if (a) {
	    char *a_url;
	    parseURL2(a->link->href->url, &p_url, current);
	    a_url = parsedURL2Str(&p_url)->ptr;
	    if (DefaultURLString == DEFAULT_URL_LINK)
		url = a_url;
	    else
		pushHist(hist, a_url);
	}
	url = inputLineHist(prompt, url, IN_URL, hist);
	if (url != NULL)
	    SKIP_BLANKS(url);
    }
#ifdef MANY_CHARSET
    if (url != NULL) {
	if (curbuf->document_encoding)
	    url = conv_str2isoStr(url, "@|",
				  curbuf->document_encoding,
				  MB_FLAG_ASCIIATCTL | MB_FLAG_DISCARD_NOTPREFERED_CHAR)->ptr;
	else
	    url = conv_to_system(url);
    }
#endif
#ifdef JP_CHARSET
    if (url != NULL) {
	if (curbuf->document_encoding)
	    url = conv(url, InnerCode, curbuf->document_encoding)->ptr;
	else
	    url = conv_to_system(url);
    }
#endif
    if (url == NULL || *url == '\0') {
	displayBuffer(curbuf, B_FORCE_REDRAW);
	return;
    }
    if (!(p0env.flag & RG_DO_DOWNLOAD) && *url == '#') {
	gotoLabel(url + 1, curbuf);
	return;
    }
    if (relative) {
	current = baseURL(curbuf);
	referer = parsedURL2Str(&curbuf->currentURL)->ptr;
    }
    else {
	current = NULL;
	referer = NULL;
    }
    parseURL2(url, &p_url, current);
    pushHashHist(URLHist, parsedURL2Str(&p_url)->ptr);
    cmd_loadURL(url, current, referer, repbuf, &p0env);
}

/* go to specified URL */
void
goURL(void)
{
    goURL_internal("Goto URL: ", FALSE, NULL, &main_p0env);
}

/* go to relatively specified URL */
void
gorURL(void)
{
    goURL_internal("Goto relative URL: ", TRUE, NULL, &main_p0env);
}

/* download specified URL */
void
svURL(void)
{
    Phase0Env p0env;

    p0env = main_p0env;
    p0env.flag |= RG_DO_DOWNLOAD;
    goURL_internal("Saved URL: ", FALSE, NULL, &p0env);
}

/* replace Currentbuf with specified URL */
void
repBf(void)
{
    if (Argumentbuf && onCurrentview(Argumentbuf))
	goURL_internal("Goto URL: ", TRUE, Argumentbuf, &main_p0env);
}

static void
cmd_loadBuffer(Buffer *buf, int prop, int linkid)
{
    if (!buf)
	disp_err_message("Can't load string", FALSE);
    else if (buf != NO_BUFFER) {
	buf->bufferprop |= (BP_INTERNAL | prop);

	if (!(buf->bufferprop & BP_NO_URL))
	    copyParsedURL(&buf->currentURL, &Currentbuf->currentURL);

	if (linkid != LB_NOLINK) {
	    buf->linkBuffer[REV_LB[linkid]] = Currentbuf;
	    Currentbuf->linkBuffer[linkid] = buf;
	}

	pushBuffer(buf);
    }
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

/* load bookmark */
void
ldBmark(void)
{
    cmd_loadURL(BookmarkFile, NULL, NO_REFERER, NULL, &main_p0env);
}


/* Add current to bookmark */
void
adBmark(void)
{
    Str url, title, tmp;
#ifdef MANY_CHARSET
    const char *cs;
#endif
#ifdef MANY_CHARSET
    cs = lookup_process_charset(W3MBOOKMARK_CMDNAME);
    tmp = conv_str2isoStr(parsedURL2Str(&Currentbuf->currentURL)->ptr, "@", cs);
    url = Str_form_quote(tmp);
    tmp = conv_str2isoStr(Currentbuf->buffername, "@", cs);
    title = Str_form_quote(tmp);
#else
    tmp = parsedURL2Str(&Currentbuf->currentURL);
    url = Str_form_quote(tmp);
    title = Str_form_quote(Strnew_charp(Currentbuf->buffername));
#endif
    tmp = Sprintf("file://%s/" W3MBOOKMARK_CMDNAME
		  "?mode=panel&bmark=%s&url=%s&title=%s"
#ifdef MANY_CHARSET
		  "&charset=%s"
#endif
		  , w3m_lib_dir(), Str_form_quote(Strnew_charp(BookmarkFile))->ptr,
		  url->ptr, title->ptr
#ifdef MANY_CHARSET
		  , cs
#endif
		  );
    cmd_loadURL(tmp->ptr, NULL, NO_REFERER, NULL, &main_p0env);
}

/* set an option */
void
setOpt(void)
{
    char *opt;

    CurrentKeyData = NULL;   /* not allowed in w3m-control: */
    TargetX = TargetY = PrevTargetX = PrevTargetY = -1;
    opt = searchKeyData();
    if (opt == NULL || *opt == '\0' || strchr(opt, '=') == NULL) {
	if (opt != NULL && *opt != '\0') {
	    char *v = get_param_option(opt);
	    opt = Sprintf("%s=%s", opt, v ? v : "")->ptr;
	}
	opt = inputWordsHistTab("Set option: ", opt, TextHist, rc_paramtab);
	if (opt == NULL || *opt == '\0') {
	    displayBuffer(Currentbuf, B_NORMAL);
	    return;
	}
    }
    if (set_param_option(opt))
	sync_with_option();
    displayCurrentView(NULL);
}

/* option setting */
void
ldOpt(void)
{
    cmd_loadBuffer(load_option_panel(), BP_NO_URL, LB_NOLINK);
}

/* error message list */
void
msgs(void)
{
    cmd_loadBuffer(message_list_panel(), BP_NO_URL, LB_NOLINK);
}

/* page info */
void
pginfo(void)
{
    Buffer *buf;

    if ((buf = Currentbuf->linkBuffer[LB_N_INFO]) != NULL) {
	pushBuffer(buf);
	displayBuffer(Currentbuf, B_NORMAL);
	return;
    }
    if ((buf = Currentbuf->linkBuffer[LB_INFO]) != NULL)
	delBuffer(buf);
    buf = page_info_panel(Currentbuf, NULL);
#ifdef MANY_CHARSET
    if (buf != NULL && !buf->document_encoding)
	buf->document_encoding = buf->document_charset = Currentbuf->document_encoding;
#else
#ifdef JP_CHARSET
    if (buf != NULL)
	buf->document_encoding = Currentbuf->document_encoding;
#endif				/* JP_CHARSET */
#endif
    cmd_loadBuffer(buf, BP_NO_URL, LB_INFO);
}

void
follow_map(struct parsed_tagarg *arg)
{
    char *name = tag_get_value(arg, "link");
#if defined(MENU_MAP) || defined(USE_IMAGE)
    Anchor *an;
    MapArea *a;
    int x, y;
    ParsedURL p_url;

    an = retrieveCurrentImg(Currentbuf);
    x = Currentbuf->cursorX + Currentbuf->lmargin + Currentbuf->view->rootX;
    y = Currentbuf->cursorY + Currentbuf->view->rootY;
    a = follow_map_menu(Currentbuf, name, an, x, y);
    if (a == NULL || a->url == NULL || *(a->url) == '\0') {
#endif
#ifndef MENU_MAP
	Buffer *buf = follow_map_panel(Currentbuf, name);

	if (buf != NULL) {
#ifdef MANY_CHARSET
	    buf->document_encoding = Currentbuf->document_encoding;
#elif defined(JP_CHARSET)
	    buf->document_code = Currentbuf->document_code;
#endif				/* JP_CHARSET */
	    cmd_loadBuffer(buf, BP_NORMAL, LB_NOLINK);
	}
#endif
#if defined(MENU_MAP) || defined(USE_IMAGE)
	return;
    }
    if (*(a->url) == '#') {
	gotoLabel(a->url + 1, Currentbuf);
	return;
    }
    parseURL2(a->url, &p_url, baseURL(Currentbuf));
    pushHashHist(URLHist, parsedURL2Str(&p_url)->ptr);
    cmd_loadURL(a->url, baseURL(Currentbuf),
		parsedURL2Str(&Currentbuf->currentURL)->ptr,
		NULL, &main_p0env);
#endif
}

#ifdef USE_COOKIE
/* cookie list */
void
cooLst(void)
{
    Buffer *buf;

    buf = cookie_list_panel();
    if (buf != NULL)
	cmd_loadBuffer(buf, BP_NO_URL, LB_NOLINK);
}
#endif				/* USE_COOKIE */

#ifdef USE_HISTORY
/* History page */
void
ldHist(void)
{
    cmd_loadBuffer(historyBuffer(URLHist), BP_NO_URL, LB_NOLINK);
}
#endif				/* USE_HISTORY */

/* download HREF link */
void
svA(void)
{
    Phase0Env p0env;

    ForcedKeyData = CurrentKeyData = NULL;	/* not allowed in w3m-control: */
    TargetX = TargetY = PrevTargetX = PrevTargetY = -1;
    p0env = main_p0env;
    p0env.flag |= RG_DO_DOWNLOAD;
    followA_internal(&p0env);
}

/* download IMG link */
void
svI(void)
{
    Phase0Env p0env;

    ForcedKeyData = CurrentKeyData = NULL;	/* not allowed in w3m-control: */
    TargetX = TargetY = PrevTargetX = PrevTargetY = -1;
    p0env = main_p0env;
    p0env.flag |= RG_DO_DOWNLOAD;
    followI_internal(&p0env);
}

/* save buffer */
void
svBuf(void)
{
    char *qfile = NULL, *file;
    FILE *f;
    int is_pipe;

    CurrentKeyData = NULL;	/* not allowed in w3m-control: */
    TargetX = TargetY = PrevTargetX = PrevTargetY = -1;
    file = searchKeyData();
    if (file == NULL || *file == '\0') {
    	qfile = inputLineHist("Save buffer to: ", NULL, IN_COMMAND, SaveHist);
	if (qfile == NULL || *qfile == '\0') {
	    displayBuffer(Currentbuf, B_NORMAL);
	    return;
	}
    }
    file = conv_to_system(qfile ? qfile : file);
    if (*file == '|') {
	is_pipe = TRUE;
	f = popen(file + 1, "w");
    }
    else {
	if (qfile) {
	    file = unescape_spaces(Strnew_charp(qfile))->ptr;
	    file = conv_to_system(file);
	}
	file = expandName(file);
	if (checkOverWrite(file) < 0)
	    return;
	f = fopen(file, "w");
	is_pipe = FALSE;
    }
    if (f == NULL) {
	char *emsg = Sprintf("Can't open %s", file)->ptr;
	disp_err_message(emsg, TRUE);
	return;
    }
    saveBuffer(Currentbuf, f);
    if (is_pipe)
	pclose(f);
    else
	fclose(f);
    displayBuffer(Currentbuf, B_NORMAL);
}

/* save source */
void
svSrc(void)
{
    char *file;

    if (Currentbuf->sourcefile == NULL)
	return;
    ForcedKeyData = CurrentKeyData = NULL;	/* not allowed in w3m-control: */
    TargetX = TargetY = PrevTargetX = PrevTargetY = -1;
    PermitSaveToPipe = TRUE;
    if (Currentbuf->real_scheme == SCM_LOCAL)
	file = conv_from_system(guess_save_name(Currentbuf->currentURL.real_file));
    else if (Currentbuf->name_by_header)
	file = guess_save_name(Currentbuf->name_by_header);
    else
	file = guess_save_name(Currentbuf->currentURL.file);
#ifdef USE_IMAGE
    if (!strncasecmp(Currentbuf->real_type, "image/", sizeof("image/") - 1)) {
	Anchor *a;

	if ((a = retrieveCurrentImg(Currentbuf)) &&
	    copyImageFile(a->link->img, &main_p0env))
	    return;
    }
#endif
    doFileCopy(Currentbuf->sourcefile, file, &main_p0env);
    PermitSaveToPipe = FALSE;
    displayBuffer(Currentbuf, B_NORMAL);
}

static void
_peekURL(int only_img)
{
    Anchor *a;
    ParsedURL pu;
    static Str s = NULL;
    static int offset = 0;
    int n;

    if (Currentbuf->firstLine == NULL)
	return;
    if (CurrentKey == prev_key && s != NULL) {
	if (s->length - offset >= Currentbuf->view->width - Currentbuf->rmargin - Currentbuf->lmargin)
	    offset++;
	else if (s->length <= offset)	/* bug ? */
	    offset = 0;
	goto disp;
    } else {
	offset = 0;
    }
    a = only_img ? NULL : retrieveCurrentAnchor(Currentbuf);
    if (a == NULL) {
	a = (only_img ? NULL : retrieveCurrentForm(Currentbuf));
	if (a == NULL) {
	    a = retrieveCurrentImg(Currentbuf);
	    if (a == NULL) {
		s = NULL;
		return;
	    }
	    parseURL2(a->link->img->url, &pu, baseURL(Currentbuf));
	    goto disp1;
	}
	else {
	    s = Strnew_charp(form2str(a->link->fi));
	    goto disp;
	}
    }
    parseURL2(a->link->href->url, &pu, baseURL(Currentbuf));
disp1:
    s = parsedURL2Str(&pu);
disp:
    if ((n = searchKeyNum()) > 1 && s->length > (n - 1) * (Currentbuf->view->width - Currentbuf->rmargin - Currentbuf->lmargin - 1))
	disp_message_nomouse(&s->ptr[(n - 1) * (Currentbuf->view->width - Currentbuf->rmargin - Currentbuf->lmargin - 1)], TRUE);
    else
	disp_message_nomouse(&s->ptr[offset], TRUE);
}

/* peek URL */
void
peekURL(void)
{
    _peekURL(0);
}

/* peek URL of image */
void
peekIMG(void)
{
    _peekURL(1);
}

/* show current URL */
static Str
currentURL(void)
{
    if (Currentbuf->bufferprop & BP_INTERNAL)
	return Strnew_size(0);
    return parsedURL2Str(&Currentbuf->currentURL);
}

void
curURL(void)
{
    static Str s = NULL;
    static int offset = 0;
    int n;

    if (Currentbuf->bufferprop & BP_INTERNAL)
        return;
    if (CurrentKey == prev_key && s != NULL) {
	if (s->length - offset >= Currentbuf->view->width - Currentbuf->rmargin - Currentbuf->lmargin)
	    offset++;
	else if (s->length <= offset)	/* bug ? */
	    offset = 0;
    } else {
	offset = 0;
	s = currentURL();
    }
    if ((n = searchKeyNum()) > 1 && s->length > (n - 1) * (Currentbuf->view->width - Currentbuf->rmargin - Currentbuf->lmargin - 1))
	disp_message_nomouse(&s->ptr[(n - 1) * (Currentbuf->view->width - Currentbuf->rmargin - Currentbuf->lmargin - 1)], TRUE);
    else
	disp_message_nomouse(&s->ptr[offset], TRUE);
}

/* view HTML source */

int
vwSrc_internal(Buffer *cur, Buffer **ret, Phase0Env *p0env_arg)
{
    char *fn;
    Buffer *buf;
    Phase0Env p0env;

    if (cur->type == NULL)
	return 0;
    if (!(p0env_arg->flag & RG_NOPROP) &&
	((buf = cur->linkBuffer[LB_SOURCE]) != NULL ||
	 (buf = cur->linkBuffer[LB_N_SOURCE]) != NULL)) {
	*ret = buf;
	return 1;
    }
    p0env = *p0env_arg;
    p0env.flag &= ~RG_PROC_MASK;
    if (cur->sourcefile == NULL) {
	if (cur->async_buf && cur->async_buf->rfd >= 0 &&
	    !strcasecmp(cur->type, "text/plain")) {
	    FILE *f;
	    Str tmpf = tmpfname(TMPF_SRC, NULL);
	    if (!(f = fopen(tmpf->ptr, "w")))
		return 0;
	    pushFileToDelete(tmpf->ptr, p0env_arg->flag);
	    saveBuffer(cur, f);
	    fclose(f);
	    fn = tmpf->ptr;
	}
	else {
	    return 0;
	}
    } else if (cur->real_scheme == SCM_LOCAL) {
	fn = cur->filename;
    } else {
	fn = cur->sourcefile;
    }
    if (!strcasecmp(cur->type, "text/html") ||
	(p0env.flag & RG_NOPROP && !strcasecmp(cur->real_type, "text/plain"))) {
#ifdef MANY_CHARSET
	p0env.content_charset = cur->document_encoding;
#endif
#ifdef JP_CHARSET
	p0env.DocumentCode = cur->document_encoding;
#endif
	buf = loadFile(fn, &p0env);
	if (buf == NULL)
	    return 0;
	buf->type = "text/plain";
	if (cur->real_type &&
	    !strcasecmp(cur->real_type, "text/html"))
	    buf->real_type = "text/plain";
	else
	    buf->real_type = cur->real_type;
	buf->buffername = Sprintf("source of %s", cur->buffername)->ptr;
#ifdef MANY_CHARSET
	buf->document_charset = cur->document_charset;
#endif
	if (!(p0env.flag & RG_NOPROP)) {
	    buf->linkBuffer[LB_N_SOURCE] = cur;
	    cur->linkBuffer[LB_SOURCE] = buf;
	}
    }
    else if (!(p0env.flag & RG_NOPROP) && !strcasecmp(cur->type, "text/plain")) {
	p0env.DefaultType = "text/html";
#ifdef MANY_CHARSET
	p0env.default_content_charset = cur->document_encoding;
#endif
	buf = loadGeneralFile(file_to_url(fn), NULL, NO_REFERER, &p0env, NULL);
	if (buf == NULL || buf == NO_BUFFER)
	    return 0;
	if (cur->real_type &&
	    !strcasecmp(cur->real_type, "text/plain"))
	    buf->real_type = "text/html";
	else
	    buf->real_type = cur->real_type;
	if (!strcmp(buf->buffername, conv_from_system(lastFileName(cur->sourcefile))))
	    buf->buffername = Sprintf("HTML view of %s", cur->buffername)->ptr;
	buf->linkBuffer[LB_SOURCE] = cur;
#ifdef MANY_CHARSET
	buf->document_charset = cur->document_charset;
#endif
	cur->linkBuffer[LB_N_SOURCE] = buf;
    }
    else {
	return 0;
    }
    buf->currentURL = cur->currentURL;
    buf->real_scheme = cur->real_scheme;
    buf->sourcefile = cur->sourcefile;
    *ret = buf;
    return 2;
}

void
vwSrc(void)
{
    Buffer *buf;

    switch (vwSrc_internal(Currentbuf, &buf, &main_p0env)) {
    case 0:
	return;
    default:
	pushBuffer(buf);
	break;
    }

    displayCurrentView(NULL);
}


/* reload */
struct reload_post_arg {
    FormList *request;
    Buffer *curbuf, sbuf;
    void (*func)(Buffer *, Phase0Env *, void *);
    void *arg;
};

static void
reload_post_internal(Buffer *buf, Phase0Env *p0env, void *chain_arg)
{
    struct reload_post_arg *arg;

    arg = chain_arg;

    if (buf->firstLine) {
	buf->form_submit = arg->curbuf->form_submit;
	replaceBuffer(arg->curbuf, buf);
	checkCurrentbuf();

	if ((arg->sbuf.type != NULL) && (buf->type != NULL) &&
	    ((!strcasecmp(buf->type, "text/plain") &&
	      !strcasecmp(arg->sbuf.type, "text/html")) ||
	     (!strcasecmp(buf->type, "text/html") &&
	      !strcasecmp(arg->sbuf.type, "text/plain")))) {
	    Buffer *src;

	    if (vwSrc_internal(buf, &src, p0env)) {
		if (src != buf)
		    delBuffer(buf);

		buf = src;
	    }
	}

	restorePosition(buf, &arg->sbuf);

	if (buf->async_buf)
	    buf->async_buf->label = NULL;
    }

    if (arg->func)
	arg->func(buf, p0env, arg->arg);
}

static void
reload_internal(Buffer *cur, void (*chain_func)(Buffer *, Phase0Env *, void *), void *chain_arg)
{
    Buffer *buf;
    char *t;
    Str url;
    Phase0Env p0env;
    struct reload_post_arg *arg;
    int multipart;

    if (Argumentbuf && Argumentbuf != cur)
	return;

    if (cur->real_type &&
	!strncasecmp(cur->real_type, "multipart/", sizeof("multipart/") - 1))
	t = cur->real_type;
    else
	switch (cur->currentURL.scheme) {
	default:
	    if (!(cur->bufferprop & BP_NO_URL)) {
		t = cur->type;
		break;
	    }
	case SCM_MISSING:
	case SCM_UNKNOWN:
	    disp_err_message("Can't reload...", FALSE);
	    return;
	}

    if (cur->currentURL.scheme == SCM_LOCAL &&
	!strcmp(cur->currentURL.file, "-")) {
	/* file is std input */
	disp_err_message("Can't reload stdin", FALSE);
	return;
    }

    arg = New(struct reload_post_arg);
    arg->func = chain_func;
    arg->arg = chain_arg;
    copyBuffer(&arg->sbuf, cur);
    arg->sbuf.type = t;
    multipart = 0;

    if (cur->form_submit) {
	arg->request = cur->form_submit->parent;

	if (arg->request->method == FORM_METHOD_POST &&
	    arg->request->enctype == FORM_ENCTYPE_MULTIPART) {
	    Str query;
	    struct stat st;

	    multipart = 1;
	    query_from_followform(&query, cur->form_submit, multipart);
	    stat(arg->request->body, &st);
	    arg->request->length = st.st_size;
	}
    }
    else
	arg->request = NULL;

    p0env = main_p0env;
    p0env.curbuf = arg->curbuf = cur;
    p0env.view = cur->view;
    p0env.flag |= RG_NOCACHE;
    p0env.RenderFrame = FALSE;
    p0env.SearchHeader = cur->search_header;
    p0env.DefaultType = cur->real_type;
    p0env.receiver = generic_receiver;
    p0env.receiver_arg = new_receiver_chain(reload_post_internal, arg);

#ifdef USE_IMAGE
    if (cur->real_type
	&& !strncasecmp(cur->real_type, "image/", sizeof("image/") - 1)
	&& cur->img) {
	int i;
	AnchorLink *alv;

	alv = cur->img->alv;
	i = cur->img->nlink;

	while (i > 0)
	    if (alv[--i].img->cache && alv[i].img->cache->loaded == IMG_FLAG_LOADED) {
		alv[i].img->cache->loaded = IMG_FLAG_ERROR;
		alv[i].img->cache->time = -1;
		alv[i].img->cache->retry = IMG_ERROR_RETRY;
		alv[i].img->cache->width = alv[i].img->cache->height = -1;
		alv[i].img->cache->index = 0;
	    }
    }
#endif

    message("Reloading...");
    refresh();
    url = parsedURL2Str(&cur->currentURL);
    buf = loadGeneralFile(url->ptr, NULL, NO_REFERER, &p0env, arg->request);

    if (buf == NULL || buf == NO_BUFFER) {
	if (multipart)
	    unlink(arg->request->body);

	if (!buf)
	    disp_err_message("Can't reload...", FALSE);

	return;
    }

    if (!buf->async_buf) {
	if (buf->firstLine) {
	    buf->bufferprop |= BP_VISIBLE;
	    pushBuffer(buf);
	}

	reload_post_internal(buf, &p0env, arg);
	displayBuffer(Currentbuf,
#ifdef USE_IMAGE
		      (activeImage && Currentbuf->image_flag == IMG_FLAG_AUTO && Currentbuf->img) ? B_REDRAW_IMAGE :
#endif
		      B_FORCE_REDRAW);
    }
}

void
reload(void)
{
    reload_internal(Currentbuf, NULL, NULL);
}

static void
reload_view_post(Buffer *buf, Phase0Env *p0env, void *arg)
{
    Buffer *cur;

    cur = arg;

    if (buf->view && buf->view == cur->view &&
	(!Currentbuf || isPrevBuffer(buf, Currentbuf) >= 0 ||
	 isPrevBuffer(cur, Currentbuf) >= 0))
	pushBuffer(buf);
}

static void
reload_view_loop(BufferView *v, Buffer *cur)
{
    if (v->frameset) {
	int i;

	for (i = v->ncols * v->nrows ; i > 0 ;)
	    reload_view_loop(v->subv[--i].top, cur);
    }
    else if (v->top)
	reload_internal(v->top, reload_view_post, cur);
}

void
reload_view(void)
{
    if (Currentbuf && Currentbuf->view && Currentbuf->view->root->frameset)
	reload_view_loop(Currentbuf->view->root, Currentbuf);
    else
	reload_internal(Currentbuf, NULL, NULL);
}

void
rerender(void)
{
    if (Currentbuf) {
#ifdef MANY_CHARSET
	char *cs;

	if ((cs = inputStrHistTab("Character encoding: ", (char *)Currentbuf->document_encoding, TextHist, mb_set_ces_tab(NULL))) && *cs)
	    Currentbuf->document_encoding = cs;
#endif

	Currentbuf->need_reshape = TRUE;
	Currentbuf->redraw_mode =
#ifdef USE_IMAGE
	    activeImage && Currentbuf->image_flag == IMG_FLAG_AUTO && Currentbuf->img ? B_REDRAW_IMAGE :
#endif
	    B_FORCE_REDRAW;
	reloadBuffer(Currentbuf, &main_p0env);

	if (Currentbuf)
	    displayBuffer(Currentbuf, B_FORCE_REDRAW);
    }
}

/* mark URL-like patterns as anchors */
void
chkURLBuffer(Buffer *buf)
{
    Str rexstr;
    static char *url_like_pat[] =
    {
	"https?://[a-zA-Z0-9][a-zA-Z0-9:%\\-\\./?=~_\\&+@#,\\$;]*[a-zA-Z0-9_/=]",
	"file:/[a-zA-Z0-9:%\\-\\./=_\\+@#,\\$;]*",
#ifdef USE_GOPHER
	"gopher://[a-zA-Z0-9][a-zA-Z0-9:%\\-\\./_]*",
#endif				/* USE_GOPHER */
	"ftp://[a-zA-Z0-9][a-zA-Z0-9:%\\-\\./=_+@#,\\$]*[a-zA-Z0-9_/]",
#ifdef USE_NNTP
	"news:[^<> 	][^<> 	]*",
	"nntp://[a-zA-Z0-9][a-zA-Z0-9:%\\-\\./_]*",
#endif				/* USE_NNTP */
#ifdef INET6
	"https?://[a-zA-Z0-9:%\\-\\./_@]*\\[[a-fA-F0-9:][a-fA-F0-9:\\.]*\\][a-zA-Z0-9:%\\-\\./?=~_\\&+@#,\\$;]*",
	"ftp://[a-zA-Z0-9:%\\-\\./_@]*\\[[a-fA-F0-9:][a-fA-F0-9:\\.]*\\][a-zA-Z0-9:%\\-\\./=_+@#,\\$]*",
#endif				/* INET6 */
	NULL,
    };
    int i;

    rexstr = Strnew_charp(url_like_pat[0]);
    for (i = 1; url_like_pat[i]; i++) {
	Strcat_char(rexstr, '|');
	Strcat_charp(rexstr, url_like_pat[i]);
    }
    chkExternalURIBuffer(rexstr);
    reAnchor(buf, rexstr->ptr);
    buf->check_url |= CHK_URL;
}

void
chkURL(void)
{
    chkURLBuffer(Currentbuf);
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

void
chkWORD(void)
{
    char *p;
    int spos, epos;
    p = getCurWord(Currentbuf, &spos, &epos, ":\"\'`<>");
    if (p == NULL)
	return;
    reAnchorWord(Currentbuf, Currentbuf->currentLine, spos, Currentbuf->currentLine, epos);
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

void
chkRegion(void)
{
    Line *bl, *el;
    int bpos, epos;

    if (GetMarkedRegion(Currentbuf, &bl, &bpos, &el, &epos)) {
	reAnchorWord(Currentbuf, bl, bpos, el, epos);
	displayBuffer(Currentbuf, B_FORCE_REDRAW);
    }
    else
	displayBuffer(Currentbuf, B_NORMAL);
}

#ifdef USE_NNTP
/* mark Message-ID-like patterns as NEWS anchors */
void
chkNMIDBuffer(Buffer *buf)
{
    static char *url_like_pat[] =
    {
	"<[^<> 	]+@[A-z0-9\\.\\-_]+>",
	NULL,
    };
    int i;
    for (i = 0; url_like_pat[i]; i++) {
	reAnchorNews(buf, url_like_pat[i]);
    }
    buf->check_url |= CHK_NMID;
}

void
chkNMID(void)
{
    chkNMIDBuffer(Currentbuf);
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}
#endif				/* USE_NNTP */

/* render frame */

Buffer *
rFrame_internal(Buffer *buf, Phase0Env *p0env_orig)
{
    Phase0Env p0env;

    if (fmInitialized) {
	message("Rendering frame");
	refresh();
    }

    p0env = *p0env_orig;
    buf->bufferprop |= BP_FRAMESET;
    return frameOrigin(renderFrame(buf->frameset, buf, &p0env));
}

void
rFrame(void)
{
    if (Currentbuf->frameset) {
	Buffer *fbuf;
	Phase0Env p0env;

	p0env = main_p0env;
	p0env.curbuf = Currentbuf;

	if ((fbuf = rFrame_internal(p0env.curbuf, &p0env))) {
	    p0env.curbuf->bufferprop &= ~BP_FRAMESET;

	    if (!fbuf->async_buf) {
		pushBuffer(fbuf);
		displayCurrentView(NULL);
	    }
	}
    }

    displayBuffer(Currentbuf, B_NORMAL);
}

/* spawn external browser */
static void
invoke_browser(char *url)
{
    Str cmd;
    char *browser = NULL;
    int bg = 0, len;

    CurrentKeyData = NULL;	/* not allowed in w3m-control: */
    TargetX = TargetY = PrevTargetX = PrevTargetY = -1;
    browser = searchKeyData();
    if (browser == NULL || *browser == '\0') {
	switch (prec_num) {
	case 0:
	case 1:
	    browser = ExtBrowser;
	    break;
	case 2:
	    browser = ExtBrowser2;
	    break;
	case 3:
	    browser = ExtBrowser3;
	    break;
	}
	if (browser == NULL || *browser == '\0')
	    browser = inputStr("Browse command: ", NULL);
    }
    if (browser == NULL || *browser == '\0')
	return;

    if ((len = strlen(browser)) >= 2 && browser[len - 1] == '&' && browser[len - 2] != '\\') {
	browser = allocStr(browser, len - 2);
	bg = MYSYSTEM_BACKGROUND | MYSYSTEM_NULLSTDIN | MYSYSTEM_NULLSTDOUT;
    }
    cmd = myExtCommand(browser, shell_quote(url), FALSE);
    Strremovetrailingspaces(cmd);
    fmTerm(0);
    mySystem(cmd->ptr, bg);
    fmInit(0);
    displayCurrentView(NULL);
}

void
extbrz()
{
    if (Currentbuf->bufferprop & BP_INTERNAL) {
	disp_err_message("Can't browse...", FALSE);
	return;
    }
    if (Currentbuf->currentURL.scheme == SCM_LOCAL &&
	!strcmp(Currentbuf->currentURL.file, "-")) {
	/* file is std input */
	disp_err_message("Can't browse stdin", FALSE);
	return;
    }
    invoke_browser(parsedURL2Str(&Currentbuf->currentURL)->ptr);
}

void
linkbrz()
{
    Anchor *a;
    ParsedURL pu;

    if (Currentbuf->firstLine == NULL)
	return;
    a = retrieveCurrentAnchor(Currentbuf);
    if (a == NULL)
	return;
    parseURL2(a->link->href->url, &pu, baseURL(Currentbuf));
    invoke_browser(parsedURL2Str(&pu)->ptr);
}

/* show current line number and number of lines in the entire document */
int
curlno_real(Line *l)
{
    for (; l ; l = l->prev)
	if (l->real_linenumber)
	    return l->real_linenumber;

    return 0;
}

Str
curlno_str(int needsspace)
{
    Str tmp;
    char begsp[] = {'\0', '\0'};
    char endsp[] = {'\0', '\0', '\0'};
    int cur = 0, all, col, len = 0;
#ifdef MANY_CHARSET
    const char *enc = Currentbuf->document_encoding;
    if (!enc) enc = "UNKNOWN";
#endif
#ifdef JP_CHARSET
    char enc = Currentbuf->document_encoding;
    if (!enc) enc = CODE_UNKNOWN;
#endif
    if (Currentbuf->currentLine != NULL) {
	Line *l = Currentbuf->currentLine;
	cur = curlno_real(l);
	if (l->width < 0)
	    l->width = COLPOS(l, l->len);
	len = l->width;
    }
    col = Currentbuf->currentColumn + Currentbuf->cursorX + 1;
    all = (Currentbuf->lastLine ? curlno_real(Currentbuf->lastLine) : Currentbuf->allLine);
    if (all == 0)
	all = 1;
    if (needsspace) {
	begsp[0] = '[';
	endsp[0] = ']';
	endsp[1] = ' ';
    }
    if (Currentbuf->async_buf && Currentbuf->async_buf->rfd >= 0)
	tmp = Sprintf("%sline %d, col %d/%d",
		      begsp, cur,
		      col, len);
    else
	tmp = Sprintf("%sline %d/%d (%d%%), col %d/%d",
		      begsp, cur, all, cur * 100 / all,
		      col, len);
#ifdef MANY_CHARSET
    Strcat_charp(tmp, ", ");
    Strcat_charp(tmp, (char *)enc);
#else
#ifdef JP_CHARSET
    Strcat_charp(tmp, ", ");
    Strcat_charp(tmp, code_to_str(enc));
#endif				/* JP_CHARSET */
#endif
    Strcat_charp(tmp, endsp);

    return tmp;
}

void
curlno(void)
{
    disp_message(curlno_str(0)->ptr, FALSE);
}

#ifdef USE_IMAGE
void
dispI(void)
{
    Phase0Env p0env;

    if (!activeImage || Currentbuf->async_buf ||
	Currentbuf->image_flag == IMG_FLAG_AUTO ||
	!(Currentbuf->type && !strcasecmp(Currentbuf->type, "text/html")))
	return;
    p0env = main_p0env;
    p0env.displayImage = TRUE;
    Currentbuf->image_flag = IMG_FLAG_AUTO;
    Currentbuf->need_reshape = TRUE;
    Currentbuf->redraw_mode = B_REDRAW_IMAGE;
    reloadBuffer(Currentbuf, &p0env);
    if (Currentbuf)
	displayBuffer(Currentbuf, B_REDRAW_IMAGE);
}

void
stopI(void)
{
    Phase0Env p0env;

    if (!activeImage || Currentbuf->async_buf ||
	Currentbuf->image_flag == IMG_FLAG_SKIP)
	return;
    p0env = main_p0env;
    p0env.displayImage = FALSE;
    Currentbuf->image_flag = IMG_FLAG_SKIP;
    Currentbuf->need_reshape = TRUE;
    Currentbuf->redraw_mode = B_REDRAW_IMAGE;
    reloadBuffer(Currentbuf, &p0env);
    if (Currentbuf)
	displayBuffer(Currentbuf, B_FORCE_REDRAW);
}
#endif

void
gotoXY(void)
{
    Buffer *buf;

    if ((TargetX < 0 || TargetY < 0) && !inputTargetXY(FALSE))
	return;

    if (!(buf = bufferAtXY(TargetX, TargetY))) {
	if (TargetY == LASTLINE) {
	    switch (TargetX) {
	    case 0:
	    case 1:
		backBf();
		break;
	    case 2:
	    case 3:
		pgBack();
		break;
	    case 4:
	    case 5:
		pgFore();
	    default:
		break;
	    }
	}

	return;
    }

    pushBuffer(buf);
    cursorXY(buf, TargetX - buf->view->rootX, TargetY - buf->view->rootY);
    displayBuffer(buf, B_NORMAL);
}

#ifdef USE_MOUSE

void
process_mouse(int btn, int x, int y, void (*doit)(int, void *), void *arg)
{
    static int press_btn = MOUSE_BTN_RESET, press_x, press_y;
    int delta_x = 0, delta_y = 0;
    int key = -1;
    int is_xterm = 0; /* XTerm or RXVT? Applies to BTN4 and 5 only -- LH */

    /* translate terminal-specific mouse codes to a unified set
     * of codes. The main motivation behind this is because
     * RXVT sends `3' for both BTN_UP and BTN4_DOWN; we need to
     * figure out what it means from context. -- LH */
    switch (btn) {
    case MOUSE_BTN1_DOWN:
    case MOUSE_BTN2_DOWN:
    case MOUSE_BTN3_DOWN:
	break; /* same for all terminals; no-op */
    case MOUSE_BTN4_DOWN_XTERM:
	btn = MOUSE_BTN4_DOWN; is_xterm = 1;
	break;
    case MOUSE_BTN5_DOWN_XTERM:
	is_xterm = 1;
    case MOUSE_BTN5_DOWN_RXVT:
	btn = MOUSE_BTN5_DOWN;
	break;
    case MOUSE_BTN1_MOVE_XTERM:
    case MOUSE_BTN2_MOVE_XTERM:
    case MOUSE_BTN3_MOVE_XTERM:
	btn += MOUSE_BTN1_MOVE - MOUSE_BTN1_MOVE_XTERM;
    case MOUSE_BTN4_DOWN_RXVT: /* == MOUSE_BTN_UP_TERM */
	/* allow for fall-through */
	if (btn == MOUSE_BTN4_DOWN_RXVT) {
	    if (press_btn == MOUSE_BTN_RESET) {
		btn = MOUSE_BTN4_DOWN;
		break;
	    }
	    else
		btn = MOUSE_BTN_UP;
	}

	/* BTN?_MOVE and BTN_UP falls though to this point. */
	delta_x = x - press_x;
	delta_y = y - press_y;

	if (reverse_mouse) {
	    delta_x = -delta_x;
	    delta_y = -delta_y;
	}

	break;
    default: /* ignore unrecognised mouse presses */
	btn = MOUSE_BTN_RESET;
	break;
    }

    switch(btn) {
    case MOUSE_BTN_UP:
	if (abs(delta_x) < abs(delta_y) / 3)
	    delta_x = 0;

	if (abs(delta_y) < abs(delta_x) / 3)
	    delta_y = 0;

	PrevTargetX = TargetX = x;
	PrevTargetY = TargetY = y;

	if (delta_x || delta_y) {
	    key = K_MOUSE(DRAG);
	    PrevTargetX = TargetX - delta_x;
	    PrevTargetY = TargetY - delta_y;
	}
	else if (y == Currentbuf->view->rootY + Currentbuf->cursorY &&
		 (x == Currentbuf->view->rootX + Currentbuf->lmargin + Currentbuf->cursorX
#if defined(MANY_CHARSET) || defined(JP_CHARSET)
		  || here_is_mousecursor_p(x, y, Currentbuf->view->rootX + Currentbuf->lmargin + Currentbuf->cursorX)
#endif
		  ))
	    key = K_MOUSE(DCLICK);
	else
	    key = K_MOUSE(CLICK);

	switch (press_btn) {
	case MOUSE_BTN1_DOWN:
	case MOUSE_BTN2_DOWN:
	case MOUSE_BTN3_DOWN:
	    key += press_btn;
	    break;
	default: /* ignore BTN_UP from BTN?_MOVE events */
	    key = -1;
	    break;
	}

	press_btn = MOUSE_BTN_RESET;
	break;
    case MOUSE_BTN1_MOVE:
    case MOUSE_BTN2_MOVE:
    case MOUSE_BTN3_MOVE:
	TargetX = press_x = x;
	TargetY = press_y = y;
	PrevTargetX = TargetX - delta_x;
	PrevTargetY = TargetY - delta_y;
	key = K_MOUSE(MOVE) + btn - MOUSE_BTN1_MOVE + MOUSE_BTN1_DOWN;
	press_btn = btn;
	break;
    default: /* mouse button down */
	press_x = x;
	press_y = y;

	switch(btn) {
	case MOUSE_BTN4_DOWN:
	case MOUSE_BTN5_DOWN:
	    /* XTerm never sends a BTN_UP event for the scroll wheel (BTN4 and
	     * BTN 5), so we have to process this on BTN?_DOWN instead.
	     * Consequently, we'll never be able to DCLICK with BTN4 and BTN5,
	     * even if RXVT is used. Oh well. -- Liyang HU <liyang@nerv.cx> */
	    press_btn = is_xterm ? MOUSE_BTN_RESET : btn;
	    key = K_MOUSE(CLICK) + btn;
	    break;
	default:
	    press_btn = btn;
	    break;
	}

	break;
    }

    if (key >= 0 && doit)
	doit(key, arg);

    TargetX = TargetY = PrevTargetX = PrevTargetY = -1;
}

void
msToggle(void)
{
    if (use_mouse) {
	use_mouse = FALSE;
	mouse_end();
    }
    else {
	use_mouse = TRUE;
    }
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

static void
process_main_mouse(int key, void *arg)
{
    FuncList *fl;

    CurrentKey = K_GEN(0, key);

    if ((
#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
	 (Currentbuf && Currentbuf->menu && (fl = lookupKey(CurrentKey, Currentbuf->menu->keymap))) ||
#endif
	 (fl = lookupKey(CurrentKey, w3mFuncKeyTabList))
	 ) &&
	((Currentbuf && Currentbuf != NO_BUFFER) ||
	 (fl - w3mFuncList) == FUNCNAME_quitfm || (fl - w3mFuncList) == FUNCNAME_qquitfm))
	fl->func.main_func();
}

void
mouse()
{
    int btn, x, y;

    btn = (unsigned char) getch() - 32;
    x = (unsigned char) getch() - 33;
    if (x < 0)
	x += 0x100;
    y = (unsigned char) getch() - 33;
    if (y < 0)
	y += 0x100;

    if (x < 0 || x >= COLS || y < 0 || y >= LINES)
	return;
    process_mouse(btn, x, y, process_main_mouse, NULL);
}
#endif				/* USE_MOUSE */

void
wrapToggle(void)
{
    if (WrapSearch) {
	WrapSearch = FALSE;
	disp_message("Wrap search off", FALSE);
    }
    else {
	WrapSearch = TRUE;
	disp_message("Wrap search on", FALSE);
    }
}

static int
is_wordchar(int c, const char *badchars)
{
    if (badchars)
	return !(IS_SPACE(c) || strchr(badchars, c));
    else
	return IS_ALPHA(c);
}

static char *
getCurWord(Buffer *buf, int *spos, int *epos, const char *badchars)
{
    char *p;
    Line *l = buf->currentLine;
    int b, e;

    *spos = 0;
    *epos = 0;
    if (l == NULL)
	return NULL;
    p = l->lineBuf;
    e = buf->pos;
    while (e > 0 && !is_wordchar(p[e], badchars))
	e--;
    if (!is_wordchar(p[e], badchars))
	return NULL;
    b = e;
    while (b > 0 && is_wordchar(p[b - 1], badchars))
	b--;
    while (e < l->len && is_wordchar(p[e], badchars))
	e++;
    *spos = b;
    *epos = e;
    return &p[b];
}

static char *
GetWord(Buffer *buf)
{
    int b, e;
    char *p;

    if ((p = getCurWord(buf, &b, &e, 0)) != NULL) {
	return Strnew_charp_n(p, e - b)->ptr;
    }
    return NULL;
}

#ifdef USE_MARK
char *
GetMarkedRegion(Buffer *buf, Line **p_bl, int *p_bpos, Line **p_el, int *p_epos)
{
    Line *l, *bl, *el;
    int bpos, epos;
#ifdef MANY_CHARSET
    size_t b, e;
    mb_wchar_t wc;
    int maynl_p = FALSE, nullnl_p = FALSE, cn;
#elif defined(JP_CHARSET)
    int maynl_p = FALSE, kanji_p = FALSE;
#endif
    int nspaces = 0;
    Str s;

    if (!(l = buf->currentLine))
	return NULL;

    if (use_mark) {
	for (el = l, epos = buf->pos + 1 ; el ;)
	    if (epos >= el->len) {
		el = el->next;
		epos = 0;
	    }
	    else if (el->propBuf[epos++] & PE_MARK)
		break;

	if (el) {
	    bl = l;
	    bpos = buf->pos;
	}
	else {
	    for (bl = l, bpos = buf->pos ; bl ;)
		if (bpos >= bl->len) {
		    bl = bl->prev;
		    bpos = -1;
		}
		else if (bpos < 0) {
		    if (bl->len)
			bpos += bl->len;
		    else
			bl = bl->prev;
		}
		else {
		    if (bl->propBuf[bpos] & PE_MARK)
			break;

		    if (bpos > 0)
			--bpos;
		    else {
			bl = bl->prev;
			bpos = -1;
		    }
		}

	    if (bl) {
		el = l;
		epos = buf->pos + 1;
	    }
	    else
		return NULL;
	}
    }
    else
	return NULL;

#ifdef MANY_CHARSET
    b = bpos;
    e = bl->len;
    mb_mem_to_wchar(bl->lineBuf, &b, &e);
    bpos = b;

    if (epos > 0) {
	b = epos - 1;
	e = el->len;
	mb_mem_to_wchar(el->lineBuf, &b, &e);
	epos = e;
    }
#endif
#ifdef JP_CHARSET
    if (bpos > 0 && CharType(bl->propBuf[bpos - 1]) == PC_KANJI1)
	--bpos;

    if (epos < el->len && CharType(el->propBuf[epos]) == PC_KANJI2)
	++epos;
#endif

    if (p_bl) *p_bl = bl;
    if (p_bpos) *p_bpos = bpos;
    if (p_el) *p_el = el;
    if (p_epos) *p_epos = epos;

    for (s = Strnew() ; bl != el || bpos < epos ;)
	if (bpos < bl->len) {
	    if (IS_SPACE((unsigned char)bl->lineBuf[bpos])) {
		++nspaces;
		++bpos;
		continue;
	    }
#ifdef MANY_CHARSET
	    if ((cn = mb_mem_to_wchar_internal(bl->lineBuf + bpos, bl->len - bpos, wc)) < 0)
		cn = 1;

	    if ((mb_wchar_prop(wc) & (MB_CPROP_MAY_BREAK | MB_CPROP_EOL_TO_NULL))
		== (MB_CPROP_MAY_BREAK | MB_CPROP_EOL_TO_NULL)) {
		if (s->length && (maynl_p ? !nullnl_p : nspaces))
		    Strcat_char(s, ' ');

		nullnl_p = TRUE;
	    }
	    else {
		if (s->length && nspaces)
		    Strcat_char(s, ' ');

		nullnl_p = FALSE;
	    }

	    maynl_p = FALSE;
	    nspaces = 0;
	    Strcat_charp_n(s, bl->lineBuf + bpos, cn);
	    bpos += cn;
#else
#ifdef JP_CHARSET
	    if (CharType(bl->propBuf[bpos]) == PC_KANJI1) {
		if (s->length && (maynl_p ? !kanji_p : nspaces))
		    Strcat_char(s, ' ');

		kanji_p = TRUE;
		Strcat_char(s, (unsigned char)bl->lineBuf[bpos++]);
	    }
	    else {
		if (maynl_p)
		    Strcat_char(s, ' ');

		kanji_p = FALSE;
	    }

	    maynl_p = FALSE;
#endif
	    nspaces = 0;
	    Strcat_char(s, (unsigned char)bl->lineBuf[bpos++]);
#endif
	}
	else {
	    if ((bl = bl->next) && bl->real_linenumber) {
#if defined(JP_CHARSET) || defined(MANY_CHARSET)
		maynl_p = TRUE;
#endif
		++nspaces;
	    }

	    bpos = 0;
	}

    return s->ptr;
}
#endif

#ifdef USE_DICT
static void
execdict_post(Buffer *buf, char *word)
{
    buf->filename = word;
    buf->buffername = Sprintf("%s %s", DICTBUFFERNAME, word)->ptr;

    if (!buf->type)
	buf->type = "text/plain";
}

static void
execdict_receiver(Buffer *buf, int nlines)
{
    displayBufferMaybe(buf, nlines);

    if (nlines < 0)
	execdict_post(buf, buf->async_buf->p2env->p1env->p0env->receiver_arg);
}

static void
execdict(char *word)
{
    char *w;
    Str dictcmd;
    Buffer *buf;
    Phase0Env p0env;

    if (!UseDictCommand || word == NULL || *word == '\0') {
	displayBuffer(Currentbuf, B_NORMAL);
	return;
    }
#ifdef MANY_CHARSET
    w = conv_str2isoStr(word, "@", lookup_process_charset(DictCommand))->ptr;
#else
    w = conv_to_system(word);
#endif
    if (!*w) {
	displayBuffer(Currentbuf, B_NORMAL);
	return;
    }
    p0env = main_p0env;
    p0env.receiver = execdict_receiver;
    p0env.receiver_arg = allocStr(word, -1);
    dictcmd = Strnew_charp(DictCommand);
    Strcat_char(dictcmd, '?');
    Strcat(dictcmd, Str_form_quote(Strnew_charp(w)));
    buf = loadGeneralFile(dictcmd->ptr, NULL, NO_REFERER, &p0env, NULL);
    if (buf == NULL) {
	disp_message("Execution failed", FALSE);
    }
    else if (!buf->async_buf) {
	execdict_post(buf, word);
	pushBuffer(buf);
    }
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

void
dictword(void)
{
    execdict(inputStr("(dictionary)!", ""));
}

void
dictwordat(void)
{
    execdict(GetWord(Currentbuf));
}

#ifdef USE_MARK
void
dictregion(void)
{
    execdict(GetMarkedRegion(Currentbuf, NULL, NULL, NULL, NULL));
}
#endif
#endif				/* USE_DICT */

void
set_buffer_environ(Buffer *buf)
{
    Anchor *a;
    Str s;
    ParsedURL pu;

    if (buf == NULL)
	return;
    set_environ("W3M_SOURCEFILE", buf->sourcefile);
    set_environ("W3M_FILENAME",buf->filename);
    set_environ("W3M_CURRENT_WORD",GetWord(buf));
    set_environ("W3M_TITLE", buf->buffername);
    set_environ("W3M_URL",parsedURL2Str(&buf->currentURL)->ptr);
    if (buf->real_type)
	set_environ("W3M_TYPE", buf->real_type);
    else
	set_environ("W3M_TYPE", "unknown");
#ifdef MANY_CHARSET
    set_environ("W3M_CHARSET", buf->document_encoding);
#endif				/* JP_CHARSET */
#ifdef JP_CHARSET
    set_environ("W3M_CHARSET", code_to_str(buf->document_encoding));
#endif				/* JP_CHARSET */
    a = retrieveCurrentAnchor(buf);
    if (a == NULL) {
	set_environ("W3M_CURRENT_LINK","");
    }
    else {
	parseURL2(a->link->href->url, &pu, baseURL(buf));
	s = parsedURL2Str(&pu);
	set_environ("W3M_CURRENT_LINK", s->ptr);
    }
    a = retrieveCurrentImg(buf);
    if (a == NULL) {
	set_environ("W3M_CURRENT_IMG","");
    }
    else {
	parseURL2(a->link->href->url, &pu, baseURL(buf));
	s = parsedURL2Str(&pu);
	set_environ("W3M_CURRENT_IMG",s->ptr);
    }
    a = retrieveCurrentForm(buf);
    if (a == NULL) {
	set_environ("W3M_CURRENT_FORM","");
    }
    else {
	s = Strnew_charp(form2str(a->link->fi));
	set_environ("W3M_CURRENT_FORM",s->ptr);
    }
}

char *
searchKeyData(void)
{
    char *data = NULL;

    if (CurrentKeyData != NULL && *CurrentKeyData != '\0')
	data = CurrentKeyData;
    else if (CurrentCmdData != NULL && *CurrentCmdData != '\0')
	data = CurrentCmdData;
    else if (CurrentKey)
	data = (ForcedKeyData && *ForcedKeyData) ? ForcedKeyData : getKeyData(CurrentKey);
    CurrentKeyData = NULL;
    CurrentCmdData = NULL;
    ForcedKeyData = NULL;
    if (data == NULL || *data == '\0')
	return NULL;
    return allocStr(data, -1);
}

int
searchKeyNum(void)
{
    char *d;
    int n = 1;

    d = searchKeyData();
    if (d != NULL)
	n = atoi(d);
    return n * PREC_NUM;
}

void
deleteFiles(void)
{
    char *f;

    discardViews(TopViewList.top);
    TopViewList.top = TopViewList.bot = NULL;
    Firstbuf = Currentbuf = NULL;

    while ((f = popText(fileToDelete)) != NULL)
	unlink(f);
}

void
w3m_exit(int i)
{
    deleteFiles();
#ifdef USE_SSL
    free_ssl_ctx();
#endif
    exit(i);
}

void
execCmd(void)
{
    char *data, *p;
    FuncList *fl;

    CurrentKeyData = NULL;	/* not allowed in w3m-control: */
    TargetX = TargetY = PrevTargetX = PrevTargetY = -1;
    data = searchKeyData();
    if (data == NULL || *data == '\0') {
	data = inputWordsHistTab("command [; ...]: ", "", TextHist, w3mFuncTab);
        if (data == NULL) {
            displayBuffer(Currentbuf, B_NORMAL);
            return;
        }
    }
    /* data: FUNC [DATA] [; FUNC [DATA] ...] */
    while (*data) {
	SKIP_BLANKS(data);
	if (*data == ';') {
	    data++;
	    continue;
	}
	p = getWord(&data);
	if (btri_fast_ci_search_str(p, w3mFuncTab, (void **)&fl) == bt_failure)
	    break;
	p = getQWord(&data);
	NStroke = CurrentKey = 0;
	CurrentKeyData = NULL;
	ForcedKeyData = NULL;
	TargetX = TargetY = PrevTargetX = PrevTargetY = -1;
	Argumentbuf = NULL;
	CurrentCmdData = *p ? p : NULL;
#ifdef USE_MOUSE
	if (use_mouse)
	    mouse_inactive();
#endif
	fl->func.main_func();
#ifdef USE_MOUSE
	if (use_mouse)
	    mouse_active();
#endif
	CurrentCmdData = NULL;
    }
    displayBuffer(Currentbuf, B_NORMAL);
}

void
setAlarm(void)
{
    char *data;
    int sec = 0, cmd = -1;

    CurrentKeyData = NULL;      /* not allowed in w3m-control: */
    data = searchKeyData();
    if (data == NULL || *data == '\0') {
	data = inputStrHist("(Alarm)sec command: ", "", TextHist);
	if (data == NULL) {
	    displayBuffer(Currentbuf, B_NORMAL);
	    return;
	}
    }
    if (*data != '\0') {
	sec = atoi(getWord(&data));
	if (sec > 0) {
	    FuncList *fl;
	    char *s;

	    s = getWord(&data);
	    cmd = btri_fast_ci_search_str(s, w3mFuncTab, (void **)&fl) != bt_failure ? fl - w3mFuncList : -1;
	}
    }
    if (cmd >= 0)
	record_crontab(cmd, getQWord(&data), 1, sec, Currentbuf);
    displayBuffer(Currentbuf, B_NORMAL);
}

void
callfunc(void)
{
    char *func;

    if ((func = inputWordsHistTab("Function name: ", NULL, FuncHist, w3mFuncTab)) && *func) {
	SKIP_BLANKS(func);

	if (*func) {
	    char *p, *q;
	    FuncList *fl;
	    int len;

	    for (p = func ; *p && !IS_SPACE(*p) ; ++p)
		;

	    for (len = strlen(p) ; len > 0 && IS_SPACE(*(p + len - 1)) ; --len)
		;

	    q = allocStr(p, len);
	    SKIP_BLANKS(q);
	    ForcedKeyData = q;
	    if (btri_fast_ci_search_mem(func, p - func, w3mFuncTab, (void **)&fl) != bt_failure)
		fl->func.main_func();
	}
    }
}

void
dispVer(void)
{
    disp_message(Sprintf("w3m version %s", w3m_version)->ptr, FALSE);
}

void
procList(void)
{
    Str src;
    int i;

    src = Strnew_charp("<html><head>"
#ifdef MANY_CHARSET
		       "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=x-moe-internal\">\n"
#endif
		       "<title>List of subprocesses</title></head><body>\n"
		       "<h1>List of subprocesses</h1><form method=internal action=process>\n"
		       "<table>\n"
		       "<tr><td>PIDs should be closed</td><td><input type=text name=closepids value=\"\"></td></tr>\n"
		       "<tr><td>PIDs should be stopped</td><td><input type=text name=stoppids value=\"\"></td></tr>\n"
		       "</table>\n"
		       "<p>(0 or &quot;*&quot; (asterisk) stands for all processes.)</p>\n"
		       "<table>\n<tr><td colspan=3><input type=submit value=\"close or stop\"></td><td>URL[PID]</td></tr>\n");

    for (i = 0 ; i < read_nfd ; ++i) {
	if (is_recorded_read_fd(i) && read_hookv[i] == readUrgentMessage && read_hook_argv[i]) {
	    AsyncRWBuf *abuf;

	    if ((abuf = ((Buffer *)read_hook_argv[i])->async_buf))
		Strcat(src,
		       Sprintf("<tr>"
			       "<td><input type=radio name=fd%d value=\"close\"></td>"
			       "<td><input type=radio name=fd%d value=\"stop\"></td>"
			       "<td><input type=radio name=fd%d value=\"noop\" checked></td>"
			       "<td>%s[%ld]</td></tr>\n",
			       i, i, i, abuf->path ? html_quote(abuf->path) : "", (long)abuf->pid));
	}
    }

    Strcat_charp(src, "</table></form></body></html>\n");
    cmd_loadBuffer(loadHTMLString(src, NULL, &main_p0env), BP_NO_URL, LB_NOLINK);
}

static int *
pid_list(char *b, int *p_n)
{
    int n, *pidv, *cur;
    char *p;

    for (n = 0, p = b ; *p ;)
	if (IS_DIGIT(*p)) {
	    ++n;
	    strtoul(p, &p, 0);
	}
	else if (*p == '*') {
	    ++n;
	    ++p;
	}
	else
	    do {
		++p;
	    } while (*p && !IS_DIGIT(*p));

    cur = pidv = NewAtom_N(int, n);

    for (p = b ; *p ;)
	if (IS_DIGIT(*p))
	    *cur++ = strtoul(p, &p, 0);
	else {
	    if (*p == '*')
		*cur++ = 0;

	    ++p;
	}

    *p_n = n;
    return pidv;
}

static int
search_pid_list(int pid, int *pidv, int n)
{
    while (n > 0) {
	--n;

	if (!pidv[n] || pid == pidv[n])
	    return TRUE;
    }

    return FALSE;
}

void
close_selected_process(struct parsed_tagarg *arg)
{
    struct parsed_tagarg *p;
    int *closepids = NULL, nclosepids = 0, *stoppids = NULL, nstoppids = 0;

    for (p = arg ; p ; p = p->next)
	if (!strcmp(p->arg, "closepids"))
	    closepids = pid_list(p->value, &nclosepids);
	else if (!strcmp(p->arg, "stoppids"))
	    stoppids = pid_list(p->value, &nstoppids);

    for (; arg ; arg = arg->next)
	if (!strncmp(arg->arg, "fd", 2)) {
	    char *e;
	    int i;

	    if ((i = strtol(&arg->arg[2], &e, 0)) >= 0 && e - arg->arg > 2 && !*e &&
		i < read_nfd && read_hookv[i] == readUrgentMessage && read_hook_argv[i]) {
		Buffer *buf;
		AsyncRWBuf *abuf;
		int close_p = FALSE, stop_p = FALSE;

		buf = read_hook_argv[i];
		abuf = buf->async_buf;

		if (!strcmp(arg->value, "close"))
		    close_p = TRUE;
		else if (abuf && abuf->pid > 0) {
		    if (!strcmp(arg->value, "stop"))
			stop_p = TRUE;
		    else if (!(stop_p = search_pid_list(abuf->pid, stoppids, nstoppids)))
			close_p = search_pid_list(abuf->pid, closepids, nclosepids);
		}

		if (close_p) {
		    if (buf->async_buf) {
			buf->bufferprop &= ~BP_ASYNC_MASK;
			buf->bufferprop |= BP_ASYNC_ABORT;
			finishAsyncBuffer(buf, -1);
			buf->async_buf = NULL;
		    }

		    if (buf->bufferprop & BP_LINKED) {
			if (!backBf_internal(buf))
			    delBuffer(buf);
		    }
		    else
			discardBuffer(buf);
		}
		else if (stop_p)
		    kill(buf->async_buf->pid, SIGINT);
	    }
	}

    backBf();
}

#ifndef ERROR_LOG_MAX
#define ERROR_LOG_MAX (1024 * 1024)
#endif

void
vwErrLog(void)
{
    if (errlog) {
	int fd;
	URLFile uf;
	struct stat stbuf;
	int off;

	if ((fd = dup(fileno(stderr))) < 0)
	    return;

	if (!fstat(fd, &stbuf)) {
	    char *numstr;

	    if ((numstr = searchKeyData()) && *numstr) {
		char *unit;

		off = strtol(numstr, &unit, 0);

		switch (*unit) {
		case 'K':
		case 'k':
		    off *= 1024;
		    break;
		case 'M':
		case 'm':
		    off *= 1024 * 1024;
		default:
		    break;
		}
	    }
	    else
		off = -ERROR_LOG_MAX;

	    if (off < 0) {
		if ((off += stbuf.st_size) < 0)
		    off = 0;
	    }
	    else if (off >= stbuf.st_size)
		return;
	}
	else
	    off = 0;

	lseek(fd, off, SEEK_SET);
	init_stream(&uf, SCM_MISSING, newInputStream(fd));
	uf.stream = newLimitedStream(uf.stream, ERROR_LOG_MAX);

	if (uf.stream) {
	    Phase0Env p0env;
	    Buffer *buf;

	    p0env = main_p0env;
	    p0env.flag &= ~(RG_DUMP_MASK | RG_HALFDUMP_MASK | RG_PROC_MASK);
	    buf = loadBuffer(&uf, NULL, &p0env);

	    if (buf->firstLine) {
		buf->buffername = "ERROR LOG";
		cmd_loadBuffer(buf, BP_NO_URL, LB_NOLINK);
	    }
	    else
		discardBuffer(buf);
	}
    }
}

void
multipart_load_part(struct parsed_tagarg *arg)
{
    char *fn = NULL, *action = NULL, *url = NULL;
    int withhead = 0;

    for (; arg ; arg = arg->next)
	if (!strcmp(arg->arg, "url"))
	    url = arg->value;
	else if (!strcmp(arg->arg, "part"))
	    fn = arg->value;
	else if (!strcmp(arg->arg, "action"))
	    action = arg->value;
	else if (!strcmp(arg->arg, "withhead") && !strcmp(arg->value, "yes"))
	    withhead = 1;

    if (fn && action) {
	Phase0Env p0env;

	p0env = main_p0env;
	p0env.SearchHeader = SEARCH_HEADER_CHECK;

	if (!withhead)
	    p0env.SearchHeader |= SEARCH_HEADER_NOVIEW;

	if (!strcmp(action, "view"))
	    cmd_loadfile(fn, url, &p0env);
	else if (!strcmp(action, "save")) {
	    p0env.flag |= RG_DO_DOWNLOAD;
	    cmd_loadfile(fn, url, &p0env);
	}
    }
}

void
resetAE(void)
{
    DecoderSearchPath = NULL;
    AllAcceptEncodings = NULL;
}

void
forever_try(void *arg)
{
    Buffer *buf;
    struct stat stbuf;
    URLFile f;
    Phase0Env p0env;
    char *url;
    pid_t pid;

    buf = arg;

    if ((buf->bufferprop & (BP_DISCARDED | BP_FOREVER)) != BP_FOREVER
	|| stat(buf->sourcefile, &stbuf))
	return;

    if (stbuf.st_size <= buf->trbyte) {
	record_raw_crontab(forever_try, buf, TRUE, FOREVER_INTERVAL);
	return;
    }

    init_stream(&f, SCM_LOCAL, NULL);
    p0env = main_p0env;
    p0env.flag &= ~(RG_DUMP_MASK | RG_HALFDUMP_MASK | RG_PROC_MASK | RG_DO_DOWNLOAD);
    examineFile(buf->sourcefile, &f, &p0env);

    if (f.stream == NULL)
	return;

    lseek(f.stream->handle.fd, buf->trbyte, SEEK_SET);
    p0env = main_p0env;
#ifdef USE_IMAGE
    p0env.cur_baseURL = New(ParsedURL);
    copyParsedURL(p0env.cur_baseURL, buf->baseURL ? buf->baseURL : &buf->currentURL);
#endif
    p0env.flag &= ~RG_PROC_MASK;
    p0env.flag |= RG_PROC_FORK;
    url = parsedURL2Str(&buf->currentURL)->ptr;

    if ((pid = forkWithChannel(url, &p0env, &buf)) == -1) {
	Str emsg;

	emsg = Sprintf("forever_try(\"%s\"): %s", url, strerror(errno));
	disp_err_message(emsg->ptr, FALSE);
    }
    else if (!pid) {
	loadBuffer(&f, buf, &p0env);
	flush_buffer_and_exit(buf);
    }

    UFclose(&f);
}

void
forever(void)
{
    if (!Currentbuf || Currentbuf->bufferprop & BP_FOREVER ||
	Currentbuf->currentURL.scheme != SCM_LOCAL)
	return;

    goLineL();

    if (Currentbuf->async_buf) {
	Currentbuf->bufferprop |= BP_FOREVER;
	return;
    }

    Currentbuf->bufferprop |= BP_FOREVER;
    record_raw_crontab(forever_try, Currentbuf, TRUE, FOREVER_INTERVAL);
}

void
nevermore(void)
{
    if (Currentbuf) {
	Currentbuf->bufferprop &= ~BP_FOREVER;
	displayBuffer(Currentbuf, B_NORMAL);
    }
}

#ifdef USE_GOPHER
void
gopher_redirect(struct parsed_tagarg *arg)
{
    char *url = NULL;
    int type = '0';
    Phase0Env p0env;

    for (; arg ; arg = arg->next)
	if (!strcmp(arg->arg, "url"))
	    url = arg->value;
	else if (!strcmp(arg->arg, "type"))
	    type = (unsigned char)*arg->value;

    if (!url)
	return;

    p0env = main_p0env;

    switch (type) {
    case '0':
	p0env.DefaultType = "text/plain";
	break;
    case '1':
	p0env.DefaultType = "gopher:directory";
	break;
    case 'm':
	p0env.DefaultType = "gopher:directory";
	break;
    case 's':
	p0env.DefaultType = "audio/basic";
	break;
    case 'g':
	p0env.DefaultType = "image/gif";
	break;
    case 'h':
	p0env.DefaultType = "text/html";
	break;
    default:
	break;
    }

    cmd_loadURL(Strnew_m_charp("gopher://", html_quote(url), NULL)->ptr,
		baseURL(Currentbuf),
		parsedURL2Str(&Currentbuf->currentURL)->ptr,
		NULL, &p0env);
}
#endif				/* USE_GOPHER */

static void
next_frame_loop(BufferView *v)
{
    if (!v->sup)
	return;

    if (v->x + 1 < v->sup->ncols || v->y + 1 < v->sup->nrows) {
	Buffer *buf;

	if ((buf = frameOrigin(v->sup->subv[v->y * v->sup->ncols + v->x + 1].top))) {
	    pushBuffer(buf);
	    return;
	}
    }

    if (v->sup->sup)
	next_frame_loop(v->sup);
    else
	pushBuffer(frameOrigin(v->sup));
}

void
next_frame(void)
{
    if (Currentbuf && Currentbuf->view &&
	Currentbuf->view->top == Currentbuf &&
	Currentbuf->view->root->frameset) {
	int n;

	for (n = searchKeyNum() ; n > 0 ; --n)
	    next_frame_loop(Currentbuf->view);
    }
}

static void
prev_frame_loop(BufferView *v)
{
    if (!v->sup)
	return;

    if (v->x || v->y) {
	Buffer *buf;

	if ((buf = frameCorner(v->sup->subv[v->y * v->sup->ncols + v->x - 1].top))) {
	    pushBuffer(buf);
	    return;
	}
    }

    if (v->sup->sup)
	prev_frame_loop(v->sup);
    else
	pushBuffer(frameCorner(v->sup));
}

void
prev_frame(void)
{
    if (Currentbuf && Currentbuf->view &&
	Currentbuf->view->root->frameset) {
	int n;

	for (n = searchKeyNum() ; n > 0 ; --n)
	    prev_frame_loop(Currentbuf->view);
    }
}

void
frame_view(void)
{
    if (Currentbuf && Currentbuf->view && Currentbuf->view->root->frameset) {
	Buffer *fbuf;

	if ((fbuf = frameViewBuffer(Currentbuf->view->root)))
	    pushBuffer(fbuf);
    }

    displayBuffer(Currentbuf, B_NORMAL);
}

void
reinit()
{
    char *resource;

    if ((resource = searchKeyData()) == NULL) {
	init_rc(config_filename);
#ifdef MANY_CHARSET
	rc_finish();
#endif
	sync_with_option();
#ifdef USE_COOKIE
	initCookie();
#endif
	return;
    }

    if (!strcasecmp(resource, "CONFIG") || !strcasecmp(resource, "RC")) {
	init_rc(config_filename);
#ifdef MANY_CHARSET
	rc_finish();
#endif
	sync_with_option();
	return;
    }

#ifdef USE_COOKIE
    if (!strcasecmp(resource, "COOKIE")) {
	initCookie();
	return;
    }
#endif

    if (!strcasecmp(resource, "KEYMAP")) {
	initKeymap(TRUE);
	return;
    }

    if (!strcasecmp(resource, "MAILCAP")) {
	initMailcap(&UserMailcap, &mailcap_list, mailcap_files, &mailcap_entries, TRUE);
	return;
    }

#ifdef USE_MENU
    if (!strcasecmp(resource, "MENU")) {
	initMenu();
	return;
    }
#endif

    if (!strcasecmp(resource, "MIMETYPES")) {
	initMimeTypes();
	return;
    }

    if (!strcasecmp(resource, "BROWSECAP")) {
	initMailcap(&UserBrowsecap, &browsecap_list, browsecap_files, &browsecap_entries, FALSE);
	return;
    }

#ifdef MANY_CHARSET
    if (!strcasecmp(resource, "LOCALE2MIME")) {
	setup_locale_charset();
	return;
    }
#endif

    disp_err_message(Sprintf("Don't know how to reinitialize '%s'", resource)->ptr, FALSE);
}

void
defKey(void)
{
    char *data;
    KeyTabList func_tab = {NULL, NULL, 0, 0}, edit_tab = {NULL, NULL, 0, 0};
#ifdef USE_MENU
    KeyTabList menu_tab = {NULL, NULL, 0, 0};
#endif

    CurrentKeyData = NULL; /* not allowed in w3m-control: */
    data = searchKeyData();
    if (data == NULL || *data == '\0') {
	data = inputStrHist("Key definition: ", "", TextHist);
	if (data == NULL || *data == '\0') {
	    displayBuffer(Currentbuf, B_NORMAL);
	    return;
	}
    }
    setKeymap(allocStr(data, -1), -1, TRUE, &func_tab, &edit_tab
#ifdef USE_MENU
	      , &menu_tab
#endif
	      );
    displayBuffer(Currentbuf, B_NORMAL);
}

void
chgDir(void)
{
    char *data;

    CurrentKeyData = NULL; /* not allowed in w3m-control: */
    data = searchKeyData();
    if (data == NULL || *data == '\0') {
	data = inputFilenameHist("Directory: ", NULL, DirectoryHist);
	if (data == NULL || *data == '\0') {
	    displayBuffer(Currentbuf, B_NORMAL);
	    return;
	}
    }
    chdir(data);
    displayBuffer(Currentbuf, B_NORMAL);
}

/* Local Variables:    */
/* c-basic-offset: 4   */
/* tab-width: 8        */
/* End:                */
