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

main.c

/* $Id: main.c,v 1.242 2004/04/04 16:47:20 ukai Exp $ */
#define MAINPROGRAM
#include "fm.h"
#include <signal.h>
#include <setjmp.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#if defined(HAVE_WAITPID) || defined(HAVE_WAIT3)
#include <sys/wait.h>
#endif
#include <time.h>
#include "terms.h"
#include "myctype.h"
#include "regex.h"
#ifdef USE_MOUSE
#ifdef USE_GPM
#include <gpm.h>
#endif                        /* USE_GPM */
#if defined(USE_GPM) || defined(USE_SYSMOUSE)
extern int do_getch();
#define getch()   do_getch()
#endif                        /* defined(USE_GPM) || defined(USE_SYSMOUSE) */
#endif

#define DSTR_LEN  256

Hist *LoadHist;
Hist *SaveHist;
Hist *URLHist;
Hist *ShellHist;
Hist *TextHist;

typedef struct _Event {
    int cmd;
    void *data;
    struct _Event *next;
} Event;
static Event *CurrentEvent = NULL;
static Event *LastEvent = NULL;

#ifdef USE_ALARM
static AlarmEvent DefaultAlarm = {
    0, AL_UNSET, FUNCNAME_nulcmd, NULL
};
static AlarmEvent *CurrentAlarm = &DefaultAlarm;
static MySignalHandler SigAlarm(SIGNAL_ARG);
#endif

#ifdef SIGWINCH
static int need_resize_screen = FALSE;
static MySignalHandler resize_hook(SIGNAL_ARG);
static MySignalHandler resize_handler(SIGNAL_ARG);
static void resize_screen(void);
#endif

#ifdef SIGPIPE
static MySignalHandler SigPipe(SIGNAL_ARG);
#endif

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

JMP_BUF IntReturn;

static void delBuffer(Buffer *buf);
static void cmd_loadfile(char *path);
static void cmd_loadURL(char *url, ParsedURL *current, char *referer,
                  FormList *request);
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 int display_ok = FALSE;
static void do_dump(Buffer *);
int prec_num = 0;
int prev_key = -1;
int on_target = 1;
static int add_download_list = FALSE;

void set_buffer_environ(Buffer *);
static void save_buffer_position(Buffer *buf);

static void _followForm(int);
static void _goLine(char *);
static void _newT(void);
static void followTab(TabBuffer * tab);
static void moveTab(TabBuffer * t, TabBuffer * t2, int right);
static void _nextA(int);
static void _prevA(int);
static int check_target = TRUE;
#define PREC_NUM (prec_num ? prec_num : 1)
#define PREC_LIMIT 10000
static int searchKeyNum(void);

#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"
#else
          "lang=en"
#endif
#ifdef USE_M17N
          ",m17n"
#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_EXTERNAL_URI_LOADER
          ",external-uri-loader"
#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_MIGEMO
          ",migemo"
#endif
      );
}

static void
fusage(FILE * f, int err)
{
    fversion(f);
    /* FIXME: gettextize? */
    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 USE_M17N
    fprintf(f, "    -I charset       document charset\n");
    fprintf(f, "    -O charset       display/output charset\n");
#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
#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, "    -v               visual startup mode\n");
#ifdef USE_COLOR
    fprintf(f, "    -M               monochrome display\n");
#endif                        /* USE_COLOR */
    fprintf(f,
          "    -N               open URL of command line on each new tab\n");
    fprintf(f, "    -F               automatically render frame\n");
    fprintf(f,
          "    -cols width      specify column width (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, "    -dump            dump formatted page into stdout\n");
    fprintf(f,
          "    -dump_head       dump response of HEAD request into stdout\n");
    fprintf(f, "    -dump_source     dump page source into stdout\n");
    fprintf(f, "    -dump_both       dump HEAD and source into stdout\n");
    fprintf(f,
          "    -dump_extra      dump HEAD, source, and extra information into stdout\n");
    fprintf(f, "    -post file       use POST method with file content\n");
    fprintf(f, "    -header string   insert string as a header\n");
    fprintf(f, "    +<num>           goto <num> line\n");
    fprintf(f, "    -num             show line number\n");
    fprintf(f, "    -no-proxy        don't use proxy\n");
#ifdef INET6
    fprintf(f, "    -4               IPv4 only (-o dns_order=4)\n");
    fprintf(f, "    -6               IPv6 only (-o dns_order=6)\n");
#endif
#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");
    fprintf(f, "    -graph           use graphic character\n");
    fprintf(f, "    -no-graph        don't use graphic character\n");
#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, "    -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, "    -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);
    exit(err);
}

#ifdef USE_M17N
#ifdef __EMX__
static char *getCodePage(void);
#endif
#endif

static GC_warn_proc orig_GC_warn_proc = NULL;
#define GC_WARN_KEEP_MAX (20)

static void
wrap_GC_warn_proc(char *msg, GC_word arg)
{
    if (fmInitialized) {
      /* *INDENT-OFF* */
      static struct {
          char *msg;
          GC_word arg;
      } msg_ring[GC_WARN_KEEP_MAX];
      /* *INDENT-ON* */
      static int i = 0;
      static int n = 0;
      static int lock = 0;
      int j;

      j = (i + n) % (sizeof(msg_ring) / sizeof(msg_ring[0]));
      msg_ring[j].msg = msg;
      msg_ring[j].arg = arg;

      if (n < sizeof(msg_ring) / sizeof(msg_ring[0]))
          ++n;
      else
          ++i;

      if (!lock) {
          lock = 1;

          for (; n > 0; --n, ++i) {
            i %= sizeof(msg_ring) / sizeof(msg_ring[0]);
            disp_message_nsec(Sprintf
                          (msg_ring[i].msg,
                           (unsigned long)msg_ring[i].arg)->ptr, FALSE,
                          1, TRUE, FALSE);
          }

          lock = 0;
      }
    }
    else if (orig_GC_warn_proc)
      orig_GC_warn_proc(msg, arg);
    else
      fprintf(stderr, msg, (unsigned long)arg);
}

#ifdef SIGCHLD
static void
sig_chld(int signo)
{
    int p_stat;
#ifdef HAVE_WAITPID
    pid_t pid;

    while ((pid = waitpid(-1, &p_stat, WNOHANG)) > 0) {
      ;
    }
#elif HAVE_WAIT3
    int pid;

    while ((pid = wait3(&p_stat, WNOHANG, NULL)) > 0) {
      ;
    }
#else
    wait(&p_stat);
#endif
    mySignal(SIGCHLD, sig_chld);
    return;
}
#endif

Str
make_optional_header_string(char *s)
{
    char *p;
    Str hs;

    if (strchr(s, '\n') || strchr(s, '\r'))
      return NULL;
    for (p = s; *p && *p != ':'; p++) ;
    if (*p != ':' || p == s)
      return NULL;
    hs = Strnew_size(strlen(s) + 3);
    Strcopy_charp_n(hs, s, p - s);
    if (!Strcasecmp_charp(hs, "content-type"))
      override_content_type = TRUE;
    Strcat_charp(hs, ": ");
    if (*(++p)) {       /* not null header */
      SKIP_BLANKS(p);         /* skip white spaces */
      Strcat_charp(hs, p);
    }
    Strcat_charp(hs, "\r\n");
    return hs;
}

int
main(int argc, char **argv, char **envp)
{
    Buffer *newbuf = NULL;
    char *p, c;
    int i;
    InputStream redin;
    char *line_str = NULL;
    char **load_argv;
    FormList *request;
    int load_argc = 0;
    int load_bookmark = FALSE;
    int visual_start = FALSE;
    int open_new_tab = FALSE;
    char search_header = FALSE;
    char *default_type = NULL;
    char *post_file = NULL;
    Str err_msg;
#ifdef USE_M17N
    char *Locale = NULL;
    wc_uint8 auto_detect;
#ifdef __EMX__
    wc_ces CodePage;
#endif
#endif
    GC_init();
    setlocale(LC_ALL, "");
    bindtextdomain(PACKAGE, LOCALEDIR);
    textdomain(PACKAGE);

#ifndef HAVE_SYS_ERRLIST
    prepare_sys_errlist();
#endif                        /* not HAVE_SYS_ERRLIST */

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

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

    CurrentDir = currentdir();
    CurrentPid = (int)getpid();
    BookmarkFile = NULL;
    config_file = NULL;

    /* 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_file = 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);
          }
      }
    }

#ifdef USE_M17N
    if (non_null(Locale = getenv("LC_ALL")) ||
      non_null(Locale = getenv("LC_CTYPE")) ||
      non_null(Locale = getenv("LANG"))) {
      DisplayCharset = wc_guess_locale_charset(Locale, DisplayCharset);
      DocumentCharset = wc_guess_locale_charset(Locale, DocumentCharset);
      SystemCharset = wc_guess_locale_charset(Locale, SystemCharset);
    }
#ifdef __EMX__
    CodePage = wc_guess_charset(getCodePage(), 0);
    if (CodePage)
      DisplayCharset = DocumentCharset = SystemCharset = CodePage;
#endif
#endif

    /* initializations */
    init_rc();

    LoadHist = newHist();
    SaveHist = newHist();
    ShellHist = newHist();
    TextHist = newHist();
    URLHist = newHist();

#ifdef USE_M17N
    if (FollowLocale && Locale) {
      DisplayCharset = wc_guess_locale_charset(Locale, DisplayCharset);
      SystemCharset = wc_guess_locale_charset(Locale, SystemCharset);
    }
    auto_detect = WcOption.auto_detect;
#endif

    if (!non_null(HTTP_proxy) &&
      ((p = getenv("HTTP_PROXY")) ||
       (p = getenv("http_proxy")) || (p = getenv("HTTP_proxy"))))
      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 = getenv("gopher_proxy")) || (p = getenv("GOPHER_proxy"))))
      GOPHER_proxy = p;
#endif                        /* USE_GOPHER */
    if (!non_null(FTP_proxy) &&
      ((p = getenv("FTP_PROXY")) ||
       (p = getenv("ftp_proxy")) || (p = getenv("FTP_proxy"))))
      FTP_proxy = p;
    if (!non_null(NO_proxy) &&
      ((p = getenv("NO_PROXY")) ||
       (p = getenv("no_proxy")) || (p = getenv("NO_proxy"))))
      NO_proxy = p;
#ifdef USE_NNTP
    if (!non_null(NNTP_server) && (p = getenv("NNTPSERVER")) != NULL)
      NNTP_server = p;
    if (!non_null(NNTP_mode) && (p = getenv("NNTPMODE")) != NULL)
      NNTP_mode = p;
#endif

    if (!non_null(Editor) && (p = getenv("EDITOR")) != NULL)
      Editor = p;
    if (!non_null(Mailer) && (p = getenv("MAILER")) != NULL)
      Mailer = 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 (atoi(argv[i]) > 0)
                PagerMax = atoi(argv[i]);
          }
#ifdef USE_M17N
#ifndef DEBIAN                /* XXX: use -o kanjicode={S|J|E} */
          else if (!strcmp("-s", argv[i]))
            DisplayCharset = WC_CES_SHIFT_JIS;
          else if (!strcmp("-j", argv[i]))
            DisplayCharset = WC_CES_ISO_2022_JP;
          else if (!strcmp("-e", argv[i]))
            DisplayCharset = WC_CES_EUC_JP;
#endif
          else if (!strncmp("-I", argv[i], 2)) {
            if (argv[i][2] != '\0')
                p = argv[i] + 2;
            else {
                if (++i >= argc)
                  usage();
                p = argv[i];
            }
            DocumentCharset = wc_guess_charset_short(p, DocumentCharset);
            WcOption.auto_detect = WC_OPT_DETECT_OFF;
            UseContentCharset = FALSE;
          }
          else if (!strncmp("-O", argv[i], 2)) {
            if (argv[i][2] != '\0')
                p = argv[i] + 2;
            else {
                if (++i >= argc)
                  usage();
                p = argv[i];
            }
            DisplayCharset = wc_guess_charset_short(p, DisplayCharset);
          }
#endif
          else if (!strcmp("-graph", argv[i]))
            UseGraphicChar = TRUE;
          else if (!strcmp("-no-graph", argv[i]))
            UseGraphicChar = FALSE;
          else if (!strcmp("-T", argv[i])) {
            if (++i >= argc)
                usage();
            DefaultType = default_type = argv[i];
          }
          else if (!strcmp("-m", argv[i]))
            SearchHeader = search_header = TRUE;
          else if (!strcmp("-v", argv[i]))
            visual_start = TRUE;
          else if (!strcmp("-N", argv[i]))
            open_new_tab = 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]))
            RenderFrame = TRUE;
          else if (!strcmp("-W", argv[i])) {
            if (WrapDefault)
                WrapDefault = FALSE;
            else
                WrapDefault = TRUE;
          }
          else if (!strcmp("-dump", argv[i]))
            w3m_dump = DUMP_BUFFER;
          else if (!strcmp("-dump_source", argv[i]))
            w3m_dump = DUMP_SOURCE;
          else if (!strcmp("-dump_head", argv[i]))
            w3m_dump = DUMP_HEAD;
          else if (!strcmp("-dump_both", argv[i]))
            w3m_dump = (DUMP_HEAD | DUMP_SOURCE);
          else if (!strcmp("-dump_extra", argv[i]))
            w3m_dump = (DUMP_HEAD | DUMP_SOURCE | DUMP_EXTRA);
          else if (!strcmp("-halfdump", argv[i]))
            w3m_dump = DUMP_HALFDUMP;
          else if (!strcmp("-halfload", argv[i])) {
            w3m_dump = 0;
            w3m_halfload = TRUE;
            DefaultType = default_type = "text/html";
          }
          else if (!strcmp("-backend", argv[i])) {
            w3m_backend = TRUE;
          }
          else if (!strcmp("-backend_batch", argv[i])) {
            w3m_backend = TRUE;
            if (++i >= argc)
                usage();
            if (!backend_batch_commands)
                backend_batch_commands = newTextList();
            pushText(backend_batch_commands, argv[i]);
          }
          else if (!strcmp("-cols", argv[i])) {
            if (++i >= argc)
                usage();
            COLS = atoi(argv[i]);
          }
          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;
                set_pixel_per_char = TRUE;
            }
          }
#ifdef USE_IMAGE
          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;
                set_pixel_per_line = TRUE;
            }
          }
#endif
          else if (!strcmp("-num", argv[i]))
            showLineNum = TRUE;
          else if (!strcmp("-no-proxy", argv[i]))
            use_proxy = FALSE;
#ifdef INET6
          else if (!strcmp("-4", argv[i]) || !strcmp("-6", argv[i]))
            set_param_option(Sprintf("dns_order=%c", argv[i][1])->ptr);
#endif
          else if (!strcmp("-post", argv[i])) {
            if (++i >= argc)
                usage();
            post_file = argv[i];
          }
          else if (!strcmp("-header", argv[i])) {
            Str hs;
            if (++i >= argc)
                usage();
            if ((hs = make_optional_header_string(argv[i])) != NULL) {
                if (header_string == NULL)
                  header_string = hs;
                else
                  Strcat(header_string, hs);
            }
            while (argv[i][0]) {
                argv[i][0] = '\0';
                argv[i]++;
            }
          }
#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_m_charp("Basic ",
                                       encodeB(argv[i])->ptr,
                                       NULL);
            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("-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 */
                /* FIXME: gettextize? */
                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      __WATT32__
    if (w3m_debug)
      dbug_init();
    sock_init();
#endif

    FirstTab = NULL;
    LastTab = NULL;
    nTab = 0;
    CurrentTab = NULL;
    CurrentKey = -1;
    if (BookmarkFile == NULL)
      BookmarkFile = rcFile(BOOKMARK);

    if (!isatty(1) && !w3m_dump) {
      /* redirected output */
      w3m_dump = DUMP_BUFFER;
    }
    if (w3m_dump) {
      if (COLS == 0)
          COLS = 80;
    }

#ifdef USE_BINMODE_STREAM
    setmode(fileno(stdout), O_BINARY);
#endif
    if (!w3m_dump && !w3m_backend) {
      fmInit();
#ifdef SIGWINCH
      mySignal(SIGWINCH, resize_hook);
#else                   /* not SIGWINCH */
      setlinescols();
      setupscreen();
#endif                        /* not SIGWINCH */
    }
#ifdef USE_IMAGE
    else if (w3m_halfdump && displayImage)
      activeImage = TRUE;
#endif

    sync_with_option();
#ifdef USE_COOKIE
    initCookie();
#endif                        /* USE_COOKIE */
#ifdef USE_HISTORY
    if (UseHistory)
      loadHistory(URLHist);
#endif                        /* not USE_HISTORY */

#ifdef USE_M17N
    wtf_init(DocumentCharset, DisplayCharset);
    /*  if (w3m_dump)
     *    WcOption.pre_conv = WC_TRUE; 
     */
#endif

    if (w3m_backend)
      backend();

    if (w3m_dump)
      mySignal(SIGINT, SIG_IGN);
#ifdef SIGCHLD
    mySignal(SIGCHLD, sig_chld);
#endif
#ifdef SIGPIPE
    mySignal(SIGPIPE, SigPipe);
#endif

    orig_GC_warn_proc = GC_set_warn_proc(wrap_GC_warn_proc);
    err_msg = Strnew();
    if (load_argc == 0) {
      /* no URL specified */
      if (!isatty(0)) {
          redin = newFileStream(fdopen(dup(0), "rb"), (void (*)())pclose);
          newbuf = openGeneralPagerBuffer(redin);
          dup2(1, 0);
      }
      else if (load_bookmark) {
          newbuf = loadGeneralFile(BookmarkFile, NULL, NO_REFERER, 0, NULL);
          if (newbuf == NULL)
            Strcat_charp(err_msg, "w3m: Can't load bookmark.\n");
      }
      else if (visual_start) {
          /* FIXME: gettextize? */
          Str s_page;
          s_page =
            Strnew_charp
            ("<title>W3M startup page</title><center><b>Welcome to ");
          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_m_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>.",
                     NULL);
#endif                        /* DEBIAN */
          newbuf = loadHTMLString(s_page);
          if (newbuf == NULL)
            Strcat_charp(err_msg, "w3m: Can't load string.\n");
          else if (newbuf != NO_BUFFER)
            newbuf->bufferprop |= (BP_INTERNAL | BP_NO_URL);
      }
      else if ((p = getenv("HTTP_HOME")) != NULL ||
             (p = getenv("WWW_HOME")) != NULL) {
          newbuf = loadGeneralFile(p, NULL, NO_REFERER, 0, NULL);
          if (newbuf == NULL)
            Strcat(err_msg, Sprintf("w3m: Can't load %s.\n", p));
          else if (newbuf != NO_BUFFER)
            pushHashHist(URLHist, parsedURL2Str(&newbuf->currentURL)->ptr);
      }
      else {
          if (fmInitialized)
            fmTerm();
          usage();
      }
      if (newbuf == NULL) {
          if (fmInitialized)
            fmTerm();
          if (err_msg->length)
            fprintf(stderr, "%s", err_msg->ptr);
          w3m_exit(2);
      }
      i = -1;
    }
    else {
      i = 0;
    }
    for (; i < load_argc; i++) {
      if (i >= 0) {
          SearchHeader = search_header;
          DefaultType = default_type;
          if (w3m_dump == DUMP_HEAD) {
            request = New(FormList);
            request->method = FORM_METHOD_HEAD;
            newbuf =
                loadGeneralFile(load_argv[i], NULL, NO_REFERER, 0,
                            request);
          }
          else {
            if (post_file && i == 0) {
                FILE *fp;
                Str body;
                if (!strcmp(post_file, "-"))
                  fp = stdin;
                else
                  fp = fopen(post_file, "r");
                if (fp == NULL) {
                  /* FIXME: gettextize? */
                  Strcat(err_msg,
                         Sprintf("w3m: Can't open %s.\n", post_file));
                  continue;
                }
                body = Strfgetall(fp);
                if (fp != stdin)
                  fclose(fp);
                request =
                  newFormList(NULL, "post", NULL, NULL, NULL, NULL,
                            NULL);
                request->body = body->ptr;
                request->boundary = NULL;
                request->length = body->length;
            }
            else {
                request = NULL;
            }
            newbuf =
                loadGeneralFile(load_argv[i], NULL, NO_REFERER, 0,
                            request);
          }
          if (newbuf == NULL) {
            /* FIXME: gettextize? */
            Strcat(err_msg,
                   Sprintf("w3m: Can't load %s.\n", load_argv[i]));
            continue;
          }
          else if (newbuf == NO_BUFFER)
            continue;
          switch (newbuf->real_scheme) {
          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;
          }
      }
      else if (newbuf == NO_BUFFER)
          continue;
      if (newbuf->pagerSource ||
          (newbuf->real_scheme == SCM_LOCAL && newbuf->header_source &&
           newbuf->currentURL.file && strcmp(newbuf->currentURL.file, "-")))
          newbuf->search_header = search_header;
      if (CurrentTab == NULL) {
          FirstTab = LastTab = CurrentTab = newTab();
          nTab = 1;
          Firstbuf = Currentbuf = newbuf;
      }
      else if (open_new_tab) {
          _newT();
          Currentbuf->nextBuffer = newbuf;
          delBuffer(Currentbuf);
      }
      else {
          Currentbuf->nextBuffer = newbuf;
          Currentbuf = newbuf;
      }
      if (!w3m_dump || w3m_dump == DUMP_BUFFER) {
          if (Currentbuf->frameset != NULL && RenderFrame)
            rFrame();
      }
      if (w3m_dump)
          do_dump(Currentbuf);
      else {
          Currentbuf = newbuf;
#ifdef USE_BUFINFO
          saveBufferInfo();
#endif
      }
    }
    if (w3m_dump) {
      if (err_msg->length)
          fprintf(stderr, "%s", err_msg->ptr);
#ifdef USE_COOKIE
      save_cookies();
#endif                        /* USE_COOKIE */
      w3m_exit(0);
    }

    if (add_download_list) {
      add_download_list = FALSE;
      CurrentTab = LastTab;
      if (!FirstTab) {
          FirstTab = LastTab = CurrentTab = newTab();
          nTab = 1;
      }
      if (!Firstbuf || Firstbuf == NO_BUFFER) {
          Firstbuf = Currentbuf = newBuffer(INIT_BUFFER_WIDTH);
          Currentbuf->bufferprop = BP_INTERNAL | BP_NO_URL;
          Currentbuf->buffername = DOWNLOAD_LIST_TITLE;
      }
      else
          Currentbuf = Firstbuf;
      ldDL();
    }
    else
      CurrentTab = FirstTab;
    if (!FirstTab || !Firstbuf || Firstbuf == NO_BUFFER) {
      if (newbuf == NO_BUFFER) {
          if (fmInitialized)
            /* FIXME: gettextize? */
            inputChar("Hit any key to quit w3m:");
      }
      if (fmInitialized)
          fmTerm();
      if (err_msg->length)
          fprintf(stderr, "%s", err_msg->ptr);
      if (newbuf == NO_BUFFER) {
#ifdef USE_COOKIE
          save_cookies();
#endif                        /* USE_COOKIE */
          if (!err_msg->length)
            w3m_exit(0);
      }
      w3m_exit(2);
    }
    if (err_msg->length)
      disp_message_nsec(err_msg->ptr, FALSE, 1, TRUE, FALSE);

    SearchHeader = FALSE;
    DefaultType = NULL;
#ifdef USE_M17N
    UseContentCharset = TRUE;
    WcOption.auto_detect = auto_detect;
#endif

    Currentbuf = Firstbuf;
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
    if (line_str) {
      _goLine(line_str);
    }
    for (;;) {
      if (add_download_list) {
          add_download_list = FALSE;
          ldDL();
      }
      if (Currentbuf->submit) {
          Anchor *a = Currentbuf->submit;
          Currentbuf->submit = NULL;
          gotoLine(Currentbuf, a->start.line);
          Currentbuf->pos = a->start.pos;
          _followForm(TRUE);
          continue;
      }
      /* event processing */
      if (CurrentEvent) {
          CurrentKey = -1;
          CurrentKeyData = NULL;
          CurrentCmdData = (char *)CurrentEvent->data;
          w3mFuncList[CurrentEvent->cmd].func();
          CurrentCmdData = NULL;
          CurrentEvent = CurrentEvent->next;
          continue;
      }
      /* get keypress event */
#ifdef USE_ALARM
      if (Currentbuf->event) {
          if (Currentbuf->event->status != AL_UNSET) {
            CurrentAlarm = Currentbuf->event;
            if (CurrentAlarm->sec == 0) { /* refresh (0sec) */
                Currentbuf->event = NULL;
                CurrentKey = -1;
                CurrentKeyData = NULL;
                CurrentCmdData = (char *)CurrentAlarm->data;
                w3mFuncList[CurrentAlarm->cmd].func();
                CurrentCmdData = NULL;
                continue;
            }
          }
          else
            Currentbuf->event = NULL;
      }
      if (!Currentbuf->event)
          CurrentAlarm = &DefaultAlarm;
#endif
#ifdef USE_MOUSE
      mouse_action.in_action = FALSE;
      if (use_mouse)
          mouse_active();
#endif                        /* USE_MOUSE */
#ifdef USE_ALARM
      if (CurrentAlarm->sec > 0) {
          mySignal(SIGALRM, SigAlarm);
          alarm(CurrentAlarm->sec);
      }
#endif
#ifdef SIGWINCH
      if (need_resize_screen) {
          need_resize_screen = FALSE;
          resize_screen();
      }
      mySignal(SIGWINCH, resize_handler);
#endif
#ifdef USE_IMAGE
      if (activeImage && displayImage && Currentbuf->img &&
          !Currentbuf->image_loaded) {
          do {
            loadImage(Currentbuf, IMG_FLAG_NEXT);
          } while (sleep_till_anykey(1, 0) <= 0);
      }
#endif
      c = getch();
#ifdef SIGWINCH
      mySignal(SIGWINCH, resize_hook);
#endif
#ifdef USE_ALARM
      if (CurrentAlarm->sec > 0) {
          alarm(0);
      }
#endif
#ifdef USE_MOUSE
      if (use_mouse)
          mouse_inactive();
#endif                        /* USE_MOUSE */
      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 {
            set_buffer_environ(Currentbuf);
            save_buffer_position(Currentbuf);
            keyPressEventProc((int)c);
            prec_num = 0;
          }
      }
      prev_key = CurrentKey;
      CurrentKey = -1;
      CurrentKeyData = NULL;
    }
}

static void
keyPressEventProc(int c)
{
    CurrentKey = c;
    w3mFuncList[(int)GlobalKeymap[c]].func();
}

void
pushEvent(int cmd, void *data)
{
    Event *event;

    event = New(Event);
    event->cmd = cmd;
    event->data = data;
    event->next = NULL;
    if (CurrentEvent)
      LastEvent->next = event;
    else
      CurrentEvent = event;
    LastEvent = event;
}

static void
dump_source(Buffer *buf)
{
    FILE *f;
    char c;
    if (buf->sourcefile == NULL)
      return;
    f = fopen(buf->sourcefile, "r");
    if (f == NULL)
      return;
    while (c = fgetc(f), !feof(f)) {
      putchar(c);
    }
    fclose(f);
}

static void
dump_head(Buffer *buf)
{
    TextListItem *ti;

    if (buf->document_header == NULL) {
      if (w3m_dump & DUMP_EXTRA)
          printf("\n");
      return;
    }
    for (ti = buf->document_header->first; ti; ti = ti->next) {
#ifdef USE_M17N
      printf("%s",
             wc_conv_strict(ti->ptr, InnerCharset,
                        buf->document_charset)->ptr);
#else
      printf("%s", ti->ptr);
#endif
    }
    puts("");
}

static void
dump_extra(Buffer *buf)
{
    printf("W3m-current-url: %s\n", parsedURL2Str(&buf->currentURL)->ptr);
    if (buf->baseURL)
      printf("W3m-base-url: %s\n", parsedURL2Str(buf->baseURL)->ptr);
#ifdef USE_M17N
    printf("W3m-document-charset: %s\n",
         wc_ces_to_charset(buf->document_charset));
#endif
#ifdef USE_SSL
    if (buf->ssl_certificate) {
      Str tmp = Strnew();
      char *p;
      for (p = buf->ssl_certificate; *p; p++) {
          Strcat_char(tmp, *p);
          if (*p == '\n') {
            for (; *(p + 1) == '\n'; p++) ;
            if (*(p + 1))
                Strcat_char(tmp, '\t');
          }
      }
      if (Strlastchar(tmp) != '\n')
          Strcat_char(tmp, '\n');
      printf("W3m-ssl-certificate: %s", tmp->ptr);
    }
#endif
}

static void
do_dump(Buffer *buf)
{
    MySignalHandler(*volatile prevtrap) (SIGNAL_ARG) = NULL;

    prevtrap = mySignal(SIGINT, intTrap);
    if (SETJMP(IntReturn) != 0) {
      mySignal(SIGINT, prevtrap);
      return;
    }
    if (w3m_dump & DUMP_EXTRA)
      dump_extra(buf);
    if (w3m_dump & DUMP_HEAD)
      dump_head(buf);
    if (w3m_dump & DUMP_SOURCE)
      dump_source(buf);
    if (w3m_dump == DUMP_BUFFER)
      saveBuffer(buf, stdout, FALSE);
    mySignal(SIGINT, prevtrap);
}

DEFUN(nulcmd, NOTHING NULL @@@, "Do nothing")
{                       /* do nothing */
}

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

static void
escKeyProc(int c, int esc, unsigned char *map)
{
    if (CurrentKey >= 0 && CurrentKey & K_MULTI) {
      unsigned char **mmap;
      mmap = (unsigned char **)getKeyData(MULTI_KEY(CurrentKey));
      if (!mmap)
          return;
      switch (esc) {
      case K_ESCD:
          map = mmap[3];
          break;
      case K_ESCB:
          map = mmap[2];
          break;
      case K_ESC:
          map = mmap[1];
          break;
      default:
          map = mmap[0];
          break;
      }
      esc |= (CurrentKey & ~0xFFFF);
    }
    CurrentKey = esc | c;
    w3mFuncList[(int)map[c]].func();
}

DEFUN(escmap, ESCMAP, "ESC map")
{
    char c;
    c = getch();
    if (IS_ASCII(c))
      escKeyProc((int)c, K_ESC, EscKeymap);
}

DEFUN(escbmap, ESCBMAP, "ESC [ map")
{
    char c;
    c = getch();
    if (IS_DIGIT(c)) {
      escdmap(c);
      return;
    }
    if (IS_ASCII(c))
      escKeyProc((int)c, K_ESCB, EscBKeymap);
}

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 == '~')
      escKeyProc((int)d, K_ESCD, EscDKeymap);
}

DEFUN(multimap, MULTIMAP, "multimap")
{
    char c;
    c = getch();
    if (IS_ASCII(c)) {
      CurrentKey = K_MULTI | (CurrentKey << 16) | c;
      escKeyProc((int)c, 0, NULL);
    }
}

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

static Str currentURL(void);

#ifdef USE_BUFINFO
void
saveBufferInfo()
{
    FILE *fp;

    if (w3m_dump)
      return;
    if ((fp = fopen(rcFile("bufinfo"), "w")) == NULL) {
      return;
    }
    fprintf(fp, "%s\n", currentURL()->ptr);
    fclose(fp);
}
#endif

static void
pushBuffer(Buffer *buf)
{
    Buffer *b;

#ifdef USE_IMAGE
    deleteImage(Currentbuf);
#endif
    if (clear_buffer)
      tmpClearBuffer(Currentbuf);
    if (Firstbuf == Currentbuf) {
      buf->nextBuffer = Firstbuf;
      Firstbuf = Currentbuf = buf;
    }
    else if ((b = prevBuffer(Firstbuf, Currentbuf)) != NULL) {
      b->nextBuffer = buf;
      buf->nextBuffer = Currentbuf;
      Currentbuf = buf;
    }
#ifdef USE_BUFINFO
    saveBufferInfo();
#endif

}

static void
delBuffer(Buffer *buf)
{
    if (buf == NULL)
      return;
    if (Currentbuf == buf)
      Currentbuf = buf->nextBuffer;
    Firstbuf = deleteBuffer(Firstbuf, buf);
    if (!Currentbuf)
      Currentbuf = Firstbuf;
}

static void
repBuffer(Buffer *oldbuf, Buffer *buf)
{
    Firstbuf = replaceBuffer(Firstbuf, oldbuf, buf);
    Currentbuf = buf;
}


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

#ifdef SIGWINCH
static MySignalHandler
resize_hook(SIGNAL_ARG)
{
    need_resize_screen = TRUE;
    mySignal(SIGWINCH, resize_hook);
    SIGNAL_RETURN;
}

static MySignalHandler
resize_handler(SIGNAL_ARG)
{
    resize_screen();
    mySignal(SIGWINCH, resize_handler);
    SIGNAL_RETURN;
}

static void
resize_screen(void)
{
    setlinescols();
    setupscreen();
    if (CurrentTab)
      displayBuffer(Currentbuf, B_FORCE_REDRAW);
}
#endif                        /* SIGWINCH */

#ifdef SIGPIPE
static MySignalHandler
SigPipe(SIGNAL_ARG)
{
#ifdef USE_MIGEMO
    init_migemo();
#endif
    mySignal(SIGPIPE, SigPipe);
    SIGNAL_RETURN;
}
#endif

/* 
 * 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)
{
    Buffer *buf = Currentbuf;
    Line *top = buf->topLine, *cur = buf->currentLine;
    int lnum, tlnum, llnum, diff_n;

    if (buf->firstLine == NULL)
      return;
    lnum = cur->linenumber;
    buf->topLine = lineSkip(buf, top, n, FALSE);
    if (buf->topLine == top) {
      lnum += n;
      if (lnum < buf->topLine->linenumber)
          lnum = buf->topLine->linenumber;
      else if (lnum > buf->lastLine->linenumber)
          lnum = buf->lastLine->linenumber;
    }
    else {
      tlnum = buf->topLine->linenumber;
      llnum = buf->topLine->linenumber + buf->LINES - 1;
      if (nextpage_topline)
          diff_n = 0;
      else
          diff_n = n - (tlnum - top->linenumber);
      if (lnum < tlnum)
          lnum = tlnum + diff_n;
      if (lnum > llnum)
          lnum = llnum + diff_n;
    }
    gotoLine(buf, lnum);
    arrangeLine(buf);
    if (n > 0) {
      if (buf->currentLine->bpos &&
          buf->currentLine->bwidth >= buf->currentColumn + buf->visualpos)
          cursorDown(buf, 1);
      else {
          while (buf->currentLine->next && buf->currentLine->next->bpos &&
               buf->currentLine->bwidth + buf->currentLine->width <
               buf->currentColumn + buf->visualpos)
            cursorDown0(buf, 1);
      }
    }
    else {
      if (buf->currentLine->bwidth + buf->currentLine->width <
          buf->currentColumn + buf->visualpos)
          cursorUp(buf, 1);
      else {
          while (buf->currentLine->prev && buf->currentLine->bpos &&
               buf->currentLine->bwidth >=
               buf->currentColumn + buf->visualpos)
            cursorUp0(buf, 1);
      }
    }
    displayBuffer(buf, mode);
}

/* Move page forward */
DEFUN(pgFore, NEXT_PAGE, "Move to next page")
{
    if (vi_prec_num)
      nscroll(searchKeyNum() * (Currentbuf->LINES - 1), B_NORMAL);
    else
      nscroll(prec_num ? searchKeyNum() : searchKeyNum()
            * (Currentbuf->LINES - 1), prec_num ? B_SCROLL : B_NORMAL);
}

/* Move page backward */
DEFUN(pgBack, PREV_PAGE, "Move to previous page")
{
    if (vi_prec_num)
      nscroll(-searchKeyNum() * (Currentbuf->LINES - 1), B_NORMAL);
    else
      nscroll(-(prec_num ? searchKeyNum() : searchKeyNum()
              * (Currentbuf->LINES - 1)), prec_num ? B_SCROLL : B_NORMAL);
}

/* 1 line up */
DEFUN(lup1, UP, "Scroll up one line")
{
    nscroll(searchKeyNum(), B_SCROLL);
}

/* 1 line down */
DEFUN(ldown1, DOWN, "Scroll down one line")
{
    nscroll(-searchKeyNum(), B_SCROLL);
}

/* move cursor position to the center of screen */
DEFUN(ctrCsrV, CENTER_V, "Move to the center column")
{
    int offsety;
    if (Currentbuf->firstLine == NULL)
      return;
    offsety = Currentbuf->LINES / 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);
    }
}

DEFUN(ctrCsrH, CENTER_H, "Move to the center line")
{
    int offsetx;
    if (Currentbuf->firstLine == NULL)
      return;
    offsetx = Currentbuf->cursorX - Currentbuf->COLS / 2;
    if (offsetx != 0) {
      columnSkip(Currentbuf, offsetx);
      arrangeCursor(Currentbuf);
      displayBuffer(Currentbuf, B_NORMAL);
    }
}

/* Redraw screen */
DEFUN(rdrwSc, REDRAW, "Redraw screen")
{
    clear();
    arrangeCursor(Currentbuf);
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

static void
clear_mark(Line *l)
{
    int pos;
    if (!l)
      return;
    for (pos = 0; pos < l->size; pos++)
      l->propBuf[pos] &= ~PE_MARK;
}

/* search by regular expression */
static int
srchcore(char *volatile str, int (*func) (Buffer *, char *))
{
    MySignalHandler(*prevtrap) ();
    volatile int i, result = SR_NOTFOUND;

    if (str != NULL && str != SearchString)
      SearchString = str;
    if (SearchString == NULL || *SearchString == '\0')
      return SR_NOTFOUND;

    str = conv_search_string(SearchString, DisplayCharset);
    prevtrap = mySignal(SIGINT, intTrap);
    crmode();
    if (SETJMP(IntReturn) == 0) {
      for (i = 0; i < PREC_NUM; i++) {
          result = func(Currentbuf, str);
          if (i < PREC_NUM - 1 && result & SR_FOUND)
            clear_mark(Currentbuf->currentLine);
      }
    }
    mySignal(SIGINT, prevtrap);
    term_raw();
    return result;
}

static void
disp_srchresult(int result, char *prompt, char *str)
{
    if (str == NULL)
      str = "";
    if (result & SR_NOTFOUND)
      disp_message(Sprintf("Not found: %s", str)->ptr, TRUE);
    else if (result & SR_WRAPPED)
      disp_message(Sprintf("Search wrapped: %s", str)->ptr, TRUE);
    else if (show_srch_str)
      disp_message(Sprintf("%s%s", prompt, str)->ptr, TRUE);
}

static int
dispincsrch(int ch, Str buf, Lineprop *prop)
{
    static Buffer sbuf;
    static Line *currentLine;
    static int 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_MIGEMO
    case 034:
      migemo_active = -migemo_active;
      goto done;
#endif

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

    if (do_next_search) {
      if (*str) {
          if (searchRoutine == forwardSearch)
            Currentbuf->pos += 1;
          SAVE_BUFPOSITION(&sbuf);
          if (srchcore(str, searchRoutine) == SR_NOTFOUND
            && searchRoutine == forwardSearch) {
            Currentbuf->pos -= 1;
            SAVE_BUFPOSITION(&sbuf);
          }
          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_MIGEMO
  done:
    while (*str++ != '\0') {
      if (migemo_active > 0)
          *prop++ |= PE_UNDER;
      else
          *prop++ &= ~PE_UNDER;
    }
#endif
    return -1;
}

void
isrch(int (*func) (Buffer *, char *), 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);
}

void
srch(int (*func) (Buffer *, char *), char *prompt)
{
    char *str;
    int result;
    int disp = FALSE;
    int pos;

    str = searchKeyData();
    if (str == NULL || *str == '\0') {
      str = inputStrHist(prompt, NULL, TextHist);
      if (str != NULL && *str == '\0')
          str = SearchString;
      if (str == NULL) {
          displayBuffer(Currentbuf, B_NORMAL);
          return;
      }
      disp = TRUE;
    }
    pos = Currentbuf->pos;
    if (func == forwardSearch)
      Currentbuf->pos += 1;
    result = srchcore(str, func);
    if (result & SR_FOUND)
      clear_mark(Currentbuf->currentLine);
    else
      Currentbuf->pos = pos;
    displayBuffer(Currentbuf, B_NORMAL);
    if (disp)
      disp_srchresult(result, prompt, str);
    searchRoutine = func;
}

/* Search regular expression forward */

DEFUN(srchfor, SEARCH SEARCH_FORE WHEREIS, "Search forward")
{
    srch(forwardSearch, "Forward: ");
}

DEFUN(isrchfor, ISEARCH, "Incremental search forward")
{
    isrch(forwardSearch, "I-search: ");
}

/* Search regular expression backward */

DEFUN(srchbak, SEARCH_BACK, "Search backward")
{
    srch(backwardSearch, "Backward: ");
}

DEFUN(isrchbak, ISEARCH_BACK, "Incremental search backward")
{
    isrch(backwardSearch, "I-search backward: ");
}

static void
srch_nxtprv(int reverse)
{
    int result;
    /* *INDENT-OFF* */
    static int (*routine[2]) (Buffer *, char *) = {
      forwardSearch, backwardSearch
    };
    /* *INDENT-ON* */

    if (searchRoutine == NULL) {
      /* FIXME: gettextize? */
      disp_message("No previous regular expression", TRUE);
      return;
    }
    if (reverse != 0)
      reverse = 1;
    if (searchRoutine == backwardSearch)
      reverse ^= 1;
    if (reverse == 0)
      Currentbuf->pos += 1;
    result = srchcore(SearchString, routine[reverse]);
    if (result & SR_FOUND)
      clear_mark(Currentbuf->currentLine);
    displayBuffer(Currentbuf, B_NORMAL);
    disp_srchresult(result, (reverse ? "Backward: " : "Forward: "),
                SearchString);
}

/* Search next matching */
DEFUN(srchnxt, SEARCH_NEXT, "Search next regexp")
{
    srch_nxtprv(0);
}

/* Search previous matching */
DEFUN(srchprv, SEARCH_PREV, "Search previous regexp")
{
    srch_nxtprv(1);
}

static void
shiftvisualpos(Buffer *buf, int shift)
{
    Line *l = buf->currentLine;
    buf->visualpos -= shift;
    if (buf->visualpos - l->bwidth >= buf->COLS)
      buf->visualpos = l->bwidth + buf->COLS - 1;
    else if (buf->visualpos - l->bwidth < 0)
      buf->visualpos = l->bwidth;
    arrangeLine(buf);
    if (buf->visualpos - l->bwidth == -shift && buf->cursorX == 0)
      buf->visualpos = l->bwidth;
}

/* Shift screen left */
DEFUN(shiftl, SHIFT_LEFT, "Shift screen left")
{
    int column;

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

/* Shift screen right */
DEFUN(shiftr, SHIFT_RIGHT, "Shift screen right")
{
    int column;

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

DEFUN(col1R, RIGHT, "Shift screen one column right")
{
    Buffer *buf = Currentbuf;
    Line *l = buf->currentLine;
    int j, column, n = searchKeyNum();

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

DEFUN(col1L, LEFT, "Shift screen one column")
{
    Buffer *buf = Currentbuf;
    Line *l = buf->currentLine;
    int j, n = searchKeyNum();

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

DEFUN(setEnv, SETENV, "Set environment variable")
{
    char *env;
    char *var, *value;

    CurrentKeyData = NULL;    /* not allowed in w3m-control: */
    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++;
      set_environ(var, value);
    }
    displayBuffer(Currentbuf, B_NORMAL);
}

DEFUN(pipeBuf, PIPE_BUF, "Send rendered document to pipe")
{
    Buffer *buf;
    char *cmd, *tmpf;
    FILE *f;

    CurrentKeyData = NULL;    /* not allowed in w3m-control: */
    cmd = searchKeyData();
    if (cmd == NULL || *cmd == '\0') {
      /* FIXME: gettextize? */
      cmd = inputLineHist("Pipe buffer to: ", "", IN_COMMAND, ShellHist);
    }
    if (cmd != NULL)
      cmd = conv_to_system(cmd);
    if (cmd == NULL || *cmd == '\0') {
      displayBuffer(Currentbuf, B_NORMAL);
      return;
    }
    tmpf = tmpfname(TMPF_DFL, NULL)->ptr;
    f = fopen(tmpf, "w");
    if (f == NULL) {
      /* FIXME: gettextize? */
      disp_message(Sprintf("Can't save buffer to %s", cmd)->ptr, TRUE);
      return;
    }
    saveBuffer(Currentbuf, f, TRUE);
    fclose(f);
    buf = getpipe(myExtCommand(cmd, shell_quote(tmpf), TRUE)->ptr);
    if (buf == NULL) {
      disp_message("Execution failed", TRUE);
      return;
    }
    else {
      buf->filename = cmd;
      buf->buffername = Sprintf("%s %s", PIPEBUFFERNAME,
                          conv_from_system(cmd))->ptr;
      buf->bufferprop |= (BP_INTERNAL | BP_NO_URL);
      if (buf->type == NULL)
          buf->type = "text/plain";
      pushBuffer(buf);
    }
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

/* Execute shell command and read output ac pipe. */
DEFUN(pipesh, PIPE_SHELL, "Execute shell command and browse")
{
    Buffer *buf;
    char *cmd;

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

/* Execute shell command and load entire output to buffer */
DEFUN(readsh, READ_SHELL, "Execute shell command and load")
{
    Buffer *buf;
    MySignalHandler(*prevtrap) ();
    char *cmd;

    CurrentKeyData = NULL;    /* not allowed in w3m-control: */
    cmd = searchKeyData();
    if (cmd == NULL || *cmd == '\0') {
      cmd = inputLineHist("(read shell)!", "", IN_COMMAND, ShellHist);
    }
    if (cmd != NULL)
      cmd = conv_to_system(cmd);
    if (cmd == NULL || *cmd == '\0') {
      displayBuffer(Currentbuf, B_NORMAL);
      return;
    }
    prevtrap = mySignal(SIGINT, intTrap);
    crmode();
    buf = getshell(cmd);
    mySignal(SIGINT, prevtrap);
    term_raw();
    if (buf == NULL) {
      /* FIXME: gettextize? */
      disp_message("Execution failed", TRUE);
      return;
    }
    else {
      buf->bufferprop |= (BP_INTERNAL | BP_NO_URL);
      if (buf->type == NULL)
          buf->type = "text/plain";
      pushBuffer(buf);
    }
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

/* Execute shell command */
DEFUN(execsh, EXEC_SHELL SHELL, "Execute shell command")
{
    char *cmd;

    CurrentKeyData = NULL;    /* not allowed in w3m-control: */
    cmd = searchKeyData();
    if (cmd == NULL || *cmd == '\0') {
      cmd = inputLineHist("(exec shell)!", "", IN_COMMAND, ShellHist);
    }
    if (cmd != NULL)
      cmd = conv_to_system(cmd);
    if (cmd != NULL && *cmd != '\0') {
      fmTerm();
      printf("\n");
      system(cmd);
      /* FIXME: gettextize? */
      printf("\n[Hit any key]");
      fflush(stdout);
      fmInit();
      getch();
    }
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

/* Load file */
DEFUN(ldfile, LOAD, "Load local file")
{
    char *fn;

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

/* Load help file */
DEFUN(ldhelp, HELP, "View help")
{
#ifdef USE_HELP_CGI
    char *lang;
    int n;
    Str tmp;

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

static void
cmd_loadfile(char *fn)
{
    Buffer *buf;

    buf = loadGeneralFile(file_to_url(fn), NULL, NO_REFERER, 0, NULL);
    if (buf == NULL) {
      /* FIXME: gettextize? */
      char *emsg = Sprintf("%s not found", conv_from_system(fn))->ptr;
      disp_err_message(emsg, FALSE);
    }
    else if (buf != NO_BUFFER) {
      pushBuffer(buf);
      if (RenderFrame && Currentbuf->frameset != NULL)
          rFrame();
    }
    displayBuffer(Currentbuf, B_NORMAL);
}

/* Move cursor left */
static void
_movL(int n)
{
    int i, m = searchKeyNum();
    if (Currentbuf->firstLine == NULL)
      return;
    for (i = 0; i < m; i++)
      cursorLeft(Currentbuf, n);
    displayBuffer(Currentbuf, B_NORMAL);
}

DEFUN(movL, MOVE_LEFT,
      "Move cursor left (a half screen shift at the left edge)")
{
    _movL(Currentbuf->COLS / 2);
}

DEFUN(movL1, MOVE_LEFT1, "Move cursor left (1 columns shift at the left edge)")
{
    _movL(1);
}

/* Move cursor downward */
static void
_movD(int n)
{
    int i, m = searchKeyNum();
    if (Currentbuf->firstLine == NULL)
      return;
    for (i = 0; i < m; i++)
      cursorDown(Currentbuf, n);
    displayBuffer(Currentbuf, B_NORMAL);
}

DEFUN(movD, MOVE_DOWN,
      "Move cursor down (a half screen scroll at the end of screen)")
{
    _movD((Currentbuf->LINES + 1) / 2);
}

DEFUN(movD1, MOVE_DOWN1,
      "Move cursor down (1 line scroll at the end of screen)")
{
    _movD(1);
}

/* move cursor upward */
static void
_movU(int n)
{
    int i, m = searchKeyNum();
    if (Currentbuf->firstLine == NULL)
      return;
    for (i = 0; i < m; i++)
      cursorUp(Currentbuf, n);
    displayBuffer(Currentbuf, B_NORMAL);
}

DEFUN(movU, MOVE_UP,
      "Move cursor up (a half screen scroll at the top of screen)")
{
    _movU((Currentbuf->LINES + 1) / 2);
}

DEFUN(movU1, MOVE_UP1, "Move cursor up (1 line scrol at the top of screen)")
{
    _movU(1);
}

/* Move cursor right */
static void
_movR(int n)
{
    int i, m = searchKeyNum();
    if (Currentbuf->firstLine == NULL)
      return;
    for (i = 0; i < m; i++)
      cursorRight(Currentbuf, n);
    displayBuffer(Currentbuf, B_NORMAL);
}

DEFUN(movR, MOVE_RIGHT,
      "Move cursor right (a half screen shift at the right edge)")
{
    _movR(Currentbuf->COLS / 2);
}

DEFUN(movR1, MOVE_RIGHT1,
      "Move cursor right (1 columns shift at the right edge)")
{
    _movR(1);
}

/* 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;
}

DEFUN(movLW, PREV_WORD, "Move to previous word")
{
    char *lb;
    Lineprop *pb;
    Line *pline;
    int ppos;
    int i, n = searchKeyNum();

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

    for (i = 0; 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;
}

DEFUN(movRW, NEXT_WORD, "Move to next word")
{
    char *lb;
    Lineprop *pb;
    Line *pline;
    int ppos;
    int i, n = searchKeyNum();

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

    for (i = 0; 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);
}

static void
_quitfm(int confirm)
{
    char *ans = "y";

    if (checkDownloadList())
      /* FIXME: gettextize? */
      ans = inputChar("Download process retains. "
                  "Do you want to exit w3m? (y/n)");
    else if (confirm)
      /* FIXME: gettextize? */
      ans = inputChar("Do you want to exit w3m? (y/n)");
    if (!(ans && TOLOWER(*ans) == 'y')) {
      displayBuffer(Currentbuf, B_NORMAL);
      return;
    }

    term_title("");           /* XXX */
#ifdef USE_IMAGE
    if (activeImage)
      termImage();
#endif
    fmTerm();
#ifdef USE_COOKIE
    save_cookies();
#endif                        /* USE_COOKIE */
#ifdef USE_HISTORY
    if (UseHistory && SaveURLHist)
      saveHistory(URLHist, URLHistSize);
#endif                        /* USE_HISTORY */
    w3m_exit(0);
}

/* Quit */
DEFUN(quitfm, ABORT EXIT, "Quit w3m without confirmation")
{
    _quitfm(FALSE);
}

/* Question and Quit */
DEFUN(qquitfm, QUIT, "Quit w3m")
{
    _quitfm(confirm_on_quit);
}

/* Select buffer */
DEFUN(selBuf, SELECT, "Go to buffer selection panel")
{
    Buffer *buf;
    int ok;
    char cmd;

    ok = FALSE;
    do {
      buf = selectBuffer(Firstbuf, Currentbuf, &cmd);
      switch (cmd) {
      case 'B':
          ok = TRUE;
          break;
      case '\n':
      case ' ':
          Currentbuf = buf;
          ok = TRUE;
          break;
      case 'D':
          delBuffer(buf);
          if (Firstbuf == NULL) {
            /* No more buffer */
            Firstbuf = nullBuffer();
            Currentbuf = Firstbuf;
          }
          break;
      case 'q':
          qquitfm();
          break;
      case 'Q':
          quitfm();
          break;
      }
    } while (!ok);

    for (buf = Firstbuf; buf != NULL; buf = buf->nextBuffer) {
      if (buf == Currentbuf)
          continue;
#ifdef USE_IMAGE
      deleteImage(buf);
#endif
      if (clear_buffer)
          tmpClearBuffer(buf);
    }
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

/* Suspend (on BSD), or run interactive shell (on SysV) */
DEFUN(susp, INTERRUPT SUSPEND, "Stop loading document")
{
#ifndef SIGSTOP
    char *shell;
#endif                        /* not SIGSTOP */
    move(LASTLINE, 0);
    clrtoeolx();
    refresh();
    fmTerm();
#ifndef SIGSTOP
    shell = getenv("SHELL");
    if (shell == NULL)
      shell = "/bin/sh";
    system(shell);
#else                   /* SIGSTOP */
    kill((pid_t) 0, SIGSTOP);
#endif                        /* SIGSTOP */
    fmInit();
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

/* Go to specified line */
static void
_goLine(char *l)
{
    if (l == NULL || *l == '\0' || Currentbuf->currentLine == NULL) {
      displayBuffer(Currentbuf, B_FORCE_REDRAW);
      return;
    }
    Currentbuf->pos = 0;
    if (((*l == '^') || (*l == '$')) && 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->LINES + 1) / 2, TRUE);
      Currentbuf->currentLine = Currentbuf->lastLine;
    }
    else
      gotoRealLine(Currentbuf, atoi(l));
    arrangeCursor(Currentbuf);
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

DEFUN(goLine, GOTO_LINE, "Go to specified line")
{

    char *str = searchKeyData();
    if (prec_num)
      _goLine("^");
    else if (str)
      _goLine(str);
    else
      /* FIXME: gettextize? */
      _goLine(inputStr("Goto line: ", ""));
}


DEFUN(goLineF, BEGIN, "Go to the first line")
{
    _goLine("^");
}

DEFUN(goLineL, END, "Go to the last line")
{
    _goLine("$");
}

/* Go to the beginning of the line */
DEFUN(linbeg, LINE_BEGIN, "Go to the beginning of line")
{
    if (Currentbuf->firstLine == NULL)
      return;
    while (Currentbuf->currentLine->prev && Currentbuf->currentLine->bpos)
      cursorUp0(Currentbuf, 1);
    Currentbuf->pos = 0;
    arrangeCursor(Currentbuf);
    displayBuffer(Currentbuf, B_NORMAL);
}

/* Go to the bottom of the line */
DEFUN(linend, LINE_END, "Go to the end of line")
{
    if (Currentbuf->firstLine == NULL)
      return;
    while (Currentbuf->currentLine->next
         && Currentbuf->currentLine->next->bpos)
      cursorDown0(Currentbuf, 1);
    Currentbuf->pos = Currentbuf->currentLine->len - 1;
    arrangeCursor(Currentbuf);
    displayBuffer(Currentbuf, B_NORMAL);
}

static int
cur_real_linenumber(Buffer *buf)
{
    Line *l, *cur = buf->currentLine;
    int n;

    if (!cur)
      return 1;
    n = cur->real_linenumber ? cur->real_linenumber : 1;
    for (l = buf->firstLine; l && l != cur && l->real_linenumber == 0; l = l->next) {     /* header */
      if (l->bpos == 0)
          n++;
    }
    return n;
}

/* Run editor on the current buffer */
DEFUN(editBf, EDIT, "Edit current document")
{
    char *fn = Currentbuf->filename;
    Str cmd;

    if (fn == NULL || Currentbuf->pagerSource != NULL ||    /* Behaving as a pager */
      (Currentbuf->type == NULL && Currentbuf->edit == NULL) ||   /* Reading shell */
      Currentbuf->real_scheme != SCM_LOCAL || !strcmp(Currentbuf->currentURL.file, "-") ||      /* file is std input  */
      Currentbuf->bufferprop & BP_FRAME) {      /* Frame */
      disp_err_message("Can't edit other than local file", TRUE);
      return;
    }
    if (Currentbuf->edit)
      cmd = unquote_mailcap(Currentbuf->edit, Currentbuf->real_type, fn,
                        checkHeader(Currentbuf, "Content-Type:"), NULL);
    else
      cmd = myEditor(Editor, shell_quote(fn),
                   cur_real_linenumber(Currentbuf));
    fmTerm();
    system(cmd->ptr);
    fmInit();

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

/* Run editor on the current screen */
DEFUN(editScr, EDIT_SCREEN, "Edit currently rendered document")
{
    char *tmpf;
    FILE *f;

    tmpf = tmpfname(TMPF_DFL, NULL)->ptr;
    f = fopen(tmpf, "w");
    if (f == NULL) {
      /* FIXME: gettextize? */
      disp_err_message(Sprintf("Can't open %s", tmpf)->ptr, TRUE);
      return;
    }
    saveBuffer(Currentbuf, f, TRUE);
    fclose(f);
    fmTerm();
    system(myEditor(Editor, shell_quote(tmpf),
                cur_real_linenumber(Currentbuf))->ptr);
    fmInit();
    unlink(tmpf);
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

#ifdef USE_MARK

/* Set / unset mark */
DEFUN(_mark, MARK, "Set/unset mark")
{
    Line *l;
    if (!use_mark)
      return;
    if (Currentbuf->firstLine == NULL)
      return;
    l = Currentbuf->currentLine;
    l->propBuf[Currentbuf->pos] ^= PE_MARK;
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

/* Go to next mark */
DEFUN(nextMk, NEXT_MARK, "Move to next word")
{
    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) {
            Currentbuf->currentLine = l;
            Currentbuf->pos = i;
            arrangeCursor(Currentbuf);
            displayBuffer(Currentbuf, B_NORMAL);
            return;
          }
      }
      l = l->next;
      i = 0;
    }
    /* FIXME: gettextize? */
    disp_message("No mark exist after here", TRUE);
}

/* Go to previous mark */
DEFUN(prevMk, PREV_MARK, "Move to previous mark")
{
    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) {
            Currentbuf->currentLine = l;
            Currentbuf->pos = i;
            arrangeCursor(Currentbuf);
            displayBuffer(Currentbuf, B_NORMAL);
            return;
          }
      }
      l = l->prev;
      if (l != NULL)
          i = l->len - 1;
    }
    /* FIXME: gettextize? */
    disp_message("No mark exist before here", TRUE);
}

/* Mark place to which the regular expression matches */
DEFUN(reMark, REG_MARK, "Set mark using regexp")
{
    Line *l;
    char *str;
    char *p, *p1, *p2;

    if (!use_mark)
      return;
    str = searchKeyData();
    if (str == NULL || *str == '\0') {
      str = inputStrHist("(Mark)Regexp: ", MarkString, TextHist);
      if (str == NULL || *str == '\0') {
          displayBuffer(Currentbuf, B_NORMAL);
          return;
      }
    }
    str = conv_search_string(str, DisplayCharset);
    if ((str = regexCompile(str, 1)) != NULL) {
      disp_message(str, TRUE);
      return;
    }
    MarkString = str;
    for (l = Currentbuf->firstLine; l != NULL; l = l->next) {
      p = l->lineBuf;
      for (;;) {
          if (regexMatch(p, &l->lineBuf[l->len] - p, p == l->lineBuf) == 1) {
            matchedPosition(&p1, &p2);
            l->propBuf[p1 - l->lineBuf] |= PE_MARK;
            p = p2;
          }
          else
            break;
      }
    }

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

static Buffer *
loadNormalBuf(Buffer *buf, int renderframe)
{
    pushBuffer(buf);
    if (renderframe && RenderFrame && Currentbuf->frameset != NULL)
      rFrame();
    return buf;
}

static Buffer *
loadLink(char *url, char *target, char *referer, FormList *request)
{
    Buffer *buf, *nfbuf;
    union frameset_element *f_element = NULL;
    int flag = 0;
    ParsedURL *base, pu;

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

    base = baseURL(Currentbuf);
    if (base == NULL ||
      base->scheme == SCM_LOCAL || base->scheme == SCM_LOCAL_CGI)
      referer = NO_REFERER;
    if (referer == NULL)
      referer = parsedURL2Str(&Currentbuf->currentURL)->ptr;
    buf = loadGeneralFile(url, baseURL(Currentbuf), referer, flag, request);
    if (buf == NULL) {
      char *emsg = Sprintf("Can't load %s", url)->ptr;
      disp_err_message(emsg, FALSE);
      return NULL;
    }

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

    if (buf == NO_BUFFER) {
      return NULL;
    }
    if (!on_target)           /* open link as an indivisual page */
      return loadNormalBuf(buf, TRUE);

    if (do_download)          /* download (thus no need to render frame) */
      return loadNormalBuf(buf, FALSE);

    if (target == NULL ||     /* no target specified (that means this page is not a frame page) */
      !strcmp(target, "_top") ||    /* this link is specified to be opened as an indivisual * page */
      !(Currentbuf->bufferprop & BP_FRAME)      /* This page is not a frame page */
      ) {
      return loadNormalBuf(buf, TRUE);
    }
    nfbuf = Currentbuf->linkBuffer[LB_N_FRAME];
    if (nfbuf == NULL) {
      /* original page (that contains <frameset> tag) doesn't exist */
      return loadNormalBuf(buf, TRUE);
    }

    f_element = search_frame(nfbuf->frameset, target);
    if (f_element == NULL) {
      /* specified target doesn't exist in this frameset */
      return loadNormalBuf(buf, TRUE);
    }

    /* frame page */

    /* stack current frameset */
    pushFrameTree(&(nfbuf->frameQ), copyFrameSet(nfbuf->frameset), Currentbuf);
    /* delete frame view buffer */
    delBuffer(Currentbuf);
    Currentbuf = nfbuf;
    /* nfbuf->frameset = copyFrameSet(nfbuf->frameset); */
    resetFrameElement(f_element, buf, referer, request);
    discardBuffer(buf);
    rFrame();
    {
      Anchor *al = NULL;
      char *label = pu.label;

      if (label && f_element->element->attr == F_BODY) {
          al = searchAnchor(f_element->body->nameList, label);
      }
      if (!al) {
          label = Strnew_m_charp("_", target, NULL)->ptr;
          al = searchURLLabel(Currentbuf, label);
      }
      if (al) {
          gotoLine(Currentbuf, al->start.line);
          if (label_topline)
            Currentbuf->topLine = lineSkip(Currentbuf, Currentbuf->topLine,
                                     Currentbuf->currentLine->
                                     linenumber -
                                     Currentbuf->topLine->linenumber,
                                     FALSE);
          Currentbuf->pos = al->start.pos;
          arrangeCursor(Currentbuf);
      }
    }
    displayBuffer(Currentbuf, B_NORMAL);
    return buf;
}

static void
gotoLabel(char *label)
{
    Buffer *buf;
    Anchor *al;
    int i;

    al = searchURLLabel(Currentbuf, label);
    if (al == NULL) {
      /* FIXME: gettextize? */
      disp_message(Sprintf("%s is not found", label)->ptr, TRUE);
      return;
    }
    buf = newBuffer(Currentbuf->width);
    copyBuffer(buf, Currentbuf);
    for (i = 0; i < MAX_LB; i++)
      buf->linkBuffer[i] = NULL;
    buf->currentURL.label = allocStr(label, -1);
    pushHashHist(URLHist, parsedURL2Str(&buf->currentURL)->ptr);
    (*buf->clone)++;
    pushBuffer(buf);
    gotoLine(Currentbuf, al->start.line);
    if (label_topline)
      Currentbuf->topLine = lineSkip(Currentbuf, Currentbuf->topLine,
                               Currentbuf->currentLine->linenumber
                               - Currentbuf->topLine->linenumber,
                               FALSE);
    Currentbuf->pos = al->start.pos;
    arrangeCursor(Currentbuf);
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
    return;
}

/* follow HREF link */
DEFUN(followA, GOTO_LINK, "Go to current link")
{
    Line *l;
    Anchor *a;
    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 && a->image && a->image->map) {
      _followForm(FALSE);
      return;
    }
    if (a && a->image && a->image->ismap) {
      getMapXY(Currentbuf, a, &x, &y);
      map = 1;
    }
#else
    a = retrieveCurrentMap(Currentbuf);
    if (a) {
      _followForm(FALSE);
      return;
    }
#endif
    a = retrieveCurrentAnchor(Currentbuf);
    if (a == NULL) {
      _followForm(FALSE);
      return;
    }
    if (*a->url == '#') {     /* index within this buffer */
      gotoLabel(a->url + 1);
      return;
    }
    parseURL2(a->url, &u, baseURL(Currentbuf));
    if (Strcmp(parsedURL2Str(&u), parsedURL2Str(&Currentbuf->currentURL)) == 0) {
      /* index within this buffer */
      if (u.label) {
          gotoLabel(u.label);
          return;
      }
    }
    if (!strncasecmp(a->url, "mailto:", 7)
#ifdef USE_W3MMAILER
      && non_null(Mailer) && strchr(a->url, '?') == NULL
#endif
      ) {
      /* invoke external mailer */
      Str to = Strnew_charp(a->url + 7);
#ifndef USE_W3MMAILER
      char *pos;
      if (!non_null(Mailer)) {
          /* FIXME: gettextize? */
          disp_err_message("no mailer is specified", TRUE);
          return;
      }
      if ((pos = strchr(to->ptr, '?')) != NULL)
          Strtruncate(to, pos - to->ptr);
#endif
      fmTerm();
      system(myExtCommand(Mailer, shell_quote(file_unquote(to->ptr)),
                      FALSE)->ptr);
      fmInit();
      displayBuffer(Currentbuf, B_FORCE_REDRAW);
      pushHashHist(URLHist, a->url);
      return;
    }
#if 0
    else if (!strncasecmp(a->url, "news:", 5) && strchr(a->url, '@') == NULL) {
      /* news:newsgroup is not supported */
      /* FIXME: gettextize? */
      disp_err_message("news:newsgroup_name is not supported", TRUE);
      return;
    }
#endif                        /* USE_NNTP */
    url = a->url;
#ifdef USE_IMAGE
    if (map)
      url = Sprintf("%s?%d,%d", a->url, x, y)->ptr;
#endif

    if (check_target && open_tab_blank && a->target &&
      (!strcasecmp(a->target, "_new") || !strcasecmp(a->target, "_blank"))) {
      Buffer *buf;

      _newT();
      buf = Currentbuf;
      loadLink(url, a->target, a->referer, NULL);
      if (buf != Currentbuf)
          delBuffer(buf);
      else
          deleteTab(CurrentTab);
      displayBuffer(Currentbuf, B_FORCE_REDRAW);
      return;
    }
    loadLink(url, a->target, a->referer, NULL);
    displayBuffer(Currentbuf, B_NORMAL);
}

/* follow HREF link in the buffer */
void
bufferA(void)
{
    on_target = FALSE;
    followA();
    on_target = TRUE;
}

/* view inline image */
DEFUN(followI, VIEW_IMAGE, "View image")
{
    Line *l;
    Anchor *a;
    Buffer *buf;

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

    a = retrieveCurrentImg(Currentbuf);
    if (a == NULL)
      return;
    /* FIXME: gettextize? */
    message(Sprintf("loading %s", a->url)->ptr, 0, 0);
    refresh();
    buf = loadGeneralFile(a->url, baseURL(Currentbuf), NULL, 0, NULL);
    if (buf == NULL) {
      /* FIXME: gettextize? */
      char *emsg = Sprintf("Can't load %s", a->url)->ptr;
      disp_err_message(emsg, FALSE);
    }
    else if (buf != NO_BUFFER) {
      pushBuffer(buf);
    }
    displayBuffer(Currentbuf, B_NORMAL);
}

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);
#ifdef USE_M17N
    list->charset = srclist->charset;
#endif
    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;
}

#ifdef USE_M17N
static Str
conv_form_encoding(Str val, FormItemList *fi, Buffer *buf)
{
    wc_ces charset = SystemCharset;

    if (fi->parent->charset)
      charset = fi->parent->charset;
    else if (buf->document_charset && buf->document_charset != WC_CES_US_ASCII)
      charset = buf->document_charset;
    return wc_Str_conv_strict(val, InnerCharset, charset);
}
#else
#define conv_form_encoding(val, fi, buf) (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", CurrentPid,
                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 = Strdup(conv_form_encoding(f2->name, fi, Currentbuf));
            Strcat_charp(*query, ".x");
            form_write_data(body, fi->parent->boundary, (*query)->ptr,
                        Sprintf("%d", x)->ptr);
            *query = Strdup(conv_form_encoding(f2->name, fi, Currentbuf));
            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 != NULL) {
            /* not IMAGE */
            *query = conv_form_encoding(f2->value, fi, Currentbuf);
            if (f2->type == FORM_INPUT_FILE)
                form_write_from_file(body, fi->parent->boundary,
                               conv_form_encoding(f2->name, fi,
                                              Currentbuf)->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)->ptr,
                            (*query)->ptr);
          }
      }
      else {
          /* not multipart */
          if (f2->type == FORM_INPUT_IMAGE) {
            int x = 0, y = 0;
#ifdef USE_IMAGE
            getMapXY(Currentbuf, retrieveCurrentImg(Currentbuf), &x, &y);
#endif
            Strcat(*query,
                   Str_form_quote(conv_form_encoding
                              (f2->name, fi, Currentbuf)));
            Strcat(*query, Sprintf(".x=%d&", x));
            Strcat(*query,
                   Str_form_quote(conv_form_encoding
                              (f2->name, fi, Currentbuf)));
            Strcat(*query, Sprintf(".y=%d", y));
          }
          else {
            /* not IMAGE */
            if (f2->name && f2->name->length > 0) {
                Strcat(*query,
                     Str_form_quote(conv_form_encoding
                                (f2->name, fi, Currentbuf)));
                Strcat_char(*query, '=');
            }
            if (f2->value != NULL) {
                if (fi->parent->method == FORM_METHOD_INTERNAL)
                  Strcat(*query, Str_form_quote(f2->value));
                else {
                  Strcat(*query,
                         Str_form_quote(conv_form_encoding
                                    (f2->value, fi, Currentbuf)));
                }
            }
          }
          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);
    }
}

/* submit form */
DEFUN(submitForm, SUBMIT, "Submit form")
{
    _followForm(TRUE);
}

/* process form */
void
followForm(void)
{
    _followForm(FALSE);
}

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

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

    a = retrieveCurrentForm(Currentbuf);
    if (a == NULL)
      return;
    fi = (FormItemList *)a->url;
    switch (fi->type) {
    case FORM_INPUT_TEXT:
      if (submit)
          goto do_submit;
      if (fi->readonly)
          /* FIXME: gettextize? */
          disp_message_nsec("Read only field!", FALSE, 1, TRUE, FALSE);
      /* FIXME: gettextize? */
      p = inputStrHist("TEXT:", fi->value ? fi->value->ptr : NULL, TextHist);
      if (p == NULL || fi->readonly)
          break;
      fi->value = Strnew_charp(p);
      formUpdateBuffer(a, 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)
          /* FIXME: gettextize? */
          disp_message_nsec("Read only field!", FALSE, 1, TRUE, FALSE);
      /* FIXME: gettextize? */
      p = inputFilenameHist("Filename:", fi->value ? fi->value->ptr : NULL,
                        NULL);
      if (p == NULL || fi->readonly)
          break;
      fi->value = Strnew_charp(p);
      formUpdateBuffer(a, 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) {
          /* FIXME: gettextize? */
          disp_message_nsec("Read only field!", FALSE, 1, TRUE, FALSE);
          break;
      }
      /* FIXME: gettextize? */
      p = inputLine("Password:", fi->value ? fi->value->ptr : NULL,
                  IN_PASSWORD);
      if (p == NULL)
          break;
      fi->value = Strnew_charp(p);
      formUpdateBuffer(a, Currentbuf, fi);
      if (fi->accept)
          goto do_submit;
      break;
    case FORM_TEXTAREA:
      if (submit)
          goto do_submit;
      if (fi->readonly)
          /* FIXME: gettextize? */
          disp_message_nsec("Read only field!", FALSE, 1, TRUE, FALSE);
      input_textarea(fi);
      formUpdateBuffer(a, Currentbuf, fi);
      break;
    case FORM_INPUT_RADIO:
      if (submit)
          goto do_submit;
      if (fi->readonly) {
          /* FIXME: gettextize? */
          disp_message_nsec("Read only field!", FALSE, 1, TRUE, FALSE);
          break;
      }
      formRecheckRadio(a, Currentbuf, fi);
      break;
    case FORM_INPUT_CHECKBOX:
      if (submit)
          goto do_submit;
      if (fi->readonly) {
          /* FIXME: gettextize? */
          disp_message_nsec("Read only field!", FALSE, 1, TRUE, FALSE);
          break;
      }
      fi->checked = !fi->checked;
      formUpdateBuffer(a, Currentbuf, fi);
      break;
#ifdef MENU_SELECT
    case FORM_SELECT:
      if (submit)
          goto do_submit;
      if (!formChooseOptionByMenu(fi,
                            Currentbuf->cursorX - Currentbuf->pos +
                            a->start.pos + Currentbuf->rootX,
                            Currentbuf->cursorY + Currentbuf->rootY))
          break;
      formUpdateBuffer(a, 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) {
          if ((p = strchr(tmp2->ptr, '?')) != NULL)
            Strshrink(tmp2, (tmp2->ptr + tmp2->length) - p);
          Strcat_charp(tmp2, "?");
          Strcat(tmp2, tmp);
          loadLink(tmp2->ptr, a->target, NULL, NULL);
      }
      else if (fi->parent->method == FORM_METHOD_POST) {
          Buffer *buf;
          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;
          }
          buf = loadLink(tmp2->ptr, a->target, NULL, fi->parent);
          if (multipart) {
            unlink(fi->parent->body);
          }
          if (buf && !(buf->bufferprop & BP_REDIRECTED)) {  /* buf must be Currentbuf */
            /* BP_REDIRECTED means that the buffer is obtained through
             * Location: header. In this case, buf->form_submit must not be set
             * because the page is not loaded by POST method but GET method.
             */
            buf->form_submit = save_submit_formlist(fi);
          }
      }
      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 (i = 0; i < Currentbuf->formitem->nanchor; i++) {
          a2 = &Currentbuf->formitem->anchors[i];
          f2 = (FormItemList *)a2->url;
          if (f2->parent == fi->parent &&
            f2->name && f2->value &&
            f2->type != FORM_INPUT_SUBMIT &&
            f2->type != FORM_INPUT_HIDDEN &&
            f2->type != FORM_INPUT_RESET) {
            f2->value = f2->init_value;
            f2->checked = f2->init_checked;
#ifdef MENU_SELECT
            f2->label = f2->init_label;
            f2->selected = f2->init_selected;
#endif                        /* MENU_SELECT */
            formUpdateBuffer(a2, Currentbuf, f2);
          }
      }
      break;
    case FORM_INPUT_HIDDEN:
    default:
      break;
    }
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

/* go to the top anchor */
DEFUN(topA, LINK_BEGIN, "Go to the first link")
{
    HmarkerList *hl = Currentbuf->hmarklist;
    BufferPoint *po;
    Anchor *an;
    int hseq = 0;

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

    if (prec_num > hl->nmark)
      hseq = hl->nmark - 1;
    else if (prec_num > 0)
      hseq = prec_num - 1;
    do {
      if (hseq >= hl->nmark)
          return;
      po = hl->marks + hseq;
      an = retrieveAnchor(Currentbuf->href, po->line, po->pos);
      if (an == NULL)
          an = retrieveAnchor(Currentbuf->formitem, po->line, po->pos);
      hseq++;
    } while (an == NULL);

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

/* go to the last anchor */
DEFUN(lastA, LINK_END, "Go to the last link")
{
    HmarkerList *hl = Currentbuf->hmarklist;
    BufferPoint *po;
    Anchor *an;
    int hseq;

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

    if (prec_num >= hl->nmark)
      hseq = 0;
    else if (prec_num > 0)
      hseq = hl->nmark - prec_num;
    else
      hseq = hl->nmark - 1;
    do {
      if (hseq < 0)
          return;
      po = hl->marks + hseq;
      an = retrieveAnchor(Currentbuf->href, po->line, po->pos);
      if (an == NULL)
          an = retrieveAnchor(Currentbuf->formitem, po->line, po->pos);
      hseq--;
    } while (an == NULL);

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

/* go to the next anchor */
DEFUN(nextA, NEXT_LINK, "Move to next link")
{
    _nextA(FALSE);
}

/* go to the previous anchor */
DEFUN(prevA, PREV_LINK, "Move to previous link")
{
    _prevA(FALSE);
}

/* go to the next visited anchor */
DEFUN(nextVA, NEXT_VISITED, "Move to next visited link")
{
    _nextA(TRUE);
}

/* go to the previous visited anchor */
DEFUN(prevVA, PREV_VISITED, "Move to previous visited link")
{
    _prevA(TRUE);
}

/* go to the next [visited] anchor */
static void
_nextA(int visited)
{
    HmarkerList *hl = Currentbuf->hmarklist;
    BufferPoint *po;
    Anchor *an, *pan;
    int i, x, y, n = searchKeyNum();
    ParsedURL url;

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

    an = retrieveCurrentAnchor(Currentbuf);
    if (visited != TRUE && an == NULL)
      an = retrieveCurrentForm(Currentbuf);

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

    if (visited == TRUE) {
      n = hl->nmark;
    }

    for (i = 0; i < n; i++) {
      pan = an;
      if (an && an->hseq >= 0) {
          int hseq = an->hseq + 1;
          do {
            if (hseq >= hl->nmark) {
                if (visited == TRUE)
                  return;
                an = pan;
                goto _end;
            }
            po = &hl->marks[hseq];
            an = retrieveAnchor(Currentbuf->href, po->line, po->pos);
            if (visited != TRUE && an == NULL)
                an = retrieveAnchor(Currentbuf->formitem, po->line,
                              po->pos);
            hseq++;
            if (visited == TRUE && an) {
                parseURL2(an->url, &url, baseURL(Currentbuf));
                if (getHashHist(URLHist, parsedURL2Str(&url)->ptr)) {
                  goto _end;
                }
            }
          } while (an == NULL || an == pan);
      }
      else {
          an = closest_next_anchor(Currentbuf->href, NULL, x, y);
          if (visited != TRUE)
            an = closest_next_anchor(Currentbuf->formitem, an, x, y);
          if (an == NULL) {
            if (visited == TRUE)
                return;
            an = pan;
            break;
          }
          x = an->start.pos;
          y = an->start.line;
          if (visited == TRUE) {
            parseURL2(an->url, &url, baseURL(Currentbuf));
            if (getHashHist(URLHist, parsedURL2Str(&url)->ptr)) {
                goto _end;
            }
          }
      }
    }
    if (visited == TRUE)
      return;

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

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

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

    an = retrieveCurrentAnchor(Currentbuf);
    if (visited != TRUE && an == NULL)
      an = retrieveCurrentForm(Currentbuf);

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

    if (visited == TRUE) {
      n = hl->nmark;
    }

    for (i = 0; i < n; i++) {
      pan = an;
      if (an && an->hseq >= 0) {
          int hseq = an->hseq - 1;
          do {
            if (hseq < 0) {
                if (visited == TRUE)
                  return;
                an = pan;
                goto _end;
            }
            po = hl->marks + hseq;
            an = retrieveAnchor(Currentbuf->href, po->line, po->pos);
            if (visited != TRUE && an == NULL)
                an = retrieveAnchor(Currentbuf->formitem, po->line,
                              po->pos);
            hseq--;
            if (visited == TRUE && an) {
                parseURL2(an->url, &url, baseURL(Currentbuf));
                if (getHashHist(URLHist, parsedURL2Str(&url)->ptr)) {
                  goto _end;
                }
            }
          } while (an == NULL || an == pan);
      }
      else {
          an = closest_prev_anchor(Currentbuf->href, NULL, x, y);
          if (visited != TRUE)
            an = closest_prev_anchor(Currentbuf->formitem, an, x, y);
          if (an == NULL) {
            if (visited == TRUE)
                return;
            an = pan;
            break;
          }
          x = an->start.pos;
          y = an->start.line;
          if (visited == TRUE && an) {
            parseURL2(an->url, &url, baseURL(Currentbuf));
            if (getHashHist(URLHist, parsedURL2Str(&url)->ptr)) {
                goto _end;
            }
          }
      }
    }
    if (visited == TRUE)
      return;

  _end:
    if (an == NULL || an->hseq < 0)
      return;
    po = hl->marks + an->hseq;
    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 = searchKeyNum();

    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; 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 */
DEFUN(nextL, NEXT_LEFT, "Move to next left link")
{
    nextX(-1, 0);
}

/* go to the next left-up anchor */
DEFUN(nextLU, NEXT_LEFT_UP, "Move to next left (or upward) link")
{
    nextX(-1, -1);
}

/* go to the next right anchor */
DEFUN(nextR, NEXT_RIGHT, "Move to next right link")
{
    nextX(1, 0);
}

/* go to the next right-down anchor */
DEFUN(nextRD, NEXT_RIGHT_DOWN, "Move to next right (or downward) link")
{
    nextX(1, 1);
}

/* go to the next downward anchor */
DEFUN(nextD, NEXT_DOWN, "Move to next downward link")
{
    nextY(1);
}

/* go to the next upward anchor */
DEFUN(nextU, NEXT_UP, "Move to next upward link")
{
    nextY(-1);
}

/* go to the next bufferr */
DEFUN(nextBf, NEXT, "Move to next buffer")
{
    Buffer *buf;
    int i;

    for (i = 0; i < PREC_NUM; i++) {
      buf = prevBuffer(Firstbuf, Currentbuf);
      if (!buf) {
          if (i == 0)
            return;
          break;
      }
      Currentbuf = buf;
    }
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

/* go to the previous bufferr */
DEFUN(prevBf, PREV, "Move to previous buffer")
{
    Buffer *buf;
    int i;

    for (i = 0; i < PREC_NUM; i++) {
      buf = Currentbuf->nextBuffer;
      if (!buf) {
          if (i == 0)
            return;
          break;
      }
      Currentbuf = buf;
    }
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

static int
checkBackBuffer(Buffer *buf)
{
    Buffer *fbuf = buf->linkBuffer[LB_N_FRAME];

    if (fbuf) {
      if (fbuf->frameQ)
          return TRUE;  /* Currentbuf has stacked frames */
      /* when no frames stacked and next is frame source, try next's
       * nextBuffer */
      if (RenderFrame && fbuf == buf->nextBuffer) {
          if (fbuf->nextBuffer != NULL)
            return TRUE;
          else
            return FALSE;
      }
    }

    if (buf->nextBuffer)
      return TRUE;

    return FALSE;
}

/* delete current buffer and back to the previous buffer */
DEFUN(backBf, BACK, "Back to previous buffer")
{
    Buffer *buf = Currentbuf->linkBuffer[LB_N_FRAME];

    if (!checkBackBuffer(Currentbuf)) {
      if (close_tab_back && nTab >= 1) {
          deleteTab(CurrentTab);
          displayBuffer(Currentbuf, B_FORCE_REDRAW);
      }
      else
          /* FIXME: gettextize? */
          disp_message("Can't back...", TRUE);
      return;
    }

    delBuffer(Currentbuf);

    if (buf) {
      if (buf->frameQ) {
          struct frameset *fs;
          long linenumber = buf->frameQ->linenumber;
          long top = buf->frameQ->top_linenumber;
          int pos = buf->frameQ->pos;
          int currentColumn = buf->frameQ->currentColumn;
          AnchorList *formitem = buf->frameQ->formitem;

          fs = popFrameTree(&(buf->frameQ));
          deleteFrameSet(buf->frameset);
          buf->frameset = fs;

          if (buf == Currentbuf) {
            rFrame();
            Currentbuf->topLine = lineSkip(Currentbuf,
                                     Currentbuf->firstLine, top - 1,
                                     FALSE);
            gotoLine(Currentbuf, linenumber);
            Currentbuf->pos = pos;
            Currentbuf->currentColumn = currentColumn;
            arrangeCursor(Currentbuf);
            formResetBuffer(Currentbuf, formitem);
          }
      }
      else if (RenderFrame && buf == Currentbuf) {
          delBuffer(Currentbuf);
      }
    }
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

DEFUN(deletePrevBuf, DELETE_PREVBUF,
      "Delete previous buffer (mainly for local-CGI)")
{
    Buffer *buf = Currentbuf->nextBuffer;
    if (buf)
      delBuffer(buf);
}

static void
cmd_loadURL(char *url, ParsedURL *current, char *referer, FormList *request)
{
    Buffer *buf;

    if (!strncasecmp(url, "mailto:", 7)
#ifdef USE_W3MMAILER
      && non_null(Mailer) && strchr(url, '?') == NULL
#endif
      ) {
      /* invoke external mailer */
      Str to = Strnew_charp(url + 7);
#ifndef USE_W3MMAILER
      char *pos;
      if (!non_null(Mailer)) {
          /* FIXME: gettextize? */
          disp_err_message("no mailer is specified", TRUE);
          return;
      }
      if ((pos = strchr(to->ptr, '?')) != NULL)
          Strtruncate(to, pos - to->ptr);
#endif
      fmTerm();
      system(myExtCommand(Mailer, shell_quote(file_unquote(to->ptr)),
                      FALSE)->ptr);
      fmInit();
      displayBuffer(Currentbuf, B_FORCE_REDRAW);
      pushHashHist(URLHist, url);
      return;
    }
#if 0
    if (!strncasecmp(url, "news:", 5) && strchr(url, '@') == NULL) {
      /* news:newsgroup is not supported */
      /* FIXME: gettextize? */
      disp_err_message("news:newsgroup_name is not supported", TRUE);
      return;
    }
#endif                        /* USE_NNTP */

    refresh();
    buf = loadGeneralFile(url, current, referer, 0, request);
    if (buf == NULL) {
      /* FIXME: gettextize? */
      char *emsg = Sprintf("Can't load %s", conv_from_system(url))->ptr;
      disp_err_message(emsg, FALSE);
    }
    else if (buf != NO_BUFFER) {
      pushBuffer(buf);
      if (RenderFrame && Currentbuf->frameset != NULL)
          rFrame();
    }
    displayBuffer(Currentbuf, B_NORMAL);
}


/* go to specified URL */
static void
goURL0(char *prompt, int relative)
{
    char *url, *referer;
    ParsedURL p_url, *current;
    Buffer *cur_buf = Currentbuf;

    url = searchKeyData();
    if (url == NULL) {
      Hist *hist = copyHist(URLHist);
      Anchor *a;

      current = baseURL(Currentbuf);
      if (current) {
          char *c_url = parsedURL2Str(current)->ptr;
          if (DefaultURLString == DEFAULT_URL_CURRENT) {
            url = c_url;
            if (DecodeURL)
                url = url_unquote_conv(url, 0);
          }
          else
            pushHist(hist, c_url);
      }
      a = retrieveCurrentAnchor(Currentbuf);
      if (a) {
          char *a_url;
          parseURL2(a->url, &p_url, current);
          a_url = parsedURL2Str(&p_url)->ptr;
          if (DefaultURLString == DEFAULT_URL_LINK) {
            url = a_url;
            if (DecodeURL)
                url = url_unquote_conv(url, Currentbuf->document_charset);
          }
          else
            pushHist(hist, a_url);
      }
      url = inputLineHist(prompt, url, IN_URL, hist);
      if (url != NULL)
          SKIP_BLANKS(url);
    }
#ifdef USE_M17N
    if (url != NULL) {
      if ((relative || *url == '#') && Currentbuf->document_charset)
          url = wc_conv_strict(url, InnerCharset,
                         Currentbuf->document_charset)->ptr;
      else
          url = conv_to_system(url);
    }
#endif
    if (url == NULL || *url == '\0') {
      displayBuffer(Currentbuf, B_FORCE_REDRAW);
      return;
    }
    if (*url == '#') {
      gotoLabel(url + 1);
      return;
    }
    if (relative) {
      current = baseURL(Currentbuf);
      referer = parsedURL2Str(&Currentbuf->currentURL)->ptr;
    }
    else {
      current = NULL;
      referer = NULL;
    }
    parseURL2(url, &p_url, current);
    pushHashHist(URLHist, parsedURL2Str(&p_url)->ptr);
    cmd_loadURL(url, current, referer, NULL);
    if (Currentbuf != cur_buf)      /* success */
      pushHashHist(URLHist, parsedURL2Str(&Currentbuf->currentURL)->ptr);
}

DEFUN(goURL, GOTO, "Go to URL")
{
    goURL0("Goto URL: ", FALSE);
}

DEFUN(gorURL, GOTO_RELATIVE, "Go to relative URL")
{
    goURL0("Goto relative URL: ", TRUE);
}

static void
cmd_loadBuffer(Buffer *buf, int prop, int linkid)
{
    if (buf == NULL) {
      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 */
DEFUN(ldBmark, BOOKMARK VIEW_BOOKMARK, "Read bookmark")
{
    cmd_loadURL(BookmarkFile, NULL, NO_REFERER, NULL);
}


/* Add current to bookmark */
DEFUN(adBmark, ADD_BOOKMARK, "Add current page to bookmark")
{
    Str tmp;
    FormList *request;

    tmp = Sprintf("mode=panel&cookie=%s&bmark=%s&url=%s&title=%s",
              (Str_form_quote(localCookie()))->ptr,
              (Str_form_quote(Strnew_charp(BookmarkFile)))->ptr,
              (Str_form_quote(parsedURL2Str(&Currentbuf->currentURL)))->
              ptr,
#ifdef USE_M17N
#if LANG == JA
              /* FIXME: why WC_CES_EUC_JP hardcoded? 
               *  backward compatibility.
               *  w3mbookmark takes arguments as EUC-JP only?
               */
              (Str_form_quote(wc_conv_strict(Currentbuf->buffername,
                                     InnerCharset,
                                     WC_CES_EUC_JP)))->ptr);
#else
              (Str_form_quote(wc_conv_strict(Currentbuf->buffername,
                                     InnerCharset,
                                     SystemCharset)))->ptr);
#endif
#else
              (Str_form_quote(Strnew_charp(Currentbuf->buffername)))->ptr);
#endif
    request = newFormList(NULL, "post", NULL, NULL, NULL, NULL, NULL);
    request->body = tmp->ptr;
    request->length = tmp->length;
    cmd_loadURL("file:///$LIB/" W3MBOOKMARK_CMDNAME, NULL, NO_REFERER,
            request);
}

/* option setting */
DEFUN(ldOpt, OPTIONS, "Option setting panel")
{
    cmd_loadBuffer(load_option_panel(), BP_NO_URL, LB_NOLINK);
}

/* set an option */
DEFUN(setOpt, SET_OPTION, "Set option")
{
    char *opt;

    CurrentKeyData = NULL;    /* not allowed in w3m-control: */
    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 = inputStrHist("Set option: ", opt, TextHist);
      if (opt == NULL || *opt == '\0') {
          displayBuffer(Currentbuf, B_NORMAL);
          return;
      }
    }
    if (set_param_option(opt))
      sync_with_option();
    displayBuffer(Currentbuf, B_REDRAW_IMAGE);
}

/* error message list */
DEFUN(msgs, MSGS, "Display error messages")
{
    cmd_loadBuffer(message_list_panel(), BP_NO_URL, LB_NOLINK);
}

/* page info */
DEFUN(pginfo, INFO, "View info of current document")
{
    Buffer *buf;

    if ((buf = Currentbuf->linkBuffer[LB_N_INFO]) != NULL) {
      Currentbuf = buf;
      displayBuffer(Currentbuf, B_NORMAL);
      return;
    }
    if ((buf = Currentbuf->linkBuffer[LB_INFO]) != NULL)
      delBuffer(buf);
    buf = page_info_panel(Currentbuf);
    cmd_loadBuffer(buf, BP_NORMAL, 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->rootX;
    y = Currentbuf->cursorY + Currentbuf->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)
          cmd_loadBuffer(buf, BP_NORMAL, LB_NOLINK);
#endif
#if defined(MENU_MAP) || defined(USE_IMAGE)
      return;
    }
    if (*(a->url) == '#') {
      gotoLabel(a->url + 1);
      return;
    }
    parseURL2(a->url, &p_url, baseURL(Currentbuf));
    pushHashHist(URLHist, parsedURL2Str(&p_url)->ptr);
    if (check_target && open_tab_blank && a->target &&
      (!strcasecmp(a->target, "_new") || !strcasecmp(a->target, "_blank"))) {
      Buffer *buf;

      _newT();
      buf = Currentbuf;
      cmd_loadURL(a->url, baseURL(Currentbuf),
                parsedURL2Str(&Currentbuf->currentURL)->ptr, NULL);
      if (buf != Currentbuf)
          delBuffer(buf);
      else
          deleteTab(CurrentTab);
      displayBuffer(Currentbuf, B_FORCE_REDRAW);
      return;
    }
    cmd_loadURL(a->url, baseURL(Currentbuf),
            parsedURL2Str(&Currentbuf->currentURL)->ptr, NULL);
#endif
}

#ifdef USE_MENU
/* link menu */
DEFUN(linkMn, LINK_MENU, "Popup link element menu")
{
    LinkList *l = link_menu(Currentbuf);
    ParsedURL p_url;

    if (!l || !l->url)
      return;
    if (*(l->url) == '#') {
      gotoLabel(l->url + 1);
      return;
    }
    parseURL2(l->url, &p_url, baseURL(Currentbuf));
    pushHashHist(URLHist, parsedURL2Str(&p_url)->ptr);
    cmd_loadURL(l->url, baseURL(Currentbuf),
            parsedURL2Str(&Currentbuf->currentURL)->ptr, NULL);
}

static void
anchorMn(Anchor *(*menu_func) (Buffer *), int go)
{
    Anchor *a;
    BufferPoint *po;

    if (!Currentbuf->href || !Currentbuf->hmarklist)
      return;
    a = menu_func(Currentbuf);
    if (!a || a->hseq < 0)
      return;
    po = &Currentbuf->hmarklist->marks[a->hseq];
    gotoLine(Currentbuf, po->line);
    Currentbuf->pos = po->pos;
    arrangeCursor(Currentbuf);
    displayBuffer(Currentbuf, B_NORMAL);
    if (go)
      followA();
}

/* accesskey */
DEFUN(accessKey, ACCESSKEY, "Popup acceskey menu")
{
    anchorMn(accesskey_menu, TRUE);
}

/* list menu */
DEFUN(listMn, LIST_MENU, "Popup link list menu and go to selected link")
{
    anchorMn(list_menu, TRUE);
}

DEFUN(movlistMn, MOVE_LIST_MENU,
      "Popup link list menu and move cursor to selected link")
{
    anchorMn(list_menu, FALSE);
}
#endif

/* link,anchor,image list */
DEFUN(linkLst, LIST, "Show all links and images")
{
    Buffer *buf;

    buf = link_list_panel(Currentbuf);
    if (buf != NULL) {
#ifdef USE_M17N
      buf->document_charset = Currentbuf->document_charset;
#endif
      cmd_loadBuffer(buf, BP_NORMAL, LB_NOLINK);
    }
}

#ifdef USE_COOKIE
/* cookie list */
DEFUN(cooLst, COOKIE, "View cookie list")
{
    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 */
DEFUN(ldHist, HISTORY, "View history of URL")
{
    cmd_loadBuffer(historyBuffer(URLHist), BP_NO_URL, LB_NOLINK);
}
#endif                        /* USE_HISTORY */

/* download HREF link */
DEFUN(svA, SAVE_LINK, "Save link to file")
{
    CurrentKeyData = NULL;    /* not allowed in w3m-control: */
    do_download = TRUE;
    followA();
    do_download = FALSE;
}

/* download IMG link */
DEFUN(svI, SAVE_IMAGE, "Save image to file")
{
    CurrentKeyData = NULL;    /* not allowed in w3m-control: */
    do_download = TRUE;
    followI();
    do_download = FALSE;
}

/* save buffer */
DEFUN(svBuf, PRINT SAVE_SCREEN, "Save rendered document to file")
{
    char *qfile = NULL, *file;
    FILE *f;
    int is_pipe;

    CurrentKeyData = NULL;    /* not allowed in w3m-control: */
    file = searchKeyData();
    if (file == NULL || *file == '\0') {
      /* FIXME: gettextize? */
      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 = expandPath(file);
      if (checkOverWrite(file) < 0) {
          displayBuffer(Currentbuf, B_NORMAL);
          return;
      }
      f = fopen(file, "w");
      is_pipe = FALSE;
    }
    if (f == NULL) {
      /* FIXME: gettextize? */
      char *emsg = Sprintf("Can't open %s", conv_from_system(file))->ptr;
      disp_err_message(emsg, TRUE);
      return;
    }
    saveBuffer(Currentbuf, f, TRUE);
    if (is_pipe)
      pclose(f);
    else
      fclose(f);
    displayBuffer(Currentbuf, B_NORMAL);
}

/* save source */
DEFUN(svSrc, DOWNLOAD SAVE, "Save document source to file")
{
    char *file;

    if (Currentbuf->sourcefile == NULL)
      return;
    CurrentKeyData = NULL;    /* not allowed in w3m-control: */
    PermitSaveToPipe = TRUE;
    if (Currentbuf->real_scheme == SCM_LOCAL)
      file = conv_from_system(guess_save_name(NULL,
                                    Currentbuf->currentURL.
                                    real_file));
    else
      file = guess_save_name(Currentbuf, Currentbuf->currentURL.file);
    doFileCopy(Currentbuf->sourcefile, file);
    PermitSaveToPipe = FALSE;
    displayBuffer(Currentbuf, B_NORMAL);
}

static void
_peekURL(int only_img)
{

    Anchor *a;
    ParsedURL pu;
    static Str s = NULL;
#ifdef USE_M17N
    static Lineprop *p = NULL;
    Lineprop *pp;
#endif
    static int offset = 0, n;

    if (Currentbuf->firstLine == NULL)
      return;
    if (CurrentKey == prev_key && s != NULL) {
      if (s->length - offset >= COLS)
          offset++;
      else if (s->length <= offset) /* bug ? */
          offset = 0;
      goto disp;
    }
    else {
      offset = 0;
    }
    s = NULL;
    a = (only_img ? NULL : retrieveCurrentAnchor(Currentbuf));
    if (a == NULL) {
      a = (only_img ? NULL : retrieveCurrentForm(Currentbuf));
      if (a == NULL) {
          a = retrieveCurrentImg(Currentbuf);
          if (a == NULL)
            return;
      }
      else
          s = Strnew_charp(form2str((FormItemList *)a->url));
    }
    if (s == NULL) {
      parseURL2(a->url, &pu, baseURL(Currentbuf));
      s = parsedURL2Str(&pu);
    }
    if (DecodeURL)
      s = Strnew_charp(url_unquote_conv
                   (s->ptr, Currentbuf->document_charset));
#ifdef USE_M17N
    s = checkType(s, &pp, NULL);
    p = NewAtom_N(Lineprop, s->length);
    bcopy((void *)pp, (void *)p, s->length * sizeof(Lineprop));
#endif
  disp:
    n = searchKeyNum();
    if (n > 1 && s->length > (n - 1) * (COLS - 1))
      offset = (n - 1) * (COLS - 1);
#ifdef USE_M17N
    while (offset < s->length && p[offset] & PC_WCHAR2)
      offset++;
#endif
    disp_message_nomouse(&s->ptr[offset], TRUE);
}

/* peek URL */
DEFUN(peekURL, PEEK_LINK, "Peek link URL")
{
    _peekURL(0);
}

/* peek URL of image */
DEFUN(peekIMG, PEEK_IMG, "Peek image URL")
{
    _peekURL(1);
}

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

DEFUN(curURL, PEEK, "Peek current URL")
{
    static Str s = NULL;
#ifdef USE_M17N
    static Lineprop *p = NULL;
    Lineprop *pp;
#endif
    static int offset = 0, n;

    if (Currentbuf->bufferprop & BP_INTERNAL)
      return;
    if (CurrentKey == prev_key && s != NULL) {
      if (s->length - offset >= COLS)
          offset++;
      else if (s->length <= offset) /* bug ? */
          offset = 0;
    }
    else {
      offset = 0;
      s = currentURL();
      if (DecodeURL)
          s = Strnew_charp(url_unquote_conv(s->ptr, 0));
#ifdef USE_M17N
      s = checkType(s, &pp, NULL);
      p = NewAtom_N(Lineprop, s->length);
      bcopy((void *)pp, (void *)p, s->length * sizeof(Lineprop));
#endif
    }
    n = searchKeyNum();
    if (n > 1 && s->length > (n - 1) * (COLS - 1))
      offset = (n - 1) * (COLS - 1);
#ifdef USE_M17N
    while (offset < s->length && p[offset] & PC_WCHAR2)
      offset++;
#endif
    disp_message_nomouse(&s->ptr[offset], TRUE);
}
/* view HTML source */

DEFUN(vwSrc, SOURCE VIEW, "View HTML source")
{
    Buffer *buf;

    if (Currentbuf->type == NULL || Currentbuf->bufferprop & BP_FRAME)
      return;
    if ((buf = Currentbuf->linkBuffer[LB_SOURCE]) != NULL ||
      (buf = Currentbuf->linkBuffer[LB_N_SOURCE]) != NULL) {
      Currentbuf = buf;
      displayBuffer(Currentbuf, B_NORMAL);
      return;
    }
    if (Currentbuf->sourcefile == NULL) {
      if (Currentbuf->pagerSource &&
          !strcasecmp(Currentbuf->type, "text/plain")) {
#ifdef USE_M17N
          wc_ces old_charset;
          wc_bool old_fix_width_conv;
#endif
          FILE *f;
          Str tmpf = tmpfname(TMPF_SRC, NULL);
          f = fopen(tmpf->ptr, "w");
          if (f == NULL)
            return;
#ifdef USE_M17N
          old_charset = DisplayCharset;
          old_fix_width_conv = WcOption.fix_width_conv;
          DisplayCharset = (Currentbuf->document_charset != WC_CES_US_ASCII)
            ? Currentbuf->document_charset : 0;
          WcOption.fix_width_conv = WC_FALSE;
#endif
          saveBufferBody(Currentbuf, f, TRUE);
#ifdef USE_M17N
          DisplayCharset = old_charset;
          WcOption.fix_width_conv = old_fix_width_conv;
#endif
          fclose(f);
          Currentbuf->sourcefile = tmpf->ptr;
      }
      else {
          return;
      }
    }

    buf = newBuffer(INIT_BUFFER_WIDTH);

    if (!strcasecmp(Currentbuf->type, "text/html")) {
      buf->type = "text/plain";
      if (Currentbuf->real_type &&
          !strcasecmp(Currentbuf->real_type, "text/html"))
          buf->real_type = "text/plain";
      else
          buf->real_type = Currentbuf->real_type;
      buf->buffername = Sprintf("source of %s", Currentbuf->buffername)->ptr;
      buf->linkBuffer[LB_N_SOURCE] = Currentbuf;
      Currentbuf->linkBuffer[LB_SOURCE] = buf;
    }
    else if (!strcasecmp(Currentbuf->type, "text/plain")) {
      buf->type = "text/html";
      if (Currentbuf->real_type &&
          !strcasecmp(Currentbuf->real_type, "text/plain"))
          buf->real_type = "text/html";
      else
          buf->real_type = Currentbuf->real_type;
      buf->buffername = Sprintf("HTML view of %s",
                          Currentbuf->buffername)->ptr;
      buf->linkBuffer[LB_SOURCE] = Currentbuf;
      Currentbuf->linkBuffer[LB_N_SOURCE] = buf;
    }
    else {
      return;
    }
    buf->currentURL = Currentbuf->currentURL;
    buf->real_scheme = Currentbuf->real_scheme;
    buf->filename = Currentbuf->filename;
    buf->sourcefile = Currentbuf->sourcefile;
    buf->header_source = Currentbuf->header_source;
    buf->search_header = Currentbuf->search_header;
#ifdef USE_M17N
    buf->document_charset = Currentbuf->document_charset;
#endif
    buf->clone = Currentbuf->clone;
    (*buf->clone)++;

    buf->need_reshape = TRUE;
    reshapeBuffer(buf);
    pushBuffer(buf);
    displayBuffer(Currentbuf, B_NORMAL);
}

/* reload */
DEFUN(reload, RELOAD, "Reload buffer")
{
    Buffer *buf, *fbuf = NULL, sbuf;
#ifdef USE_M17N
    wc_ces old_charset;
#endif
    Str url;
    FormList *request;
    int multipart;

    if (Currentbuf->bufferprop & BP_INTERNAL) {
      if (!strcmp(Currentbuf->buffername, DOWNLOAD_LIST_TITLE)) {
          ldDL();
          return;
      }
      /* FIXME: gettextize? */
      disp_err_message("Can't reload...", TRUE);
      return;
    }
    if (Currentbuf->currentURL.scheme == SCM_LOCAL &&
      !strcmp(Currentbuf->currentURL.file, "-")) {
      /* file is std input */
      /* FIXME: gettextize? */
      disp_err_message("Can't reload stdin", TRUE);
      return;
    }
    copyBuffer(&sbuf, Currentbuf);
    if (Currentbuf->bufferprop & BP_FRAME &&
      (fbuf = Currentbuf->linkBuffer[LB_N_FRAME])) {
      if (fmInitialized) {
          message("Rendering frame", 0, 0);
          refresh();
      }
      if (!(buf = renderFrame(fbuf, 1))) {
          displayBuffer(Currentbuf, B_NORMAL);
          return;
      }
      if (fbuf->linkBuffer[LB_FRAME]) {
          if (buf->sourcefile &&
            fbuf->linkBuffer[LB_FRAME]->sourcefile &&
            !strcmp(buf->sourcefile,
                  fbuf->linkBuffer[LB_FRAME]->sourcefile))
            fbuf->linkBuffer[LB_FRAME]->sourcefile = NULL;
          delBuffer(fbuf->linkBuffer[LB_FRAME]);
      }
      fbuf->linkBuffer[LB_FRAME] = buf;
      buf->linkBuffer[LB_N_FRAME] = fbuf;
      pushBuffer(buf);
      Currentbuf = buf;
      if (Currentbuf->firstLine)
          restorePosition(Currentbuf, &sbuf);
      displayBuffer(Currentbuf, B_FORCE_REDRAW);
      return;
    }
    else if (Currentbuf->frameset != NULL)
      fbuf = Currentbuf->linkBuffer[LB_FRAME];
    multipart = 0;
    if (Currentbuf->form_submit) {
      request = Currentbuf->form_submit->parent;
      if (request->method == FORM_METHOD_POST
          && request->enctype == FORM_ENCTYPE_MULTIPART) {
          Str query;
          struct stat st;
          multipart = 1;
          query_from_followform(&query, Currentbuf->form_submit, multipart);
          stat(request->body, &st);
          request->length = st.st_size;
      }
    }
    else {
      request = NULL;
    }
    url = parsedURL2Str(&Currentbuf->currentURL);
    /* FIXME: gettextize? */
    message("Reloading...", 0, 0);
    refresh();
#ifdef USE_M17N
    old_charset = DocumentCharset;
    if (Currentbuf->document_charset != WC_CES_US_ASCII)
      DocumentCharset = Currentbuf->document_charset;
#endif
    SearchHeader = Currentbuf->search_header;
    DefaultType = Currentbuf->real_type;
    buf = loadGeneralFile(url->ptr, NULL, NO_REFERER, RG_NOCACHE, request);
#ifdef USE_M17N
    DocumentCharset = old_charset;
#endif
    SearchHeader = FALSE;
    DefaultType = NULL;

    if (multipart)
      unlink(request->body);
    if (buf == NULL) {
      /* FIXME: gettextize? */
      disp_err_message("Can't reload...", TRUE);
      return;
    }
    else if (buf == NO_BUFFER) {
      displayBuffer(Currentbuf, B_NORMAL);
      return;
    }
    if (fbuf != NULL)
      Firstbuf = deleteBuffer(Firstbuf, fbuf);
    repBuffer(Currentbuf, buf);
    if ((buf->type != NULL) && (sbuf.type != NULL) &&
      ((!strcasecmp(buf->type, "text/plain") &&
        !strcasecmp(sbuf.type, "text/html")) ||
       (!strcasecmp(buf->type, "text/html") &&
        !strcasecmp(sbuf.type, "text/plain")))) {
      vwSrc();
      if (Currentbuf != buf)
          Firstbuf = deleteBuffer(Firstbuf, buf);
    }
    Currentbuf->search_header = sbuf.search_header;
    Currentbuf->form_submit = sbuf.form_submit;
    if (Currentbuf->firstLine)
      restorePosition(Currentbuf, &sbuf);
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

/* reshape */
DEFUN(reshape, RESHAPE, "Re-render buffer")
{
    Currentbuf->need_reshape = TRUE;
    reshapeBuffer(Currentbuf);
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

#ifdef USE_M17N
static void
_docCSet(wc_ces charset)
{
    if (Currentbuf->bufferprop & BP_INTERNAL)
      return;
    if (Currentbuf->sourcefile == NULL) {
      disp_message("Can't reload...", FALSE);
      return;
    }
    Currentbuf->document_charset = charset;
    Currentbuf->need_reshape = TRUE;
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

void
change_charset(struct parsed_tagarg *arg)
{
    Buffer *buf = Currentbuf->linkBuffer[LB_N_INFO];
    wc_ces charset;

    if (buf == NULL)
      return;
    delBuffer(Currentbuf);
    Currentbuf = buf;
    if (Currentbuf->bufferprop & BP_INTERNAL)
      return;
    charset = Currentbuf->document_charset;
    for (; arg; arg = arg->next) {
      if (!strcmp(arg->arg, "charset"))
          charset = atoi(arg->value);
    }
    _docCSet(charset);
}

DEFUN(docCSet, CHARSET, "Change the current document charset")
{
    char *cs;
    wc_ces charset;

    cs = searchKeyData();
    if (cs == NULL || *cs == '\0')
      /* FIXME: gettextize? */
      cs = inputStr("Document charset: ",
                  wc_ces_to_charset(Currentbuf->document_charset));
    charset = wc_guess_charset_short(cs, 0);
    if (charset == 0) {
      displayBuffer(Currentbuf, B_NORMAL);
      return;
    }
    _docCSet(charset);
}

DEFUN(defCSet, DEFAULT_CHARSET, "Change the default document charset")
{
    char *cs;
    wc_ces charset;

    cs = searchKeyData();
    if (cs == NULL || *cs == '\0')
      /* FIXME: gettextize? */
      cs = inputStr("Default document charset: ",
                  wc_ces_to_charset(DocumentCharset));
    charset = wc_guess_charset_short(cs, 0);
    if (charset != 0)
      DocumentCharset = charset;
    displayBuffer(Currentbuf, B_NORMAL);
}
#endif

/* mark URL-like patterns as anchors */
void
chkURLBuffer(Buffer *buf)
{
    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 */
#ifndef USE_W3MMAILER         /* see also chkExternalURIBuffer() */
      "mailto:[^<>      ][^<>       ]*@[a-zA-Z0-9][a-zA-Z0-9\\-\\._]*[a-zA-Z0-9]",
#endif
#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;
    for (i = 0; url_like_pat[i]; i++) {
      reAnchor(buf, url_like_pat[i]);
    }
#ifdef USE_EXTERNAL_URI_LOADER
    chkExternalURIBuffer(buf);
#endif
    buf->check_url |= CHK_URL;
}

DEFUN(chkURL, MARK_URL, "Mark URL-like strings as anchors")
{
    chkURLBuffer(Currentbuf);
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

DEFUN(chkWORD, MARK_WORD, "Mark current word as anchor")
{
    char *p;
    int spos, epos;
    p = getCurWord(Currentbuf, &spos, &epos, ":\"\'`<>()[]{}&|;*?$");
    if (p == NULL)
      return;
    reAnchorWord(Currentbuf, Currentbuf->currentLine, spos, epos);
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

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

DEFUN(chkNMID, MARK_MID, "Mark Message-ID-like strings as anchors")
{
    chkNMIDBuffer(Currentbuf);
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}
#endif                        /* USE_NNTP */

/* render frame */
DEFUN(rFrame, FRAME, "Render frame")
{
    Buffer *buf;

    if ((buf = Currentbuf->linkBuffer[LB_FRAME]) != NULL) {
      Currentbuf = buf;
      displayBuffer(Currentbuf, B_NORMAL);
      return;
    }
    if (Currentbuf->frameset == NULL) {
      if ((buf = Currentbuf->linkBuffer[LB_N_FRAME]) != NULL) {
          Currentbuf = buf;
          displayBuffer(Currentbuf, B_NORMAL);
      }
      return;
    }
    if (fmInitialized) {
      message("Rendering frame", 0, 0);
      refresh();
    }
    buf = renderFrame(Currentbuf, 0);
    if (buf == NULL) {
      displayBuffer(Currentbuf, B_NORMAL);
      return;
    }
    buf->linkBuffer[LB_N_FRAME] = Currentbuf;
    Currentbuf->linkBuffer[LB_FRAME] = buf;
    pushBuffer(buf);
    if (fmInitialized && display_ok)
      displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

/* 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: */
    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 = conv_to_system(browser);
      }
    }
    else {
      browser = conv_to_system(browser);
    }
    if (browser == NULL || *browser == '\0') {
      displayBuffer(Currentbuf, B_NORMAL);
      return;
    }

    if ((len = strlen(browser)) >= 2 && browser[len - 1] == '&' &&
      browser[len - 2] != '\\') {
      browser = allocStr(browser, len - 2);
      bg = 1;
    }
    cmd = myExtCommand(browser, shell_quote(url), FALSE);
    Strremovetrailingspaces(cmd);
    fmTerm();
    mySystem(cmd->ptr, bg);
    fmInit();
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

DEFUN(extbrz, EXTERN, "Execute external browser")
{
    if (Currentbuf->bufferprop & BP_INTERNAL) {
      /* FIXME: gettextize? */
      disp_err_message("Can't browse...", TRUE);
      return;
    }
    if (Currentbuf->currentURL.scheme == SCM_LOCAL &&
      !strcmp(Currentbuf->currentURL.file, "-")) {
      /* file is std input */
      /* FIXME: gettextize? */
      disp_err_message("Can't browse stdin", TRUE);
      return;
    }
    invoke_browser(parsedURL2Str(&Currentbuf->currentURL)->ptr);
}

DEFUN(linkbrz, EXTERN_LINK, "View current link using external browser")
{
    Anchor *a;
    ParsedURL pu;

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

/* show current line number and number of lines in the entire document */
DEFUN(curlno, LINE_INFO, "Show current line number")
{
    Line *l = Currentbuf->currentLine;
    Str tmp;
    int cur = 0, all = 0, col = 0, len = 0;

    if (l != NULL) {
      cur = l->real_linenumber;
      col = l->bwidth + Currentbuf->currentColumn + Currentbuf->cursorX + 1;
      while (l->next && l->next->bpos)
          l = l->next;
      if (l->width < 0)
          l->width = COLPOS(l, l->len);
      len = l->bwidth + l->width;
    }
    if (Currentbuf->lastLine)
      all = Currentbuf->lastLine->real_linenumber;
    if (Currentbuf->pagerSource && !(Currentbuf->bufferprop & BP_CLOSE))
      tmp = Sprintf("line %d col %d/%d", cur, col, len);
    else
      tmp = Sprintf("line %d/%d (%d%%) col %d/%d", cur, all,
                  (int)((double)cur * 100.0 / (double)(all ? all : 1)
                      + 0.5), col, len);
#ifdef USE_M17N
    Strcat_charp(tmp, "  ");
    Strcat_charp(tmp, wc_ces_to_charset_desc(Currentbuf->document_charset));
#endif

    disp_message(tmp->ptr, FALSE);
}

#ifdef USE_IMAGE
DEFUN(dispI, DISPLAY_IMAGE, "Restart loading and drawing of images")
{
    if (!displayImage)
      initImage();
    if (!activeImage)
      return;
    displayImage = TRUE;
    /*
     * if (!(Currentbuf->type && !strcmp(Currentbuf->type, "text/html")))
     * return;
     */
    Currentbuf->image_flag = IMG_FLAG_AUTO;
    Currentbuf->need_reshape = TRUE;
    displayBuffer(Currentbuf, B_REDRAW_IMAGE);
}

DEFUN(stopI, STOP_IMAGE, "Stop loading and drawing of images")
{
    if (!activeImage)
      return;
    /*
     * if (!(Currentbuf->type && !strcmp(Currentbuf->type, "text/html")))
     * return;
     */
    Currentbuf->image_flag = IMG_FLAG_SKIP;
    displayBuffer(Currentbuf, B_REDRAW_IMAGE);
}
#endif

#ifdef USE_MOUSE

static int
mouse_scroll_line(void)
{
    if (relative_wheel_scroll)
      return (relative_wheel_scroll_ratio * LASTLINE + 99) / 100;
    else
      return fixed_wheel_scroll_count;
}

static TabBuffer *
posTab(int x, int y)
{
    TabBuffer *tab;

    if (mouse_action.menu_str && x < mouse_action.menu_width && y == 0)
      return NO_TABBUFFER;
    if (y > LastTab->y)
      return NULL;
    for (tab = FirstTab; tab; tab = tab->nextTab) {
      if (tab->x1 <= x && x <= tab->x2 && tab->y == y)
          return tab;
    }
    return NULL;
}

static void
do_mouse_action(int btn, int x, int y)
{
    MouseActionMap *map = NULL;
    int ny = -1;

    if (nTab > 1 || mouse_action.menu_str)
      ny = LastTab->y + 1;

    switch (btn) {
    case MOUSE_BTN1_DOWN:
      btn = 0;
      break;
    case MOUSE_BTN2_DOWN:
      btn = 1;
      break;
    case MOUSE_BTN3_DOWN:
      btn = 2;
      break;
    default:
      return;
    }
    if (y < ny) {
      if (mouse_action.menu_str && x >= 0 && x < mouse_action.menu_width) {
          if (mouse_action.menu_map[btn])
            map = &mouse_action.menu_map[btn][x];
      }
      else
          map = &mouse_action.tab_map[btn];
    }
    else if (y == LASTLINE) {
      if (mouse_action.lastline_str && x >= 0 &&
          x < mouse_action.lastline_width) {
          if (mouse_action.lastline_map[btn])
            map = &mouse_action.lastline_map[btn][x];
      }
    }
    else if (y > ny) {
      if (y == Currentbuf->cursorY + Currentbuf->rootY &&
          (x == Currentbuf->cursorX + Currentbuf->rootX
#ifdef USE_M17N
           || (WcOption.use_wide && Currentbuf->currentLine != NULL &&
             (CharType(Currentbuf->currentLine->propBuf[Currentbuf->pos])
              == PC_KANJI1)
             && x == Currentbuf->cursorX + Currentbuf->rootX + 1)
#endif
          )) {
          if (retrieveCurrentAnchor(Currentbuf) ||
            retrieveCurrentForm(Currentbuf)) {
            map = &mouse_action.active_map[btn];
            if (!(map && map->func))
                map = &mouse_action.anchor_map[btn];
          }
      }
      else {
          int cx = Currentbuf->cursorX, cy = Currentbuf->cursorY;
          cursorXY(Currentbuf, x - Currentbuf->rootX, y - Currentbuf->rootY);
          if (y == Currentbuf->cursorY + Currentbuf->rootY &&
            (x == Currentbuf->cursorX + Currentbuf->rootX
#ifdef USE_M17N
             || (WcOption.use_wide && Currentbuf->currentLine != NULL &&
                 (CharType(Currentbuf->currentLine->
                         propBuf[Currentbuf->pos]) == PC_KANJI1)
                 && x == Currentbuf->cursorX + Currentbuf->rootX + 1)
#endif
            ) &&
            (retrieveCurrentAnchor(Currentbuf) ||
             retrieveCurrentForm(Currentbuf)))
            map = &mouse_action.anchor_map[btn];
          cursorXY(Currentbuf, cx, cy);
      }
    }
    if (!(map && map->func))
      map = &mouse_action.default_map[btn];
    if (map && map->func) {
      mouse_action.in_action = TRUE;
      mouse_action.cursorX = x;
      mouse_action.cursorY = y;
      CurrentKey = -1;
      CurrentKeyData = NULL;
      CurrentCmdData = map->data;
      (*map->func) ();
      CurrentCmdData = NULL;
    }
}

static void
process_mouse(int btn, int x, int y)
{
    int delta_x, delta_y, i;
    static int press_btn = MOUSE_BTN_RESET, press_x, press_y;
    TabBuffer *t;
    int ny = -1;

    if (nTab > 1 || mouse_action.menu_str)
      ny = LastTab->y + 1;
    if (btn == MOUSE_BTN_UP) {
      switch (press_btn) {
      case MOUSE_BTN1_DOWN:
          if (press_y == y && press_x == x)
            do_mouse_action(press_btn, x, y);
          else if (ny > 0 && y < ny) {
            if (press_y < ny) {
                moveTab(posTab(press_x, press_y), posTab(x, y),
                      (press_y == y) ? (press_x < x) : (press_y < y));
                return;
            }
            else if (press_x >= Currentbuf->rootX) {
                Buffer *buf = Currentbuf;
                int cx = Currentbuf->cursorX, cy = Currentbuf->cursorY;

                t = posTab(x, y);
                if (t == NULL)
                  return;
                if (t == NO_TABBUFFER)
                  t = NULL;   /* open new tab */
                cursorXY(Currentbuf, press_x - Currentbuf->rootX,
                       press_y - Currentbuf->rootY);
                if (Currentbuf->cursorY == press_y - Currentbuf->rootY &&
                  (Currentbuf->cursorX == press_x - Currentbuf->rootX
#ifdef USE_M17N
                   || (WcOption.use_wide &&
                       Currentbuf->currentLine != NULL &&
                       (CharType(Currentbuf->currentLine->
                               propBuf[Currentbuf->pos]) == PC_KANJI1)
                       && Currentbuf->cursorX == press_x
                       - Currentbuf->rootX - 1)
#endif
                  )) {
                  displayBuffer(Currentbuf, B_NORMAL);
                  followTab(t);
                }
                if (buf == Currentbuf)
                  cursorXY(Currentbuf, cx, cy);
            }
            return;
          }
          else {
            delta_x = x - press_x;
            delta_y = y - press_y;

            if (abs(delta_x) < abs(delta_y) / 3)
                delta_x = 0;
            if (abs(delta_y) < abs(delta_x) / 3)
                delta_y = 0;
            if (reverse_mouse) {
                delta_y = -delta_y;
                delta_x = -delta_x;
            }
            if (delta_y > 0) {
                prec_num = delta_y;
                ldown1();
            }
            else if (delta_y < 0) {
                prec_num = -delta_y;
                lup1();
            }
            if (delta_x > 0) {
                prec_num = delta_x;
                col1L();
            }
            else if (delta_x < 0) {
                prec_num = -delta_x;
                col1R();
            }
          }
          break;
      case MOUSE_BTN2_DOWN:
      case MOUSE_BTN3_DOWN:
          if (press_y == y && press_x == x)
            do_mouse_action(press_btn, x, y);
          break;
      case MOUSE_BTN4_DOWN_RXVT:
          for (i = 0; i < mouse_scroll_line(); i++)
            ldown1();
          break;
      case MOUSE_BTN5_DOWN_RXVT:
          for (i = 0; i < mouse_scroll_line(); i++)
            lup1();
          break;
      }
    }
    else if (btn == MOUSE_BTN4_DOWN_XTERM) {
      for (i = 0; i < mouse_scroll_line(); i++)
          ldown1();
    }
    else if (btn == MOUSE_BTN5_DOWN_XTERM) {
      for (i = 0; i < mouse_scroll_line(); i++)
          lup1();
    }

    if (btn != MOUSE_BTN4_DOWN_RXVT || press_btn == MOUSE_BTN_RESET) {
      press_btn = btn;
      press_x = x;
      press_y = y;
    }
    else {
      press_btn = MOUSE_BTN_RESET;
    }
}

DEFUN(msToggle, MOUSE_TOGGLE, "Toggle activity of mouse")
{
    if (use_mouse) {
      use_mouse = FALSE;
    }
    else {
      use_mouse = TRUE;
    }
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

DEFUN(mouse, MOUSE, "mouse operation")
{
    int btn, x, y;

    btn = (unsigned char)getch() - 32;
#if defined(__CYGWIN__)
    if (cygwin_mouse_btn_swapped) {
      if (btn == MOUSE_BTN2_DOWN)
          btn = MOUSE_BTN3_DOWN;
      else if (btn == MOUSE_BTN3_DOWN)
          btn = MOUSE_BTN2_DOWN;
    }
#endif
    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 > LASTLINE)
      return;
    process_mouse(btn, x, y);
}

#ifdef USE_GPM
int
gpm_process_mouse(Gpm_Event * event, void *data)
{
    int btn = MOUSE_BTN_RESET, x, y;
    if (event->type & GPM_UP)
      btn = MOUSE_BTN_UP;
    else if (event->type & GPM_DOWN) {
      switch (event->buttons) {
      case GPM_B_LEFT:
          btn = MOUSE_BTN1_DOWN;
          break;
      case GPM_B_MIDDLE:
          btn = MOUSE_BTN2_DOWN;
          break;
      case GPM_B_RIGHT:
          btn = MOUSE_BTN3_DOWN;
          break;
      }
    }
    else {
      GPM_DRAWPOINTER(event);
      return 0;
    }
    x = event->x;
    y = event->y;
    process_mouse(btn, x - 1, y - 1);
    return 0;
}
#endif                        /* USE_GPM */

#ifdef USE_SYSMOUSE
int
sysm_process_mouse(int x, int y, int nbs, int obs)
{
    int btn;
    int bits;

    if (obs & ~nbs)
      btn = MOUSE_BTN_UP;
    else if (nbs & ~obs) {
      bits = nbs & ~obs;
      btn = bits & 0x1 ? MOUSE_BTN1_DOWN :
          (bits & 0x2 ? MOUSE_BTN2_DOWN :
           (bits & 0x4 ? MOUSE_BTN3_DOWN : 0));
    }
    else                /* nbs == obs */
      return 0;
    process_mouse(btn, x, y);
    return 0;
}
#endif                        /* USE_SYSMOUSE */

DEFUN(movMs, MOVE_MOUSE, "Move cursor to mouse cursor (for mouse action)")
{
    if (!mouse_action.in_action)
      return;
    if ((nTab > 1 || mouse_action.menu_str) &&
      mouse_action.cursorY < LastTab->y + 1)
      return;
    else if (mouse_action.cursorX >= Currentbuf->rootX &&
           mouse_action.cursorY < LASTLINE) {
      cursorXY(Currentbuf, mouse_action.cursorX - Currentbuf->rootX,
             mouse_action.cursorY - Currentbuf->rootY);
    }
    displayBuffer(Currentbuf, B_NORMAL);
}

#ifdef USE_MENU
#ifdef KANJI_SYMBOLS
#define FRAME_WIDTH 2
#else
#define FRAME_WIDTH 1
#endif

DEFUN(menuMs, MENU_MOUSE, "Popup menu at mouse cursor (for mouse action)")
{
    if (!mouse_action.in_action)
      return;
    if ((nTab > 1 || mouse_action.menu_str) &&
      mouse_action.cursorY < LastTab->y + 1)
      mouse_action.cursorX -= FRAME_WIDTH + 1;
    else if (mouse_action.cursorX >= Currentbuf->rootX &&
           mouse_action.cursorY < LASTLINE) {
      cursorXY(Currentbuf, mouse_action.cursorX - Currentbuf->rootX,
             mouse_action.cursorY - Currentbuf->rootY);
      displayBuffer(Currentbuf, B_NORMAL);
    }
    mainMn();
}
#endif

DEFUN(tabMs, TAB_MOUSE, "Move to tab on mouse cursor (for mouse action)")
{
    TabBuffer *tab;

    if (!mouse_action.in_action)
      return;
    tab = posTab(mouse_action.cursorX, mouse_action.cursorY);
    if (!tab || tab == NO_TABBUFFER)
      return;
    CurrentTab = tab;
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

DEFUN(closeTMs, CLOSE_TAB_MOUSE,
      "Close tab on mouse cursor (for mouse action)")
{
    TabBuffer *tab;

    if (!mouse_action.in_action)
      return;
    tab = posTab(mouse_action.cursorX, mouse_action.cursorY);
    if (!tab || tab == NO_TABBUFFER)
      return;
    deleteTab(tab);
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}
#endif                        /* USE_MOUSE */

DEFUN(dispVer, VERSION, "Display version of w3m")
{
    disp_message(Sprintf("w3m version %s", w3m_version)->ptr, TRUE);
}

DEFUN(wrapToggle, WRAP_TOGGLE, "Toggle wrap search mode")
{
    if (WrapSearch) {
      WrapSearch = FALSE;
      /* FIXME: gettextize? */
      disp_message("Wrap search off", TRUE);
    }
    else {
      WrapSearch = TRUE;
      /* FIXME: gettextize? */
      disp_message("Wrap search on", TRUE);
    }
}

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_DICT
static void
execdict(char *word)
{
    char *w, *dictcmd;
    Buffer *buf;

    if (!UseDictCommand || word == NULL || *word == '\0') {
      displayBuffer(Currentbuf, B_NORMAL);
      return;
    }
    w = conv_to_system(word);
    if (*w == '\0') {
      displayBuffer(Currentbuf, B_NORMAL);
      return;
    }
    dictcmd = Sprintf("%s?%s", DictCommand,
                  Str_form_quote(Strnew_charp(w))->ptr)->ptr;
    buf = loadGeneralFile(dictcmd, NULL, NO_REFERER, 0, NULL);
    if (buf == NULL) {
      disp_message("Execution failed", TRUE);
      return;
    }
    else {
      buf->filename = w;
      buf->buffername = Sprintf("%s %s", DICTBUFFERNAME, word)->ptr;
      if (buf->type == NULL)
          buf->type = "text/plain";
      pushBuffer(buf);
    }
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

DEFUN(dictword, DICT_WORD, "Execute dictionary command (see README.dict)")
{
    execdict(inputStr("(dictionary)!", ""));
}

DEFUN(dictwordat, DICT_WORD_AT,
      "Execute dictionary command for word at cursor")
{
    execdict(GetWord(Currentbuf));
}
#endif                        /* USE_DICT */

void
set_buffer_environ(Buffer *buf)
{
    static Buffer *prev_buf = NULL;
    static Line *prev_line = NULL;
    static int prev_pos = -1;
    Line *l;

    if (buf == NULL)
      return;
    if (buf != prev_buf) {
      set_environ("W3M_SOURCEFILE", buf->sourcefile);
      set_environ("W3M_FILENAME", buf->filename);
      set_environ("W3M_TITLE", buf->buffername);
      set_environ("W3M_URL", parsedURL2Str(&buf->currentURL)->ptr);
      set_environ("W3M_TYPE", buf->real_type ? buf->real_type : "unknown");
#ifdef USE_M17N
      set_environ("W3M_CHARSET", wc_ces_to_charset(buf->document_charset));
#endif
    }
    l = buf->currentLine;
    if (l && (buf != prev_buf || l != prev_line || buf->pos != prev_pos)) {
      Anchor *a;
      ParsedURL pu;
      char *s = GetWord(buf);
      set_environ("W3M_CURRENT_WORD", s ? s : "");
      a = retrieveCurrentAnchor(buf);
      if (a) {
          parseURL2(a->url, &pu, baseURL(buf));
          set_environ("W3M_CURRENT_LINK", parsedURL2Str(&pu)->ptr);
      }
      else
          set_environ("W3M_CURRENT_LINK", "");
      a = retrieveCurrentImg(buf);
      if (a) {
          parseURL2(a->url, &pu, baseURL(buf));
          set_environ("W3M_CURRENT_IMG", parsedURL2Str(&pu)->ptr);
      }
      else
          set_environ("W3M_CURRENT_IMG", "");
      a = retrieveCurrentForm(buf);
      if (a)
          set_environ("W3M_CURRENT_FORM", form2str((FormItemList *)a->url));
      else
          set_environ("W3M_CURRENT_FORM", "");
      set_environ("W3M_CURRENT_LINE", Sprintf("%d",
                                    l->real_linenumber)->ptr);
      set_environ("W3M_CURRENT_COLUMN", Sprintf("%d",
                                      buf->currentColumn +
                                      buf->cursorX + 1)->ptr);
    }
    else if (!l) {
      set_environ("W3M_CURRENT_WORD", "");
      set_environ("W3M_CURRENT_LINK", "");
      set_environ("W3M_CURRENT_IMG", "");
      set_environ("W3M_CURRENT_FORM", "");
      set_environ("W3M_CURRENT_LINE", "0");
      set_environ("W3M_CURRENT_COLUMN", "0");
    }
    prev_buf = buf;
    prev_line = l;
    prev_pos = buf->pos;
}

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

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

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

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

#ifdef __EMX__
#ifdef USE_M17N
static char *
getCodePage(void)
{
    ULONG CpList[8], CpSize;

    if (!getenv("WINDOWID") && !DosQueryCp(sizeof(CpList), CpList, &CpSize))
      return Sprintf("CP%d", *CpList)->ptr;
    return NULL;
}
#endif
#endif

void
deleteFiles()
{
    Buffer *buf;
    char *f;

    for (CurrentTab = FirstTab; CurrentTab; CurrentTab = CurrentTab->nextTab) {
      while (Firstbuf && Firstbuf != NO_BUFFER) {
          buf = Firstbuf->nextBuffer;
          discardBuffer(Firstbuf);
          Firstbuf = buf;
      }
    }
    while ((f = popText(fileToDelete)) != NULL)
      unlink(f);
}

void
w3m_exit(int i)
{
#ifdef USE_MIGEMO
    init_migemo();            /* close pipe to migemo */
#endif
    stopDownload();
    deleteFiles();
#ifdef USE_SSL
    free_ssl_ctx();
#endif
    disconnectFTP();
#ifdef USE_NNTP
    disconnectNews();
#endif
    exit(i);
}

DEFUN(execCmd, COMMAND, "Execute w3m command(s)")
{
    char *data, *p;
    int cmd;

    CurrentKeyData = NULL;    /* not allowed in w3m-control: */
    data = searchKeyData();
    if (data == NULL || *data == '\0') {
      data = inputStrHist("command [; ...]: ", "", TextHist);
      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);
      cmd = getFuncList(p);
      if (cmd < 0)
          break;
      p = getQWord(&data);
      CurrentKey = -1;
      CurrentKeyData = NULL;
      CurrentCmdData = *p ? p : NULL;
#ifdef USE_MOUSE
      if (use_mouse)
          mouse_inactive();
#endif
      w3mFuncList[cmd].func();
#ifdef USE_MOUSE
      if (use_mouse)
          mouse_active();
#endif
      CurrentCmdData = NULL;
    }
    displayBuffer(Currentbuf, B_NORMAL);
}

#ifdef USE_ALARM
static MySignalHandler
SigAlarm(SIGNAL_ARG)
{
    char *data;

    if (CurrentAlarm->sec > 0) {
      CurrentKey = -1;
      CurrentKeyData = NULL;
      CurrentCmdData = data = (char *)CurrentAlarm->data;
#ifdef USE_MOUSE
      if (use_mouse)
          mouse_inactive();
#endif
      w3mFuncList[CurrentAlarm->cmd].func();
#ifdef USE_MOUSE
      if (use_mouse)
          mouse_active();
#endif
      CurrentCmdData = NULL;
      if (CurrentAlarm->status == AL_IMPLICIT_ONCE) {
          CurrentAlarm->sec = 0;
          CurrentAlarm->status = AL_UNSET;
      }
      if (Currentbuf->event) {
          if (Currentbuf->event->status != AL_UNSET)
            CurrentAlarm = Currentbuf->event;
          else
            Currentbuf->event = NULL;
      }
      if (!Currentbuf->event)
          CurrentAlarm = &DefaultAlarm;
      if (CurrentAlarm->sec > 0) {
          mySignal(SIGALRM, SigAlarm);
          alarm(CurrentAlarm->sec);
      }
    }
    SIGNAL_RETURN;
}


DEFUN(setAlarm, ALARM, "Set alarm")
{
    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)
          cmd = getFuncList(getWord(&data));
    }
    if (cmd >= 0) {
      data = getQWord(&data);
      setAlarmEvent(&DefaultAlarm, sec, AL_EXPLICIT, cmd, data);
      disp_message_nsec(Sprintf("%dsec %s %s", sec, w3mFuncList[cmd].id,
                          data)->ptr, FALSE, 1, FALSE, TRUE);
    }
    else {
      setAlarmEvent(&DefaultAlarm, 0, AL_UNSET, FUNCNAME_nulcmd, NULL);
    }
    displayBuffer(Currentbuf, B_NORMAL);
}

AlarmEvent *
setAlarmEvent(AlarmEvent * event, int sec, short status, int cmd, void *data)
{
    if (event == NULL)
      event = New(AlarmEvent);
    event->sec = sec;
    event->status = status;
    event->cmd = cmd;
    event->data = data;
    return event;
}
#endif

DEFUN(reinit, REINIT, "Reload configuration files")
{
    char *resource = searchKeyData();

    if (resource == NULL) {
      init_rc();
      sync_with_option();
#ifdef USE_COOKIE
      initCookie();
#endif
      displayBuffer(Currentbuf, B_REDRAW_IMAGE);
      return;
    }

    if (!strcasecmp(resource, "CONFIG") || !strcasecmp(resource, "RC")) {
      init_rc();
      sync_with_option();
      displayBuffer(Currentbuf, B_REDRAW_IMAGE);
      return;
    }

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

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

    if (!strcasecmp(resource, "MAILCAP")) {
      initMailcap();
      return;
    }

#ifdef USE_MOUSE
    if (!strcasecmp(resource, "MOUSE")) {
      initMouseAction();
      displayBuffer(Currentbuf, B_REDRAW_IMAGE);
      return;
    }
#endif

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

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

#ifdef USE_EXTERNAL_URI_LOADER
    if (!strcasecmp(resource, "URIMETHODS")) {
      initURIMethods();
      return;
    }
#endif

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

DEFUN(defKey, DEFINE_KEY,
      "Define a binding between a key stroke and a user command")
{
    char *data;

    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);
    displayBuffer(Currentbuf, B_NORMAL);
}

TabBuffer *
newTab(void)
{
    TabBuffer *n;

    n = New(TabBuffer);
    if (n == NULL)
      return NULL;
    n->nextTab = NULL;
    n->currentBuffer = NULL;
    n->firstBuffer = NULL;
    return n;
}

static void
_newT(void)
{
    TabBuffer *tag;
    Buffer *buf;
    int i;

    tag = newTab();
    if (!tag)
      return;

    buf = newBuffer(Currentbuf->width);
    copyBuffer(buf, Currentbuf);
    buf->nextBuffer = NULL;
    for (i = 0; i < MAX_LB; i++)
      buf->linkBuffer[i] = NULL;
    (*buf->clone)++;
    tag->firstBuffer = tag->currentBuffer = buf;

    tag->nextTab = CurrentTab->nextTab;
    tag->prevTab = CurrentTab;
    if (CurrentTab->nextTab)
      CurrentTab->nextTab->prevTab = tag;
    else
      LastTab = tag;
    CurrentTab->nextTab = tag;
    CurrentTab = tag;
    nTab++;
}

DEFUN(newT, NEW_TAB, "Open new tab")
{
    _newT();
    displayBuffer(Currentbuf, B_REDRAW_IMAGE);
}

static TabBuffer *
numTab(int n)
{
    TabBuffer *tab;
    int i;

    if (n == 0)
      return CurrentTab;
    if (n == 1)
      return FirstTab;
    if (nTab <= 1)
      return NULL;
    for (tab = FirstTab, i = 1; tab && i < n; tab = tab->nextTab, i++) ;
    return tab;
}

void
calcTabPos(void)
{
    TabBuffer *tab;
#if 0
    int lcol = 0, rcol = 2, col;
#else
    int lcol = 0, rcol = 0, col;
#endif
    int n1, n2, na, nx, ny, ix, iy;

#ifdef USE_MOUSE
    lcol = mouse_action.menu_str ? mouse_action.menu_width : 0;
#endif

    if (nTab <= 0)
      return;
    n1 = (COLS - rcol - lcol) / TabCols;
    if (n1 >= nTab) {
      n2 = 1;
      ny = 1;
    }
    else {
      if (n1 < 0)
          n1 = 0;
      n2 = COLS / TabCols;
      if (n2 == 0)
          n2 = 1;
      ny = (nTab - n1 - 1) / n2 + 2;
    }
    na = n1 + n2 * (ny - 1);
    n1 -= (na - nTab) / ny;
    if (n1 < 0)
      n1 = 0;
    na = n1 + n2 * (ny - 1);
    tab = FirstTab;
    for (iy = 0; iy < ny && tab; iy++) {
      if (iy == 0) {
          nx = n1;
          col = COLS - rcol - lcol;
      }
      else {
          nx = n2 - (na - nTab + (iy - 1)) / (ny - 1);
          col = COLS;
      }
      for (ix = 0; ix < nx && tab; ix++, tab = tab->nextTab) {
          tab->x1 = col * ix / nx;
          tab->x2 = col * (ix + 1) / nx - 1;
          tab->y = iy;
          if (iy == 0) {
            tab->x1 += lcol;
            tab->x2 += lcol;
          }
      }
    }
}

TabBuffer *
deleteTab(TabBuffer * tab)
{
    Buffer *buf, *next;

    if (nTab <= 1)
      return FirstTab;
    if (tab->prevTab) {
      if (tab->nextTab)
          tab->nextTab->prevTab = tab->prevTab;
      else
          LastTab = tab->prevTab;
      tab->prevTab->nextTab = tab->nextTab;
      if (tab == CurrentTab)
          CurrentTab = tab->prevTab;
    }
    else {              /* tab == FirstTab */
      tab->nextTab->prevTab = NULL;
      FirstTab = tab->nextTab;
      if (tab == CurrentTab)
          CurrentTab = tab->nextTab;
    }
    nTab--;
    buf = tab->firstBuffer;
    while (buf && buf != NO_BUFFER) {
      next = buf->nextBuffer;
      discardBuffer(buf);
      buf = next;
    }
    return FirstTab;
}

DEFUN(closeT, CLOSE_TAB, "Close current tab")
{
    TabBuffer *tab;

    if (nTab <= 1)
      return;
    if (prec_num)
      tab = numTab(PREC_NUM);
    else
      tab = CurrentTab;
    if (tab)
      deleteTab(tab);
    displayBuffer(Currentbuf, B_REDRAW_IMAGE);
}

DEFUN(nextT, NEXT_TAB, "Move to next tab")
{
    int i;

    if (nTab <= 1)
      return;
    for (i = 0; i < PREC_NUM; i++) {
      if (CurrentTab->nextTab)
          CurrentTab = CurrentTab->nextTab;
      else
          CurrentTab = FirstTab;
    }
    displayBuffer(Currentbuf, B_REDRAW_IMAGE);
}

DEFUN(prevT, PREV_TAB, "Move to previous tab")
{
    int i;

    if (nTab <= 1)
      return;
    for (i = 0; i < PREC_NUM; i++) {
      if (CurrentTab->prevTab)
          CurrentTab = CurrentTab->prevTab;
      else
          CurrentTab = LastTab;
    }
    displayBuffer(Currentbuf, B_REDRAW_IMAGE);
}

void
followTab(TabBuffer * tab)
{
    Buffer *buf;
    Anchor *a;

#ifdef USE_IMAGE
    a = retrieveCurrentImg(Currentbuf);
    if (!(a && a->image && a->image->map))
#endif
      a = retrieveCurrentAnchor(Currentbuf);
    if (a == NULL)
      return;

    if (tab == CurrentTab) {
      check_target = FALSE;
      followA();
      check_target = TRUE;
      return;
    }
    _newT();
    buf = Currentbuf;
    check_target = FALSE;
    followA();
    check_target = TRUE;
    if (tab == NULL) {
      if (buf != Currentbuf)
          delBuffer(buf);
      else
          deleteTab(CurrentTab);
    }
    else if (buf != Currentbuf) {
      /* buf <- p <- ... <- Currentbuf = c */
      Buffer *c, *p;

      c = Currentbuf;
      p = prevBuffer(c, buf);
      p->nextBuffer = NULL;
      Firstbuf = buf;
      deleteTab(CurrentTab);
      CurrentTab = tab;
      for (buf = p; buf; buf = p) {
          p = prevBuffer(c, buf);
          pushBuffer(buf);
      }
    }
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

DEFUN(tabA, TAB_LINK, "Open current link on new tab")
{
    followTab(prec_num ? numTab(PREC_NUM) : NULL);
}

static void
tabURL0(TabBuffer * tab, char *prompt, int relative)
{
    Buffer *buf;

    if (tab == CurrentTab) {
      goURL0(prompt, relative);
      return;
    }
    _newT();
    buf = Currentbuf;
    goURL0(prompt, relative);
    if (tab == NULL) {
      if (buf != Currentbuf)
          delBuffer(buf);
      else
          deleteTab(CurrentTab);
    }
    else if (buf != Currentbuf) {
      /* buf <- p <- ... <- Currentbuf = c */
      Buffer *c, *p;

      c = Currentbuf;
      p = prevBuffer(c, buf);
      p->nextBuffer = NULL;
      Firstbuf = buf;
      deleteTab(CurrentTab);
      CurrentTab = tab;
      for (buf = p; buf; buf = p) {
          p = prevBuffer(c, buf);
          pushBuffer(buf);
      }
    }
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

DEFUN(tabURL, TAB_GOTO, "Open URL on new tab")
{
    tabURL0(prec_num ? numTab(PREC_NUM) : NULL,
          "Goto URL on new tab: ", FALSE);
}

DEFUN(tabrURL, TAB_GOTO_RELATIVE, "Open relative URL on new tab")
{
    tabURL0(prec_num ? numTab(PREC_NUM) : NULL,
          "Goto relative URL on new tab: ", TRUE);
}

void
moveTab(TabBuffer * t, TabBuffer * t2, int right)
{
    if (t2 == NO_TABBUFFER)
      t2 = FirstTab;
    if (!t || !t2 || t == t2 || t == NO_TABBUFFER)
      return;
    if (t->prevTab) {
      if (t->nextTab)
          t->nextTab->prevTab = t->prevTab;
      else
          LastTab = t->prevTab;
      t->prevTab->nextTab = t->nextTab;
    }
    else {
      t->nextTab->prevTab = NULL;
      FirstTab = t->nextTab;
    }
    if (right) {
      t->nextTab = t2->nextTab;
      t->prevTab = t2;
      if (t2->nextTab)
          t2->nextTab->prevTab = t;
      else
          LastTab = t;
      t2->nextTab = t;
    }
    else {
      t->prevTab = t2->prevTab;
      t->nextTab = t2;
      if (t2->prevTab)
          t2->prevTab->nextTab = t;
      else
          FirstTab = t;
      t2->prevTab = t;
    }
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

DEFUN(tabR, TAB_RIGHT, "Move current tab right")
{
    TabBuffer *tab;
    int i;

    for (tab = CurrentTab, i = 0; tab && i < PREC_NUM;
       tab = tab->nextTab, i++) ;
    moveTab(CurrentTab, tab ? tab : LastTab, TRUE);
}

DEFUN(tabL, TAB_LEFT, "Move current tab left")
{
    TabBuffer *tab;
    int i;

    for (tab = CurrentTab, i = 0; tab && i < PREC_NUM;
       tab = tab->prevTab, i++) ;
    moveTab(CurrentTab, tab ? tab : FirstTab, FALSE);
}

void
addDownloadList(pid_t pid, char *url, char *save, char *lock, clen_t size)
{
    DownloadList *d;

    d = New(DownloadList);
    d->pid = pid;
    d->url = url;
    if (save[0] != '/' && save[0] != '~')
      save = Strnew_m_charp(CurrentDir, "/", save, NULL)->ptr;
    d->save = expandPath(save);
    d->lock = lock;
    d->size = size;
    d->time = time(0);
    d->ok = FALSE;
    d->next = NULL;
    d->prev = LastDL;
    if (LastDL)
      LastDL->next = d;
    else
      FirstDL = d;
    LastDL = d;
    add_download_list = TRUE;
}

int
checkDownloadList(void)
{
    DownloadList *d;
    struct stat st;

    if (!FirstDL)
      return FALSE;
    for (d = FirstDL; d != NULL; d = d->next) {
      if (!d->ok && !lstat(d->lock, &st))
          return TRUE;
    }
    return FALSE;
}

static char *
convert_size3(clen_t size)
{
    Str tmp = Strnew();
    int n;

    do {
      n = size % 1000;
      size /= 1000;
      tmp = Sprintf(size ? ",%.3d%s" : "%d%s", n, tmp->ptr);
    } while (size);
    return tmp->ptr;
}

static Buffer *
DownloadListBuffer(void)
{
    DownloadList *d;
    Str src = NULL;
    struct stat st;
    time_t cur_time;
    int duration, rate, eta;
    size_t size;

    if (!FirstDL)
      return NULL;
    cur_time = time(0);
    /* FIXME: gettextize? */
    src = Strnew_charp("<html><head><title>" DOWNLOAD_LIST_TITLE
                   "</title></head>\n<body><h1 align=center>"
                   DOWNLOAD_LIST_TITLE "</h1>\n"
                   "<form method=internal action=download><hr>\n");
    for (d = LastDL; d != NULL; d = d->prev) {
      if (lstat(d->lock, &st))
          d->ok = TRUE;
      Strcat_charp(src, "<pre>\n");
      Strcat(src, Sprintf("%s\n  --&gt; %s\n  ", html_quote(d->url),
                      html_quote(conv_from_system(d->save))));
      duration = cur_time - d->time;
      if (!stat(d->save, &st)) {
          size = st.st_size;
          if (d->ok) {
            d->size = size;
            duration = st.st_mtime - d->time;
          }
      }
      else
          size = 0;
      if (d->size) {
          int i, l = COLS - 6;
          if (size < d->size)
            i = 1.0 * l * size / d->size;
          else
            i = l;
          l -= i;
          while (i-- > 0)
            Strcat_char(src, '#');
          while (l-- > 0)
            Strcat_char(src, '_');
          Strcat_char(src, '\n');
      }
      if (!d->ok && size < d->size)
          Strcat(src, Sprintf("  %s / %s bytes (%d%%)",
                        convert_size3(size), convert_size3(d->size),
                        (int)(100.0 * size / d->size)));
      else
          Strcat(src, Sprintf("  %s bytes loaded", convert_size3(size)));
      if (duration > 0) {
          rate = size / duration;
          Strcat(src, Sprintf("  %02d:%02d:%02d  rate %s/sec",
                        duration / (60 * 60), (duration / 60) % 60,
                        duration % 60, convert_size(rate, 1)));
          if (!d->ok && size < d->size && rate) {
            eta = (d->size - size) / rate;
            Strcat(src, Sprintf("  eta %02d:%02d:%02d", eta / (60 * 60),
                            (eta / 60) % 60, eta % 60));
          }
      }
      Strcat_char(src, '\n');
      if (d->ok) {
          Strcat(src, Sprintf("<input type=submit name=ok%d value=OK>",
                        d->pid));
          if (size < d->size)
            Strcat_charp(src, " Download incompleted");
          else
            Strcat_charp(src, " Download completed");
      }
      else
          Strcat(src, Sprintf("<input type=submit name=stop%d value=STOP>",
                        d->pid));
      Strcat_charp(src, "\n</pre><hr>\n");
    }
    Strcat_charp(src, "</form></body></html>");
    return loadHTMLString(src);
}

void
download_action(struct parsed_tagarg *arg)
{
    DownloadList *d;
    pid_t pid;

    for (; arg; arg = arg->next) {
      if (!strncmp(arg->arg, "stop", 4)) {
          pid = (pid_t) atoi(&arg->arg[4]);
          kill(pid, SIGKILL);
      }
      else if (!strncmp(arg->arg, "ok", 2))
          pid = (pid_t) atoi(&arg->arg[2]);
      else
          continue;
      for (d = FirstDL; d; d = d->next) {
          if (d->pid == pid) {
            unlink(d->lock);
            if (d->prev)
                d->prev->next = d->next;
            else
                FirstDL = d->next;
            if (d->next)
                d->next->prev = d->prev;
            else
                LastDL = d->prev;
            break;
          }
      }
    }
    ldDL();
}

void
stopDownload(void)
{
    DownloadList *d;

    if (!FirstDL)
      return;
    for (d = FirstDL; d != NULL; d = d->next) {
      if (d->ok)
          continue;
      kill(d->pid, SIGKILL);
      unlink(d->lock);
    }
}

/* download panel */
DEFUN(ldDL, DOWNLOAD_LIST, "Display download list panel")
{
    Buffer *buf;
    int replace = FALSE, new_tab = FALSE;
#ifdef USE_ALARM
    int reload;
#endif

    if (Currentbuf->bufferprop & BP_INTERNAL &&
      !strcmp(Currentbuf->buffername, DOWNLOAD_LIST_TITLE))
      replace = TRUE;
    if (!FirstDL) {
      if (replace) {
          if (Currentbuf == Firstbuf && Currentbuf->nextBuffer == NULL) {
            if (nTab > 1)
                deleteTab(CurrentTab);
          }
          else
            delBuffer(Currentbuf);
          displayBuffer(Currentbuf, B_FORCE_REDRAW);
      }
      return;
    }
#ifdef USE_ALARM
    reload = checkDownloadList();
#endif
    buf = DownloadListBuffer();
    if (!buf) {
      displayBuffer(Currentbuf, B_NORMAL);
      return;
    }
    buf->bufferprop |= (BP_INTERNAL | BP_NO_URL);
    if (replace)
      restorePosition(buf, Currentbuf);
    if (!replace && open_tab_dl_list) {
      _newT();
      new_tab = TRUE;
    }
    pushBuffer(buf);
    if (replace || new_tab)
      deletePrevBuf();
#ifdef USE_ALARM
    if (reload)
      Currentbuf->event = setAlarmEvent(Currentbuf->event, 1, AL_IMPLICIT,
                                FUNCNAME_reload, NULL);
#endif
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

static void
save_buffer_position(Buffer *buf)
{
    BufferPos *b = buf->undo;

    if (!buf->firstLine)
      return;
    if (b && b->top_linenumber == TOP_LINENUMBER(buf) &&
      b->cur_linenumber == CUR_LINENUMBER(buf) &&
      b->currentColumn == buf->currentColumn && b->pos == buf->pos)
      return;
    b = New(BufferPos);
    b->top_linenumber = TOP_LINENUMBER(buf);
    b->cur_linenumber = CUR_LINENUMBER(buf);
    b->currentColumn = buf->currentColumn;
    b->pos = buf->pos;
    b->bpos = buf->currentLine ? buf->currentLine->bpos : 0;
    b->next = NULL;
    b->prev = buf->undo;
    if (buf->undo)
      buf->undo->next = b;
    buf->undo = b;
}

static void
resetPos(BufferPos * b)
{
    Buffer buf;
    Line top, cur;

    top.linenumber = b->top_linenumber;
    cur.linenumber = b->cur_linenumber;
    cur.bpos = b->bpos;
    buf.topLine = &top;
    buf.currentLine = &cur;
    buf.pos = b->pos;
    buf.currentColumn = b->currentColumn;
    restorePosition(Currentbuf, &buf);
    Currentbuf->undo = b;
    displayBuffer(Currentbuf, B_FORCE_REDRAW);
}

DEFUN(undoPos, UNDO, "Cancel the last cursor movement")
{
    BufferPos *b = Currentbuf->undo;
    int i;

    if (!Currentbuf->firstLine)
      return;
    if (!b || !b->prev)
      return;
    for (i = 0; i < PREC_NUM && b->prev; i++, b = b->prev) ;
    resetPos(b);
}

DEFUN(redoPos, REDO, "Cancel the last undo")
{
    BufferPos *b = Currentbuf->undo;
    int i;

    if (!Currentbuf->firstLine)
      return;
    if (!b || !b->next)
      return;
    for (i = 0; i < PREC_NUM && b->next; i++, b = b->next) ;
    resetPos(b);
}

Generated by  Doxygen 1.6.0   Back to index