Logo Search packages:      
Sourcecode: w3mmee version File versions  Download package

main.c

/* 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:                */

Generated by  Doxygen 1.6.0   Back to index