w3m

Unnamed repository; edit this file to name it for gitweb.
git clone https://logand.com/git/w3m.git/
Log | Files | Refs | README

main.c (155402B)


      1 /* $Id$ */
      2 #define MAINPROGRAM
      3 #include "fm.h"
      4 #include <signal.h>
      5 #include <setjmp.h>
      6 #include <sys/stat.h>
      7 #include <sys/types.h>
      8 #include <unistd.h>
      9 #include <fcntl.h>
     10 #if defined(HAVE_WAITPID) || defined(HAVE_WAIT3)
     11 #include <sys/wait.h>
     12 #endif
     13 #include <time.h>
     14 #include "terms.h"
     15 #include "myctype.h"
     16 #include "regex.h"
     17 #ifdef USE_M17N
     18 #include "wc.h"
     19 #include "wtf.h"
     20 #ifdef USE_UNICODE
     21 #include "ucs.h"
     22 #endif
     23 #endif
     24 #ifdef USE_MOUSE
     25 #ifdef USE_GPM
     26 #include <gpm.h>
     27 #endif				/* USE_GPM */
     28 #if defined(USE_GPM) || defined(USE_SYSMOUSE)
     29 extern int do_getch();
     30 #define getch()	do_getch()
     31 #endif				/* defined(USE_GPM) || defined(USE_SYSMOUSE) */
     32 #endif
     33 
     34 #ifdef __MINGW32_VERSION
     35 #include <winsock.h>
     36 
     37 WSADATA WSAData;
     38 #endif
     39 
     40 #define DSTR_LEN	256
     41 
     42 Hist *LoadHist;
     43 Hist *SaveHist;
     44 Hist *URLHist;
     45 Hist *ShellHist;
     46 Hist *TextHist;
     47 
     48 typedef struct _Event {
     49     int cmd;
     50     void *data;
     51     struct _Event *next;
     52 } Event;
     53 static Event *CurrentEvent = NULL;
     54 static Event *LastEvent = NULL;
     55 
     56 #ifdef USE_ALARM
     57 static AlarmEvent DefaultAlarm = {
     58     0, AL_UNSET, FUNCNAME_nulcmd, NULL
     59 };
     60 static AlarmEvent *CurrentAlarm = &DefaultAlarm;
     61 static MySignalHandler SigAlarm(SIGNAL_ARG);
     62 #endif
     63 
     64 #ifdef SIGWINCH
     65 static int need_resize_screen = FALSE;
     66 static MySignalHandler resize_hook(SIGNAL_ARG);
     67 static void resize_screen(void);
     68 #endif
     69 
     70 #ifdef SIGPIPE
     71 static MySignalHandler SigPipe(SIGNAL_ARG);
     72 #endif
     73 
     74 #ifdef USE_MARK
     75 static char *MarkString = NULL;
     76 #endif
     77 static char *SearchString = NULL;
     78 int (*searchRoutine) (Buffer *, char *);
     79 
     80 #ifndef __MINGW32_VERSION
     81 JMP_BUF IntReturn;
     82 #else
     83 _JBTYPE IntReturn[_JBLEN];
     84 #endif /* __MINGW32_VERSION */
     85 
     86 static void delBuffer(Buffer *buf);
     87 static void cmd_loadfile(char *path);
     88 static void cmd_loadURL(char *url, ParsedURL *current, char *referer,
     89 			FormList *request);
     90 static void cmd_loadBuffer(Buffer *buf, int prop, int linkid);
     91 static void keyPressEventProc(int c);
     92 int show_params_p = 0;
     93 void show_params(FILE * fp);
     94 
     95 static char *getCurWord(Buffer *buf, int *spos, int *epos);
     96 
     97 static int display_ok = FALSE;
     98 static void do_dump(Buffer *);
     99 int prec_num = 0;
    100 int prev_key = -1;
    101 int on_target = 1;
    102 static int add_download_list = FALSE;
    103 
    104 void set_buffer_environ(Buffer *);
    105 static void save_buffer_position(Buffer *buf);
    106 
    107 static void _followForm(int);
    108 static void _goLine(char *);
    109 static void _newT(void);
    110 static void followTab(TabBuffer * tab);
    111 static void moveTab(TabBuffer * t, TabBuffer * t2, int right);
    112 static void _nextA(int);
    113 static void _prevA(int);
    114 static int check_target = TRUE;
    115 #define PREC_NUM (prec_num ? prec_num : 1)
    116 #define PREC_LIMIT 10000
    117 static int searchKeyNum(void);
    118 
    119 #define help() fusage(stdout, 0)
    120 #define usage() fusage(stderr, 1)
    121 
    122 static void
    123 fversion(FILE * f)
    124 {
    125     fprintf(f, "w3m version %s, options %s\n", w3m_version,
    126 #if LANG == JA
    127 	    "lang=ja"
    128 #else
    129 	    "lang=en"
    130 #endif
    131 #ifdef USE_M17N
    132 	    ",m17n"
    133 #endif
    134 #ifdef USE_IMAGE
    135 	    ",image"
    136 #endif
    137 #ifdef USE_COLOR
    138 	    ",color"
    139 #ifdef USE_ANSI_COLOR
    140 	    ",ansi-color"
    141 #endif
    142 #endif
    143 #ifdef USE_MOUSE
    144 	    ",mouse"
    145 #ifdef USE_GPM
    146 	    ",gpm"
    147 #endif
    148 #ifdef USE_SYSMOUSE
    149 	    ",sysmouse"
    150 #endif
    151 #endif
    152 #ifdef USE_MENU
    153 	    ",menu"
    154 #endif
    155 #ifdef USE_COOKIE
    156 	    ",cookie"
    157 #endif
    158 #ifdef USE_SSL
    159 	    ",ssl"
    160 #ifdef USE_SSL_VERIFY
    161 	    ",ssl-verify"
    162 #endif
    163 #endif
    164 #ifdef USE_EXTERNAL_URI_LOADER
    165 	    ",external-uri-loader"
    166 #endif
    167 #ifdef USE_W3MMAILER
    168 	    ",w3mmailer"
    169 #endif
    170 #ifdef USE_NNTP
    171 	    ",nntp"
    172 #endif
    173 #ifdef USE_GOPHER
    174 	    ",gopher"
    175 #endif
    176 #ifdef INET6
    177 	    ",ipv6"
    178 #endif
    179 #ifdef USE_ALARM
    180 	    ",alarm"
    181 #endif
    182 #ifdef USE_MARK
    183 	    ",mark"
    184 #endif
    185 #ifdef USE_MIGEMO
    186 	    ",migemo"
    187 #endif
    188 	);
    189 }
    190 
    191 static void
    192 fusage(FILE * f, int err)
    193 {
    194     fversion(f);
    195     /* FIXME: gettextize? */
    196     fprintf(f, "usage: w3m [options] [URL or filename]\noptions:\n");
    197     fprintf(f, "    -t tab           set tab width\n");
    198     fprintf(f, "    -r               ignore backspace effect\n");
    199     fprintf(f, "    -l line          # of preserved line (default 10000)\n");
    200 #ifdef USE_M17N
    201     fprintf(f, "    -I charset       document charset\n");
    202     fprintf(f, "    -O charset       display/output charset\n");
    203     fprintf(f, "    -e               EUC-JP\n");
    204     fprintf(f, "    -s               Shift_JIS\n");
    205     fprintf(f, "    -j               JIS\n");
    206 #endif
    207     fprintf(f, "    -B               load bookmark\n");
    208     fprintf(f, "    -bookmark file   specify bookmark file\n");
    209     fprintf(f, "    -T type          specify content-type\n");
    210     fprintf(f, "    -m               internet message mode\n");
    211     fprintf(f, "    -v               visual startup mode\n");
    212 #ifdef USE_COLOR
    213     fprintf(f, "    -M               monochrome display\n");
    214 #endif				/* USE_COLOR */
    215     fprintf(f,
    216 	    "    -N               open URL of command line on each new tab\n");
    217     fprintf(f, "    -F               automatically render frame\n");
    218     fprintf(f,
    219 	    "    -cols width      specify column width (used with -dump)\n");
    220     fprintf(f,
    221 	    "    -ppc count       specify the number of pixels per character (4.0...32.0)\n");
    222 #ifdef USE_IMAGE
    223     fprintf(f,
    224 	    "    -ppl count       specify the number of pixels per line (4.0...64.0)\n");
    225 #endif
    226     fprintf(f, "    -dump            dump formatted page into stdout\n");
    227     fprintf(f,
    228 	    "    -dump_head       dump response of HEAD request into stdout\n");
    229     fprintf(f, "    -dump_source     dump page source into stdout\n");
    230     fprintf(f, "    -dump_both       dump HEAD and source into stdout\n");
    231     fprintf(f,
    232 	    "    -dump_extra      dump HEAD, source, and extra information into stdout\n");
    233     fprintf(f, "    -post file       use POST method with file content\n");
    234     fprintf(f, "    -header string   insert string as a header\n");
    235     fprintf(f, "    +<num>           goto <num> line\n");
    236     fprintf(f, "    -num             show line number\n");
    237     fprintf(f, "    -no-proxy        don't use proxy\n");
    238 #ifdef INET6
    239     fprintf(f, "    -4               IPv4 only (-o dns_order=4)\n");
    240     fprintf(f, "    -6               IPv6 only (-o dns_order=6)\n");
    241 #endif
    242 #ifdef USE_MOUSE
    243     fprintf(f, "    -no-mouse        don't use mouse\n");
    244 #endif				/* USE_MOUSE */
    245 #ifdef USE_COOKIE
    246     fprintf(f,
    247 	    "    -cookie          use cookie (-no-cookie: don't use cookie)\n");
    248 #endif				/* USE_COOKIE */
    249     fprintf(f, "    -graph           use DEC special graphics for border of table and menu\n");
    250     fprintf(f, "    -no-graph        use ACII character for border of table and menu\n");
    251     fprintf(f, "    -S               squeeze multiple blank lines\n");
    252     fprintf(f, "    -W               toggle wrap search mode\n");
    253     fprintf(f, "    -X               don't use termcap init/deinit\n");
    254     fprintf(f,
    255 	    "    -title[=TERM]    set buffer name to terminal title string\n");
    256     fprintf(f, "    -o opt=value     assign value to config option\n");
    257     fprintf(f, "    -show-option     print all config options\n");
    258     fprintf(f, "    -config file     specify config file\n");
    259     fprintf(f, "    -help            print this usage message\n");
    260     fprintf(f, "    -version         print w3m version\n");
    261     fprintf(f, "    -reqlog          write request logfile\n");
    262     fprintf(f, "    -debug           DO NOT USE\n");
    263     if (show_params_p)
    264 	show_params(f);
    265     exit(err);
    266 }
    267 
    268 #ifdef USE_M17N
    269 #ifdef __EMX__
    270 static char *getCodePage(void);
    271 #endif
    272 #endif
    273 
    274 static GC_warn_proc orig_GC_warn_proc = NULL;
    275 #define GC_WARN_KEEP_MAX (20)
    276 
    277 static void
    278 wrap_GC_warn_proc(char *msg, GC_word arg)
    279 {
    280     if (fmInitialized) {
    281 	/* *INDENT-OFF* */
    282 	static struct {
    283 	    char *msg;
    284 	    GC_word arg;
    285 	} msg_ring[GC_WARN_KEEP_MAX];
    286 	/* *INDENT-ON* */
    287 	static int i = 0;
    288 	static int n = 0;
    289 	static int lock = 0;
    290 	int j;
    291 
    292 	j = (i + n) % (sizeof(msg_ring) / sizeof(msg_ring[0]));
    293 	msg_ring[j].msg = msg;
    294 	msg_ring[j].arg = arg;
    295 
    296 	if (n < sizeof(msg_ring) / sizeof(msg_ring[0]))
    297 	    ++n;
    298 	else
    299 	    ++i;
    300 
    301 	if (!lock) {
    302 	    lock = 1;
    303 
    304 	    for (; n > 0; --n, ++i) {
    305 		i %= sizeof(msg_ring) / sizeof(msg_ring[0]);
    306 
    307 		printf(msg_ring[i].msg,	(unsigned long)msg_ring[i].arg);
    308 		sleep_till_anykey(1, 1);
    309 	    }
    310 
    311 	    lock = 0;
    312 	}
    313     }
    314     else if (orig_GC_warn_proc)
    315 	orig_GC_warn_proc(msg, arg);
    316     else
    317 	fprintf(stderr, msg, (unsigned long)arg);
    318 }
    319 
    320 #ifdef SIGCHLD
    321 static void
    322 sig_chld(int signo)
    323 {
    324     int p_stat;
    325     pid_t pid;
    326 
    327 #ifdef HAVE_WAITPID
    328     while ((pid = waitpid(-1, &p_stat, WNOHANG)) > 0)
    329 #elif HAVE_WAIT3
    330     while ((pid = wait3(&p_stat, WNOHANG, NULL)) > 0)
    331 #else
    332     if ((pid = wait(&p_stat)) > 0)
    333 #endif
    334     {
    335 	DownloadList *d;
    336 
    337 	if (WIFEXITED(p_stat)) {
    338 	    for (d = FirstDL; d != NULL; d = d->next) {
    339 		if (d->pid == pid) {
    340 		    d->err = WEXITSTATUS(p_stat);
    341 		    break;
    342 		}
    343 	    }
    344 	}
    345     }
    346     mySignal(SIGCHLD, sig_chld);
    347     return;
    348 }
    349 #endif
    350 
    351 Str
    352 make_optional_header_string(char *s)
    353 {
    354     char *p;
    355     Str hs;
    356 
    357     if (strchr(s, '\n') || strchr(s, '\r'))
    358 	return NULL;
    359     for (p = s; *p && *p != ':'; p++) ;
    360     if (*p != ':' || p == s)
    361 	return NULL;
    362     hs = Strnew_size(strlen(s) + 3);
    363     Strcopy_charp_n(hs, s, p - s);
    364     if (!Strcasecmp_charp(hs, "content-type"))
    365 	override_content_type = TRUE;
    366     Strcat_charp(hs, ": ");
    367     if (*(++p)) {		/* not null header */
    368 	SKIP_BLANKS(p);		/* skip white spaces */
    369 	Strcat_charp(hs, p);
    370     }
    371     Strcat_charp(hs, "\r\n");
    372     return hs;
    373 }
    374 
    375 int
    376 main(int argc, char **argv, char **envp)
    377 {
    378     Buffer *newbuf = NULL;
    379     char *p, c;
    380     int i;
    381     InputStream redin;
    382     char *line_str = NULL;
    383     char **load_argv;
    384     FormList *request;
    385     int load_argc = 0;
    386     int load_bookmark = FALSE;
    387     int visual_start = FALSE;
    388     int open_new_tab = FALSE;
    389     char search_header = FALSE;
    390     char *default_type = NULL;
    391     char *post_file = NULL;
    392     Str err_msg;
    393 #ifdef USE_M17N
    394     char *Locale = NULL;
    395     wc_uint8 auto_detect;
    396 #ifdef __EMX__
    397     wc_ces CodePage;
    398 #endif
    399 #endif
    400     GC_INIT();
    401 #if defined(ENABLE_NLS) || (defined(USE_M17N) && defined(HAVE_LANGINFO_CODESET))
    402     setlocale(LC_ALL, "");
    403 #endif
    404 #ifdef ENABLE_NLS
    405     bindtextdomain(PACKAGE, LOCALEDIR);
    406     textdomain(PACKAGE);
    407 #endif
    408 
    409 #ifndef HAVE_SYS_ERRLIST
    410     prepare_sys_errlist();
    411 #endif				/* not HAVE_SYS_ERRLIST */
    412 
    413     NO_proxy_domains = newTextList();
    414     fileToDelete = newTextList();
    415 
    416     load_argv = New_N(char *, argc - 1);
    417     load_argc = 0;
    418 
    419     CurrentDir = currentdir();
    420     CurrentPid = (int)getpid();
    421     BookmarkFile = NULL;
    422     config_file = NULL;
    423 
    424     /* argument search 1 */
    425     for (i = 1; i < argc; i++) {
    426 	if (*argv[i] == '-') {
    427 	    if (!strcmp("-config", argv[i])) {
    428 		argv[i] = "-dummy";
    429 		if (++i >= argc)
    430 		    usage();
    431 		config_file = argv[i];
    432 		argv[i] = "-dummy";
    433 	    }
    434 	    else if (!strcmp("-h", argv[i]) || !strcmp("-help", argv[i]))
    435 		help();
    436 	    else if (!strcmp("-V", argv[i]) || !strcmp("-version", argv[i])) {
    437 		fversion(stdout);
    438 		exit(0);
    439 	    }
    440 	}
    441     }
    442 
    443 #ifdef USE_M17N
    444     if (non_null(Locale = getenv("LC_ALL")) ||
    445 	non_null(Locale = getenv("LC_CTYPE")) ||
    446 	non_null(Locale = getenv("LANG"))) {
    447 	DisplayCharset = wc_guess_locale_charset(Locale, DisplayCharset);
    448 	DocumentCharset = wc_guess_locale_charset(Locale, DocumentCharset);
    449 	SystemCharset = wc_guess_locale_charset(Locale, SystemCharset);
    450     }
    451 #ifdef __EMX__
    452     CodePage = wc_guess_charset(getCodePage(), 0);
    453     if (CodePage)
    454 	DisplayCharset = DocumentCharset = SystemCharset = CodePage;
    455 #endif
    456 #endif
    457 
    458     /* initializations */
    459     init_rc();
    460 
    461     LoadHist = newHist();
    462     SaveHist = newHist();
    463     ShellHist = newHist();
    464     TextHist = newHist();
    465     URLHist = newHist();
    466 
    467 #ifdef USE_M17N
    468     if (FollowLocale && Locale) {
    469 	DisplayCharset = wc_guess_locale_charset(Locale, DisplayCharset);
    470 	SystemCharset = wc_guess_locale_charset(Locale, SystemCharset);
    471     }
    472     auto_detect = WcOption.auto_detect;
    473     BookmarkCharset = DocumentCharset;
    474 #endif
    475 
    476     if (!non_null(HTTP_proxy) &&
    477 	((p = getenv("HTTP_PROXY")) ||
    478 	 (p = getenv("http_proxy")) || (p = getenv("HTTP_proxy"))))
    479 	HTTP_proxy = p;
    480 #ifdef USE_SSL
    481     if (!non_null(HTTPS_proxy) &&
    482 	((p = getenv("HTTPS_PROXY")) ||
    483 	 (p = getenv("https_proxy")) || (p = getenv("HTTPS_proxy"))))
    484 	HTTPS_proxy = p;
    485     if (HTTPS_proxy == NULL && non_null(HTTP_proxy))
    486 	HTTPS_proxy = HTTP_proxy;
    487 #endif				/* USE_SSL */
    488 #ifdef USE_GOPHER
    489     if (!non_null(GOPHER_proxy) &&
    490 	((p = getenv("GOPHER_PROXY")) ||
    491 	 (p = getenv("gopher_proxy")) || (p = getenv("GOPHER_proxy"))))
    492 	GOPHER_proxy = p;
    493 #endif				/* USE_GOPHER */
    494     if (!non_null(FTP_proxy) &&
    495 	((p = getenv("FTP_PROXY")) ||
    496 	 (p = getenv("ftp_proxy")) || (p = getenv("FTP_proxy"))))
    497 	FTP_proxy = p;
    498     if (!non_null(NO_proxy) &&
    499 	((p = getenv("NO_PROXY")) ||
    500 	 (p = getenv("no_proxy")) || (p = getenv("NO_proxy"))))
    501 	NO_proxy = p;
    502 #ifdef USE_NNTP
    503     if (!non_null(NNTP_server) && (p = getenv("NNTPSERVER")) != NULL)
    504 	NNTP_server = p;
    505     if (!non_null(NNTP_mode) && (p = getenv("NNTPMODE")) != NULL)
    506 	NNTP_mode = p;
    507 #endif
    508 
    509     if (!non_null(Editor) && (p = getenv("EDITOR")) != NULL)
    510 	Editor = p;
    511     if (!non_null(Mailer) && (p = getenv("MAILER")) != NULL)
    512 	Mailer = p;
    513 
    514     /* argument search 2 */
    515     i = 1;
    516     while (i < argc) {
    517 	if (*argv[i] == '-') {
    518 	    if (!strcmp("-t", argv[i])) {
    519 		if (++i >= argc)
    520 		    usage();
    521 		if (atoi(argv[i]) > 0)
    522 		    Tabstop = atoi(argv[i]);
    523 	    }
    524 	    else if (!strcmp("-r", argv[i]))
    525 		ShowEffect = FALSE;
    526 	    else if (!strcmp("-l", argv[i])) {
    527 		if (++i >= argc)
    528 		    usage();
    529 		if (atoi(argv[i]) > 0)
    530 		    PagerMax = atoi(argv[i]);
    531 	    }
    532 #ifdef USE_M17N
    533 	    else if (!strcmp("-s", argv[i]))
    534 		DisplayCharset = WC_CES_SHIFT_JIS;
    535 	    else if (!strcmp("-j", argv[i]))
    536 		DisplayCharset = WC_CES_ISO_2022_JP;
    537 	    else if (!strcmp("-e", argv[i]))
    538 		DisplayCharset = WC_CES_EUC_JP;
    539 	    else if (!strncmp("-I", argv[i], 2)) {
    540 		if (argv[i][2] != '\0')
    541 		    p = argv[i] + 2;
    542 		else {
    543 		    if (++i >= argc)
    544 			usage();
    545 		    p = argv[i];
    546 		}
    547 		DocumentCharset = wc_guess_charset_short(p, DocumentCharset);
    548 		WcOption.auto_detect = WC_OPT_DETECT_OFF;
    549 		UseContentCharset = FALSE;
    550 	    }
    551 	    else if (!strncmp("-O", argv[i], 2)) {
    552 		if (argv[i][2] != '\0')
    553 		    p = argv[i] + 2;
    554 		else {
    555 		    if (++i >= argc)
    556 			usage();
    557 		    p = argv[i];
    558 		}
    559 		DisplayCharset = wc_guess_charset_short(p, DisplayCharset);
    560 	    }
    561 #endif
    562 	    else if (!strcmp("-graph", argv[i]))
    563 		UseGraphicChar = GRAPHIC_CHAR_DEC;
    564 	    else if (!strcmp("-no-graph", argv[i]))
    565 		UseGraphicChar = GRAPHIC_CHAR_ASCII;
    566 	    else if (!strcmp("-T", argv[i])) {
    567 		if (++i >= argc)
    568 		    usage();
    569 		DefaultType = default_type = argv[i];
    570 	    }
    571 	    else if (!strcmp("-m", argv[i]))
    572 		SearchHeader = search_header = TRUE;
    573 	    else if (!strcmp("-v", argv[i]))
    574 		visual_start = TRUE;
    575 	    else if (!strcmp("-N", argv[i]))
    576 		open_new_tab = TRUE;
    577 #ifdef USE_COLOR
    578 	    else if (!strcmp("-M", argv[i]))
    579 		useColor = FALSE;
    580 #endif				/* USE_COLOR */
    581 	    else if (!strcmp("-B", argv[i]))
    582 		load_bookmark = TRUE;
    583 	    else if (!strcmp("-bookmark", argv[i])) {
    584 		if (++i >= argc)
    585 		    usage();
    586 		BookmarkFile = argv[i];
    587 		if (BookmarkFile[0] != '~' && BookmarkFile[0] != '/') {
    588 		    Str tmp = Strnew_charp(CurrentDir);
    589 		    if (Strlastchar(tmp) != '/')
    590 			Strcat_char(tmp, '/');
    591 		    Strcat_charp(tmp, BookmarkFile);
    592 		    BookmarkFile = cleanupName(tmp->ptr);
    593 		}
    594 	    }
    595 	    else if (!strcmp("-F", argv[i]))
    596 		RenderFrame = TRUE;
    597 	    else if (!strcmp("-W", argv[i])) {
    598 		if (WrapDefault)
    599 		    WrapDefault = FALSE;
    600 		else
    601 		    WrapDefault = TRUE;
    602 	    }
    603 	    else if (!strcmp("-dump", argv[i]))
    604 		w3m_dump = DUMP_BUFFER;
    605 	    else if (!strcmp("-dump_source", argv[i]))
    606 		w3m_dump = DUMP_SOURCE;
    607 	    else if (!strcmp("-dump_head", argv[i]))
    608 		w3m_dump = DUMP_HEAD;
    609 	    else if (!strcmp("-dump_both", argv[i]))
    610 		w3m_dump = (DUMP_HEAD | DUMP_SOURCE);
    611 	    else if (!strcmp("-dump_extra", argv[i]))
    612 		w3m_dump = (DUMP_HEAD | DUMP_SOURCE | DUMP_EXTRA);
    613 	    else if (!strcmp("-halfdump", argv[i]))
    614 		w3m_dump = DUMP_HALFDUMP;
    615 	    else if (!strcmp("-halfload", argv[i])) {
    616 		w3m_dump = 0;
    617 		w3m_halfload = TRUE;
    618 		DefaultType = default_type = "text/html";
    619 	    }
    620 	    else if (!strcmp("-backend", argv[i])) {
    621 		w3m_backend = TRUE;
    622 	    }
    623 	    else if (!strcmp("-backend_batch", argv[i])) {
    624 		w3m_backend = TRUE;
    625 		if (++i >= argc)
    626 		    usage();
    627 		if (!backend_batch_commands)
    628 		    backend_batch_commands = newTextList();
    629 		pushText(backend_batch_commands, argv[i]);
    630 	    }
    631 	    else if (!strcmp("-cols", argv[i])) {
    632 		if (++i >= argc)
    633 		    usage();
    634 		COLS = atoi(argv[i]);
    635 		if (COLS > MAXIMUM_COLS) {
    636 		    COLS = MAXIMUM_COLS;
    637 		}
    638 	    }
    639 	    else if (!strcmp("-ppc", argv[i])) {
    640 		double ppc;
    641 		if (++i >= argc)
    642 		    usage();
    643 		ppc = atof(argv[i]);
    644 		if (ppc >= MINIMUM_PIXEL_PER_CHAR &&
    645 		    ppc <= MAXIMUM_PIXEL_PER_CHAR) {
    646 		    pixel_per_char = ppc;
    647 		    set_pixel_per_char = TRUE;
    648 		}
    649 	    }
    650 #ifdef USE_IMAGE
    651 	    else if (!strcmp("-ppl", argv[i])) {
    652 		double ppc;
    653 		if (++i >= argc)
    654 		    usage();
    655 		ppc = atof(argv[i]);
    656 		if (ppc >= MINIMUM_PIXEL_PER_CHAR &&
    657 		    ppc <= MAXIMUM_PIXEL_PER_CHAR * 2) {
    658 		    pixel_per_line = ppc;
    659 		    set_pixel_per_line = TRUE;
    660 		}
    661 	    }
    662 #endif
    663 	    else if (!strcmp("-num", argv[i]))
    664 		showLineNum = TRUE;
    665 	    else if (!strcmp("-no-proxy", argv[i]))
    666 		use_proxy = FALSE;
    667 #ifdef INET6
    668 	    else if (!strcmp("-4", argv[i]) || !strcmp("-6", argv[i]))
    669 		set_param_option(Sprintf("dns_order=%c", argv[i][1])->ptr);
    670 #endif
    671 	    else if (!strcmp("-post", argv[i])) {
    672 		if (++i >= argc)
    673 		    usage();
    674 		post_file = argv[i];
    675 	    }
    676 	    else if (!strcmp("-header", argv[i])) {
    677 		Str hs;
    678 		if (++i >= argc)
    679 		    usage();
    680 		if ((hs = make_optional_header_string(argv[i])) != NULL) {
    681 		    if (header_string == NULL)
    682 			header_string = hs;
    683 		    else
    684 			Strcat(header_string, hs);
    685 		}
    686 		while (argv[i][0]) {
    687 		    argv[i][0] = '\0';
    688 		    argv[i]++;
    689 		}
    690 	    }
    691 #ifdef USE_MOUSE
    692 	    else if (!strcmp("-no-mouse", argv[i])) {
    693 		use_mouse = FALSE;
    694 	    }
    695 #endif				/* USE_MOUSE */
    696 #ifdef USE_COOKIE
    697 	    else if (!strcmp("-no-cookie", argv[i])) {
    698 		use_cookie = FALSE;
    699 		accept_cookie = FALSE;
    700 	    }
    701 	    else if (!strcmp("-cookie", argv[i])) {
    702 		use_cookie = TRUE;
    703 		accept_cookie = TRUE;
    704 	    }
    705 #endif				/* USE_COOKIE */
    706 	    else if (!strcmp("-S", argv[i]))
    707 		squeezeBlankLine = TRUE;
    708 	    else if (!strcmp("-X", argv[i]))
    709 		Do_not_use_ti_te = TRUE;
    710 	    else if (!strcmp("-title", argv[i]))
    711 		displayTitleTerm = getenv("TERM");
    712 	    else if (!strncmp("-title=", argv[i], 7))
    713 		displayTitleTerm = argv[i] + 7;
    714 	    else if (!strcmp("-o", argv[i]) ||
    715 		     !strcmp("-show-option", argv[i])) {
    716 		if (!strcmp("-show-option", argv[i]) || ++i >= argc ||
    717 		    !strcmp(argv[i], "?")) {
    718 		    show_params(stdout);
    719 		    exit(0);
    720 		}
    721 		if (!set_param_option(argv[i])) {
    722 		    /* option set failed */
    723 		    /* FIXME: gettextize? */
    724 		    fprintf(stderr, "%s: bad option\n", argv[i]);
    725 		    show_params_p = 1;
    726 		    usage();
    727 		}
    728 	    }
    729 	    else if (!strcmp("-dummy", argv[i])) {
    730 		/* do nothing */
    731 	    }
    732 	    else if (!strcmp("-debug", argv[i])) {
    733 		w3m_debug = TRUE;
    734 	    }
    735 	    else if (!strcmp("-reqlog",argv[i])) {
    736 		w3m_reqlog=rcFile("request.log");
    737 	    }
    738 	    else {
    739 		usage();
    740 	    }
    741 	}
    742 	else if (*argv[i] == '+') {
    743 	    line_str = argv[i] + 1;
    744 	}
    745 	else {
    746 	    load_argv[load_argc++] = argv[i];
    747 	}
    748 	i++;
    749     }
    750 
    751 #ifdef	__WATT32__
    752     if (w3m_debug)
    753 	dbug_init();
    754     sock_init();
    755 #endif
    756 
    757 #ifdef __MINGW32_VERSION
    758     {
    759       int err;
    760       WORD wVerReq;
    761 
    762       wVerReq = MAKEWORD(1, 1);
    763 
    764       err = WSAStartup(wVerReq, &WSAData);
    765       if (err != 0)
    766         {
    767 	  fprintf(stderr, "Can't find winsock\n");
    768 	  return 1;
    769         }
    770       _fmode = _O_BINARY;
    771     }
    772 #endif
    773 
    774     FirstTab = NULL;
    775     LastTab = NULL;
    776     nTab = 0;
    777     CurrentTab = NULL;
    778     CurrentKey = -1;
    779     if (BookmarkFile == NULL)
    780 	BookmarkFile = rcFile(BOOKMARK);
    781 
    782     if (!isatty(1) && !w3m_dump) {
    783 	/* redirected output */
    784 	w3m_dump = DUMP_BUFFER;
    785     }
    786     if (w3m_dump) {
    787 	if (COLS == 0)
    788 	    COLS = DEFAULT_COLS;
    789     }
    790 
    791 #ifdef USE_BINMODE_STREAM
    792     setmode(fileno(stdout), O_BINARY);
    793 #endif
    794     if (!w3m_dump && !w3m_backend) {
    795 	fmInit();
    796 #ifdef SIGWINCH
    797 	mySignal(SIGWINCH, resize_hook);
    798 #else				/* not SIGWINCH */
    799 	setlinescols();
    800 	setupscreen();
    801 #endif				/* not SIGWINCH */
    802     }
    803 #ifdef USE_IMAGE
    804     else if (w3m_halfdump && displayImage)
    805 	activeImage = TRUE;
    806 #endif
    807 
    808     sync_with_option();
    809 #ifdef USE_COOKIE
    810     initCookie();
    811 #endif				/* USE_COOKIE */
    812 #ifdef USE_HISTORY
    813     if (UseHistory)
    814 	loadHistory(URLHist);
    815 #endif				/* not USE_HISTORY */
    816 
    817 #ifdef USE_M17N
    818     wtf_init(DocumentCharset, DisplayCharset);
    819     /*  if (w3m_dump)
    820      *    WcOption.pre_conv = WC_TRUE; 
    821      */
    822 #endif
    823 
    824     if (w3m_backend)
    825 	backend();
    826 
    827     if (w3m_dump)
    828 	mySignal(SIGINT, SIG_IGN);
    829 #ifdef SIGCHLD
    830     mySignal(SIGCHLD, sig_chld);
    831 #endif
    832 #ifdef SIGPIPE
    833     mySignal(SIGPIPE, SigPipe);
    834 #endif
    835 
    836     orig_GC_warn_proc = GC_set_warn_proc(wrap_GC_warn_proc);
    837     err_msg = Strnew();
    838     if (load_argc == 0) {
    839 	/* no URL specified */
    840 	if (!isatty(0)) {
    841 	    redin = newFileStream(fdopen(dup(0), "rb"), (void (*)())pclose);
    842 	    newbuf = openGeneralPagerBuffer(redin);
    843 	    dup2(1, 0);
    844 	}
    845 	else if (load_bookmark) {
    846 	    newbuf = loadGeneralFile(BookmarkFile, NULL, NO_REFERER, 0, NULL);
    847 	    if (newbuf == NULL)
    848 		Strcat_charp(err_msg, "w3m: Can't load bookmark.\n");
    849 	}
    850 	else if (visual_start) {
    851 	    /* FIXME: gettextize? */
    852 	    Str s_page;
    853 	    s_page =
    854 		Strnew_charp
    855 		("<title>W3M startup page</title><center><b>Welcome to ");
    856 	    Strcat_charp(s_page, "<a href='http://w3m.sourceforge.net/'>");
    857 	    Strcat_m_charp(s_page,
    858 			   "w3m</a>!<p><p>This is w3m version ",
    859 			   w3m_version,
    860 			   "<br>Written by <a href='mailto:aito@fw.ipsj.or.jp'>Akinori Ito</a>",
    861 			   NULL);
    862 	    newbuf = loadHTMLString(s_page);
    863 	    if (newbuf == NULL)
    864 		Strcat_charp(err_msg, "w3m: Can't load string.\n");
    865 	    else if (newbuf != NO_BUFFER)
    866 		newbuf->bufferprop |= (BP_INTERNAL | BP_NO_URL);
    867 	}
    868 	else if ((p = getenv("HTTP_HOME")) != NULL ||
    869 		 (p = getenv("WWW_HOME")) != NULL) {
    870 	    newbuf = loadGeneralFile(p, NULL, NO_REFERER, 0, NULL);
    871 	    if (newbuf == NULL)
    872 		Strcat(err_msg, Sprintf("w3m: Can't load %s.\n", p));
    873 	    else if (newbuf != NO_BUFFER)
    874 		pushHashHist(URLHist, parsedURL2Str(&newbuf->currentURL)->ptr);
    875 	}
    876 	else {
    877 	    if (fmInitialized)
    878 		fmTerm();
    879 	    usage();
    880 	}
    881 	if (newbuf == NULL) {
    882 	    if (fmInitialized)
    883 		fmTerm();
    884 	    if (err_msg->length)
    885 		fprintf(stderr, "%s", err_msg->ptr);
    886 	    w3m_exit(2);
    887 	}
    888 	i = -1;
    889     }
    890     else {
    891 	i = 0;
    892     }
    893     for (; i < load_argc; i++) {
    894 	if (i >= 0) {
    895 	    SearchHeader = search_header;
    896 	    DefaultType = default_type;
    897 	    if (w3m_dump == DUMP_HEAD) {
    898 		request = New(FormList);
    899 		request->method = FORM_METHOD_HEAD;
    900 		newbuf =
    901 		    loadGeneralFile(load_argv[i], NULL, NO_REFERER, 0,
    902 				    request);
    903 	    }
    904 	    else {
    905 		if (post_file && i == 0) {
    906 		    FILE *fp;
    907 		    Str body;
    908 		    if (!strcmp(post_file, "-"))
    909 			fp = stdin;
    910 		    else
    911 			fp = fopen(post_file, "r");
    912 		    if (fp == NULL) {
    913 			/* FIXME: gettextize? */
    914 			Strcat(err_msg,
    915 			       Sprintf("w3m: Can't open %s.\n", post_file));
    916 			continue;
    917 		    }
    918 		    body = Strfgetall(fp);
    919 		    if (fp != stdin)
    920 			fclose(fp);
    921 		    request =
    922 			newFormList(NULL, "post", NULL, NULL, NULL, NULL,
    923 				    NULL);
    924 		    request->body = body->ptr;
    925 		    request->boundary = NULL;
    926 		    request->length = body->length;
    927 		}
    928 		else {
    929 		    request = NULL;
    930 		}
    931 		newbuf =
    932 		    loadGeneralFile(load_argv[i], NULL, NO_REFERER, 0,
    933 				    request);
    934 	    }
    935 	    if (newbuf == NULL) {
    936 		/* FIXME: gettextize? */
    937 		Strcat(err_msg,
    938 		       Sprintf("w3m: Can't load %s.\n", load_argv[i]));
    939 		continue;
    940 	    }
    941 	    else if (newbuf == NO_BUFFER)
    942 		continue;
    943 	    switch (newbuf->real_scheme) {
    944 	    case SCM_MAILTO:
    945 		break;
    946 	    case SCM_LOCAL:
    947 	    case SCM_LOCAL_CGI:
    948 		unshiftHist(LoadHist, conv_from_system(load_argv[i]));
    949 	    default:
    950 		pushHashHist(URLHist, parsedURL2Str(&newbuf->currentURL)->ptr);
    951 		break;
    952 	    }
    953 	}
    954 	else if (newbuf == NO_BUFFER)
    955 	    continue;
    956 	if (newbuf->pagerSource ||
    957 	    (newbuf->real_scheme == SCM_LOCAL && newbuf->header_source &&
    958 	     newbuf->currentURL.file && strcmp(newbuf->currentURL.file, "-")))
    959 	    newbuf->search_header = search_header;
    960 	if (CurrentTab == NULL) {
    961 	    FirstTab = LastTab = CurrentTab = newTab();
    962 	    nTab = 1;
    963 	    Firstbuf = Currentbuf = newbuf;
    964 	}
    965 	else if (open_new_tab) {
    966 	    _newT();
    967 	    Currentbuf->nextBuffer = newbuf;
    968 	    delBuffer(Currentbuf);
    969 	}
    970 	else {
    971 	    Currentbuf->nextBuffer = newbuf;
    972 	    Currentbuf = newbuf;
    973 	}
    974 	if (!w3m_dump || w3m_dump == DUMP_BUFFER) {
    975 	    if (Currentbuf->frameset != NULL && RenderFrame)
    976 		rFrame();
    977 	}
    978 	if (w3m_dump)
    979 	    do_dump(Currentbuf);
    980 	else {
    981 	    Currentbuf = newbuf;
    982 #ifdef USE_BUFINFO
    983 	    saveBufferInfo();
    984 #endif
    985 	}
    986     }
    987     if (w3m_dump) {
    988 	if (err_msg->length)
    989 	    fprintf(stderr, "%s", err_msg->ptr);
    990 #ifdef USE_COOKIE
    991 	save_cookies();
    992 #endif				/* USE_COOKIE */
    993 	w3m_exit(0);
    994     }
    995 
    996     if (add_download_list) {
    997 	add_download_list = FALSE;
    998 	CurrentTab = LastTab;
    999 	if (!FirstTab) {
   1000 	    FirstTab = LastTab = CurrentTab = newTab();
   1001 	    nTab = 1;
   1002 	}
   1003 	if (!Firstbuf || Firstbuf == NO_BUFFER) {
   1004 	    Firstbuf = Currentbuf = newBuffer(INIT_BUFFER_WIDTH);
   1005 	    Currentbuf->bufferprop = BP_INTERNAL | BP_NO_URL;
   1006 	    Currentbuf->buffername = DOWNLOAD_LIST_TITLE;
   1007 	}
   1008 	else
   1009 	    Currentbuf = Firstbuf;
   1010 	ldDL();
   1011     }
   1012     else
   1013 	CurrentTab = FirstTab;
   1014     if (!FirstTab || !Firstbuf || Firstbuf == NO_BUFFER) {
   1015 	if (newbuf == NO_BUFFER) {
   1016 	    if (fmInitialized)
   1017 		/* FIXME: gettextize? */
   1018 		inputChar("Hit any key to quit w3m:");
   1019 	}
   1020 	if (fmInitialized)
   1021 	    fmTerm();
   1022 	if (err_msg->length)
   1023 	    fprintf(stderr, "%s", err_msg->ptr);
   1024 	if (newbuf == NO_BUFFER) {
   1025 #ifdef USE_COOKIE
   1026 	    save_cookies();
   1027 #endif				/* USE_COOKIE */
   1028 	    if (!err_msg->length)
   1029 		w3m_exit(0);
   1030 	}
   1031 	w3m_exit(2);
   1032     }
   1033     if (err_msg->length)
   1034 	disp_message_nsec(err_msg->ptr, FALSE, 1, TRUE, FALSE);
   1035 
   1036     SearchHeader = FALSE;
   1037     DefaultType = NULL;
   1038 #ifdef USE_M17N
   1039     UseContentCharset = TRUE;
   1040     WcOption.auto_detect = auto_detect;
   1041 #endif
   1042 
   1043     Currentbuf = Firstbuf;
   1044     displayBuffer(Currentbuf, B_FORCE_REDRAW);
   1045     if (line_str) {
   1046 	_goLine(line_str);
   1047     }
   1048     for (;;) {
   1049 	if (add_download_list) {
   1050 	    add_download_list = FALSE;
   1051 	    ldDL();
   1052 	}
   1053 	if (Currentbuf->submit) {
   1054 	    Anchor *a = Currentbuf->submit;
   1055 	    Currentbuf->submit = NULL;
   1056 	    gotoLine(Currentbuf, a->start.line);
   1057 	    Currentbuf->pos = a->start.pos;
   1058 	    _followForm(TRUE);
   1059 	    continue;
   1060 	}
   1061 	/* event processing */
   1062 	if (CurrentEvent) {
   1063 	    CurrentKey = -1;
   1064 	    CurrentKeyData = NULL;
   1065 	    CurrentCmdData = (char *)CurrentEvent->data;
   1066 	    w3mFuncList[CurrentEvent->cmd].func();
   1067 	    CurrentCmdData = NULL;
   1068 	    CurrentEvent = CurrentEvent->next;
   1069 	    continue;
   1070 	}
   1071 	/* get keypress event */
   1072 #ifdef USE_ALARM
   1073 	if (Currentbuf->event) {
   1074 	    if (Currentbuf->event->status != AL_UNSET) {
   1075 		CurrentAlarm = Currentbuf->event;
   1076 		if (CurrentAlarm->sec == 0) {	/* refresh (0sec) */
   1077 		    Currentbuf->event = NULL;
   1078 		    CurrentKey = -1;
   1079 		    CurrentKeyData = NULL;
   1080 		    CurrentCmdData = (char *)CurrentAlarm->data;
   1081 		    w3mFuncList[CurrentAlarm->cmd].func();
   1082 		    CurrentCmdData = NULL;
   1083 		    continue;
   1084 		}
   1085 	    }
   1086 	    else
   1087 		Currentbuf->event = NULL;
   1088 	}
   1089 	if (!Currentbuf->event)
   1090 	    CurrentAlarm = &DefaultAlarm;
   1091 #endif
   1092 #ifdef USE_MOUSE
   1093 	mouse_action.in_action = FALSE;
   1094 	if (use_mouse)
   1095 	    mouse_active();
   1096 #endif				/* USE_MOUSE */
   1097 #ifdef USE_ALARM
   1098 	if (CurrentAlarm->sec > 0) {
   1099 	    mySignal(SIGALRM, SigAlarm);
   1100 	    alarm(CurrentAlarm->sec);
   1101 	}
   1102 #endif
   1103 #ifdef SIGWINCH
   1104 	mySignal(SIGWINCH, resize_hook);
   1105 #endif
   1106 #ifdef USE_IMAGE
   1107 	if (activeImage && displayImage && Currentbuf->img &&
   1108 	    !Currentbuf->image_loaded) {
   1109 	    do {
   1110 #ifdef SIGWINCH
   1111 		if (need_resize_screen)
   1112 		    resize_screen();
   1113 #endif
   1114 		loadImage(Currentbuf, IMG_FLAG_NEXT);
   1115 	    } while (sleep_till_anykey(1, 0) <= 0);
   1116 	}
   1117 #ifdef SIGWINCH
   1118 	else
   1119 #endif
   1120 #endif
   1121 #ifdef SIGWINCH
   1122 	{
   1123 	    do {
   1124 		if (need_resize_screen)
   1125 		    resize_screen();
   1126 	    } while (sleep_till_anykey(1, 0) <= 0);
   1127 	}
   1128 #endif
   1129 	c = getch();
   1130 #ifdef USE_ALARM
   1131 	if (CurrentAlarm->sec > 0) {
   1132 	    alarm(0);
   1133 	}
   1134 #endif
   1135 #ifdef USE_MOUSE
   1136 	if (use_mouse)
   1137 	    mouse_inactive();
   1138 #endif				/* USE_MOUSE */
   1139 	if (IS_ASCII(c)) {	/* Ascii */
   1140 	    if (('0' <= c) && (c <= '9') &&
   1141 		(prec_num || (GlobalKeymap[c] == FUNCNAME_nulcmd))) {
   1142 		prec_num = prec_num * 10 + (int)(c - '0');
   1143 		if (prec_num > PREC_LIMIT)
   1144 		   prec_num = PREC_LIMIT;
   1145 	    }
   1146 	    else {
   1147 		set_buffer_environ(Currentbuf);
   1148 		save_buffer_position(Currentbuf);
   1149 		keyPressEventProc((int)c);
   1150 		prec_num = 0;
   1151 	    }
   1152 	}
   1153 	prev_key = CurrentKey;
   1154 	CurrentKey = -1;
   1155 	CurrentKeyData = NULL;
   1156     }
   1157 }
   1158 
   1159 static void
   1160 keyPressEventProc(int c)
   1161 {
   1162     CurrentKey = c;
   1163     w3mFuncList[(int)GlobalKeymap[c]].func();
   1164 }
   1165 
   1166 void
   1167 pushEvent(int cmd, void *data)
   1168 {
   1169     Event *event;
   1170 
   1171     event = New(Event);
   1172     event->cmd = cmd;
   1173     event->data = data;
   1174     event->next = NULL;
   1175     if (CurrentEvent)
   1176 	LastEvent->next = event;
   1177     else
   1178 	CurrentEvent = event;
   1179     LastEvent = event;
   1180 }
   1181 
   1182 static void
   1183 dump_source(Buffer *buf)
   1184 {
   1185     FILE *f;
   1186     char c;
   1187     if (buf->sourcefile == NULL)
   1188 	return;
   1189     f = fopen(buf->sourcefile, "r");
   1190     if (f == NULL)
   1191 	return;
   1192     while (c = fgetc(f), !feof(f)) {
   1193 	putchar(c);
   1194     }
   1195     fclose(f);
   1196 }
   1197 
   1198 static void
   1199 dump_head(Buffer *buf)
   1200 {
   1201     TextListItem *ti;
   1202 
   1203     if (buf->document_header == NULL) {
   1204 	if (w3m_dump & DUMP_EXTRA)
   1205 	    printf("\n");
   1206 	return;
   1207     }
   1208     for (ti = buf->document_header->first; ti; ti = ti->next) {
   1209 #ifdef USE_M17N
   1210 	printf("%s",
   1211 	       wc_conv_strict(ti->ptr, InnerCharset,
   1212 			      buf->document_charset)->ptr);
   1213 #else
   1214 	printf("%s", ti->ptr);
   1215 #endif
   1216     }
   1217     puts("");
   1218 }
   1219 
   1220 static void
   1221 dump_extra(Buffer *buf)
   1222 {
   1223     printf("W3m-current-url: %s\n", parsedURL2Str(&buf->currentURL)->ptr);
   1224     if (buf->baseURL)
   1225 	printf("W3m-base-url: %s\n", parsedURL2Str(buf->baseURL)->ptr);
   1226 #ifdef USE_M17N
   1227     printf("W3m-document-charset: %s\n",
   1228 	   wc_ces_to_charset(buf->document_charset));
   1229 #endif
   1230 #ifdef USE_SSL
   1231     if (buf->ssl_certificate) {
   1232 	Str tmp = Strnew();
   1233 	char *p;
   1234 	for (p = buf->ssl_certificate; *p; p++) {
   1235 	    Strcat_char(tmp, *p);
   1236 	    if (*p == '\n') {
   1237 		for (; *(p + 1) == '\n'; p++) ;
   1238 		if (*(p + 1))
   1239 		    Strcat_char(tmp, '\t');
   1240 	    }
   1241 	}
   1242 	if (Strlastchar(tmp) != '\n')
   1243 	    Strcat_char(tmp, '\n');
   1244 	printf("W3m-ssl-certificate: %s", tmp->ptr);
   1245     }
   1246 #endif
   1247 }
   1248 
   1249 static void
   1250 do_dump(Buffer *buf)
   1251 {
   1252     MySignalHandler(*volatile prevtrap) (SIGNAL_ARG) = NULL;
   1253 
   1254     prevtrap = mySignal(SIGINT, intTrap);
   1255     if (SETJMP(IntReturn) != 0) {
   1256 	mySignal(SIGINT, prevtrap);
   1257 	return;
   1258     }
   1259     if (w3m_dump & DUMP_EXTRA)
   1260 	dump_extra(buf);
   1261     if (w3m_dump & DUMP_HEAD)
   1262 	dump_head(buf);
   1263     if (w3m_dump & DUMP_SOURCE)
   1264 	dump_source(buf);
   1265     if (w3m_dump == DUMP_BUFFER) {
   1266 	int i;
   1267 	saveBuffer(buf, stdout, FALSE);
   1268 	if (displayLinkNumber && buf->href) {
   1269 	    printf("\nReferences:\n\n");
   1270 	    for (i = 0; i < buf->href->nanchor; i++) {
   1271 	        ParsedURL pu;
   1272 	        static Str s = NULL;
   1273 		if (buf->href->anchors[i].slave)
   1274 		    continue;
   1275 	        parseURL2(buf->href->anchors[i].url, &pu, baseURL(buf));
   1276 	        s = parsedURL2Str(&pu);
   1277     	        if (DecodeURL)
   1278 		    s = Strnew_charp(url_unquote_conv
   1279 				     (s->ptr, Currentbuf->document_charset));
   1280 	        printf("[%d] %s\n", buf->href->anchors[i].hseq + 1, s->ptr);
   1281 	    }
   1282 	}
   1283     }
   1284     mySignal(SIGINT, prevtrap);
   1285 }
   1286 
   1287 DEFUN(nulcmd, NOTHING NULL @@@, "Do nothing")
   1288 {				/* do nothing */
   1289 }
   1290 
   1291 #ifdef __EMX__
   1292 DEFUN(pcmap, PCMAP, "pcmap")
   1293 {
   1294     w3mFuncList[(int)PcKeymap[(int)getch()]].func();
   1295 }
   1296 #else				/* not __EMX__ */
   1297 void
   1298 pcmap(void)
   1299 {
   1300 }
   1301 #endif
   1302 
   1303 static void
   1304 escKeyProc(int c, int esc, unsigned char *map)
   1305 {
   1306     if (CurrentKey >= 0 && CurrentKey & K_MULTI) {
   1307 	unsigned char **mmap;
   1308 	mmap = (unsigned char **)getKeyData(MULTI_KEY(CurrentKey));
   1309 	if (!mmap)
   1310 	    return;
   1311 	switch (esc) {
   1312 	case K_ESCD:
   1313 	    map = mmap[3];
   1314 	    break;
   1315 	case K_ESCB:
   1316 	    map = mmap[2];
   1317 	    break;
   1318 	case K_ESC:
   1319 	    map = mmap[1];
   1320 	    break;
   1321 	default:
   1322 	    map = mmap[0];
   1323 	    break;
   1324 	}
   1325 	esc |= (CurrentKey & ~0xFFFF);
   1326     }
   1327     CurrentKey = esc | c;
   1328     w3mFuncList[(int)map[c]].func();
   1329 }
   1330 
   1331 DEFUN(escmap, ESCMAP, "ESC map")
   1332 {
   1333     char c;
   1334     c = getch();
   1335     if (IS_ASCII(c))
   1336 	escKeyProc((int)c, K_ESC, EscKeymap);
   1337 }
   1338 
   1339 DEFUN(escbmap, ESCBMAP, "ESC [ map")
   1340 {
   1341     char c;
   1342     c = getch();
   1343     if (IS_DIGIT(c)) {
   1344 	escdmap(c);
   1345 	return;
   1346     }
   1347     if (IS_ASCII(c))
   1348 	escKeyProc((int)c, K_ESCB, EscBKeymap);
   1349 }
   1350 
   1351 void
   1352 escdmap(char c)
   1353 {
   1354     int d;
   1355     d = (int)c - (int)'0';
   1356     c = getch();
   1357     if (IS_DIGIT(c)) {
   1358 	d = d * 10 + (int)c - (int)'0';
   1359 	c = getch();
   1360     }
   1361     if (c == '~')
   1362 	escKeyProc((int)d, K_ESCD, EscDKeymap);
   1363 }
   1364 
   1365 DEFUN(multimap, MULTIMAP, "multimap")
   1366 {
   1367     char c;
   1368     c = getch();
   1369     if (IS_ASCII(c)) {
   1370 	CurrentKey = K_MULTI | (CurrentKey << 16) | c;
   1371 	escKeyProc((int)c, 0, NULL);
   1372     }
   1373 }
   1374 
   1375 void
   1376 tmpClearBuffer(Buffer *buf)
   1377 {
   1378     if (buf->pagerSource == NULL && writeBufferCache(buf) == 0) {
   1379 	buf->firstLine = NULL;
   1380 	buf->topLine = NULL;
   1381 	buf->currentLine = NULL;
   1382 	buf->lastLine = NULL;
   1383     }
   1384 }
   1385 
   1386 static Str currentURL(void);
   1387 
   1388 #ifdef USE_BUFINFO
   1389 void
   1390 saveBufferInfo()
   1391 {
   1392     FILE *fp;
   1393 
   1394     if (w3m_dump)
   1395 	return;
   1396     if ((fp = fopen(rcFile("bufinfo"), "w")) == NULL) {
   1397 	return;
   1398     }
   1399     fprintf(fp, "%s\n", currentURL()->ptr);
   1400     fclose(fp);
   1401 }
   1402 #endif
   1403 
   1404 static void
   1405 pushBuffer(Buffer *buf)
   1406 {
   1407     Buffer *b;
   1408 
   1409 #ifdef USE_IMAGE
   1410     deleteImage(Currentbuf);
   1411 #endif
   1412     if (clear_buffer)
   1413 	tmpClearBuffer(Currentbuf);
   1414     if (Firstbuf == Currentbuf) {
   1415 	buf->nextBuffer = Firstbuf;
   1416 	Firstbuf = Currentbuf = buf;
   1417     }
   1418     else if ((b = prevBuffer(Firstbuf, Currentbuf)) != NULL) {
   1419 	b->nextBuffer = buf;
   1420 	buf->nextBuffer = Currentbuf;
   1421 	Currentbuf = buf;
   1422     }
   1423 #ifdef USE_BUFINFO
   1424     saveBufferInfo();
   1425 #endif
   1426 
   1427 }
   1428 
   1429 static void
   1430 delBuffer(Buffer *buf)
   1431 {
   1432     if (buf == NULL)
   1433 	return;
   1434     if (Currentbuf == buf)
   1435 	Currentbuf = buf->nextBuffer;
   1436     Firstbuf = deleteBuffer(Firstbuf, buf);
   1437     if (!Currentbuf)
   1438 	Currentbuf = Firstbuf;
   1439 }
   1440 
   1441 static void
   1442 repBuffer(Buffer *oldbuf, Buffer *buf)
   1443 {
   1444     Firstbuf = replaceBuffer(Firstbuf, oldbuf, buf);
   1445     Currentbuf = buf;
   1446 }
   1447 
   1448 
   1449 MySignalHandler
   1450 intTrap(SIGNAL_ARG)
   1451 {				/* Interrupt catcher */
   1452     LONGJMP(IntReturn, 0);
   1453     SIGNAL_RETURN;
   1454 }
   1455 
   1456 #ifdef SIGWINCH
   1457 static MySignalHandler
   1458 resize_hook(SIGNAL_ARG)
   1459 {
   1460     need_resize_screen = TRUE;
   1461     mySignal(SIGWINCH, resize_hook);
   1462     SIGNAL_RETURN;
   1463 }
   1464 
   1465 static void
   1466 resize_screen(void)
   1467 {
   1468     need_resize_screen = FALSE;
   1469     setlinescols();
   1470     setupscreen();
   1471     if (CurrentTab)
   1472 	displayBuffer(Currentbuf, B_FORCE_REDRAW);
   1473 }
   1474 #endif				/* SIGWINCH */
   1475 
   1476 #ifdef SIGPIPE
   1477 static MySignalHandler
   1478 SigPipe(SIGNAL_ARG)
   1479 {
   1480 #ifdef USE_MIGEMO
   1481     init_migemo();
   1482 #endif
   1483     mySignal(SIGPIPE, SigPipe);
   1484     SIGNAL_RETURN;
   1485 }
   1486 #endif
   1487 
   1488 /* 
   1489  * Command functions: These functions are called with a keystroke.
   1490  */
   1491 
   1492 static void
   1493 nscroll(int n, int mode)
   1494 {
   1495     Buffer *buf = Currentbuf;
   1496     Line *top = buf->topLine, *cur = buf->currentLine;
   1497     int lnum, tlnum, llnum, diff_n;
   1498 
   1499     if (buf->firstLine == NULL)
   1500 	return;
   1501     lnum = cur->linenumber;
   1502     buf->topLine = lineSkip(buf, top, n, FALSE);
   1503     if (buf->topLine == top) {
   1504 	lnum += n;
   1505 	if (lnum < buf->topLine->linenumber)
   1506 	    lnum = buf->topLine->linenumber;
   1507 	else if (lnum > buf->lastLine->linenumber)
   1508 	    lnum = buf->lastLine->linenumber;
   1509     }
   1510     else {
   1511 	tlnum = buf->topLine->linenumber;
   1512 	llnum = buf->topLine->linenumber + buf->LINES - 1;
   1513 	if (nextpage_topline)
   1514 	    diff_n = 0;
   1515 	else
   1516 	    diff_n = n - (tlnum - top->linenumber);
   1517 	if (lnum < tlnum)
   1518 	    lnum = tlnum + diff_n;
   1519 	if (lnum > llnum)
   1520 	    lnum = llnum + diff_n;
   1521     }
   1522     gotoLine(buf, lnum);
   1523     arrangeLine(buf);
   1524     if (n > 0) {
   1525 	if (buf->currentLine->bpos &&
   1526 	    buf->currentLine->bwidth >= buf->currentColumn + buf->visualpos)
   1527 	    cursorDown(buf, 1);
   1528 	else {
   1529 	    while (buf->currentLine->next && buf->currentLine->next->bpos &&
   1530 		   buf->currentLine->bwidth + buf->currentLine->width <
   1531 		   buf->currentColumn + buf->visualpos)
   1532 		cursorDown0(buf, 1);
   1533 	}
   1534     }
   1535     else {
   1536 	if (buf->currentLine->bwidth + buf->currentLine->width <
   1537 	    buf->currentColumn + buf->visualpos)
   1538 	    cursorUp(buf, 1);
   1539 	else {
   1540 	    while (buf->currentLine->prev && buf->currentLine->bpos &&
   1541 		   buf->currentLine->bwidth >=
   1542 		   buf->currentColumn + buf->visualpos)
   1543 		cursorUp0(buf, 1);
   1544 	}
   1545     }
   1546     displayBuffer(buf, mode);
   1547 }
   1548 
   1549 /* Move page forward */
   1550 DEFUN(pgFore, NEXT_PAGE, "Move to next page")
   1551 {
   1552     if (vi_prec_num)
   1553 	nscroll(searchKeyNum() * (Currentbuf->LINES - 1), B_NORMAL);
   1554     else
   1555 	nscroll(prec_num ? searchKeyNum() : searchKeyNum()
   1556 		* (Currentbuf->LINES - 1), prec_num ? B_SCROLL : B_NORMAL);
   1557 }
   1558 
   1559 /* Move page backward */
   1560 DEFUN(pgBack, PREV_PAGE, "Move to previous page")
   1561 {
   1562     if (vi_prec_num)
   1563 	nscroll(-searchKeyNum() * (Currentbuf->LINES - 1), B_NORMAL);
   1564     else
   1565 	nscroll(-(prec_num ? searchKeyNum() : searchKeyNum()
   1566 		  * (Currentbuf->LINES - 1)), prec_num ? B_SCROLL : B_NORMAL);
   1567 }
   1568 
   1569 /* 1 line up */
   1570 DEFUN(lup1, UP, "Scroll up one line")
   1571 {
   1572     nscroll(searchKeyNum(), B_SCROLL);
   1573 }
   1574 
   1575 /* 1 line down */
   1576 DEFUN(ldown1, DOWN, "Scroll down one line")
   1577 {
   1578     nscroll(-searchKeyNum(), B_SCROLL);
   1579 }
   1580 
   1581 /* move cursor position to the center of screen */
   1582 DEFUN(ctrCsrV, CENTER_V, "Move to the center column")
   1583 {
   1584     int offsety;
   1585     if (Currentbuf->firstLine == NULL)
   1586 	return;
   1587     offsety = Currentbuf->LINES / 2 - Currentbuf->cursorY;
   1588     if (offsety != 0) {
   1589 #if 0
   1590 	Currentbuf->currentLine = lineSkip(Currentbuf,
   1591 					   Currentbuf->currentLine, offsety,
   1592 					   FALSE);
   1593 #endif
   1594 	Currentbuf->topLine =
   1595 	    lineSkip(Currentbuf, Currentbuf->topLine, -offsety, FALSE);
   1596 	arrangeLine(Currentbuf);
   1597 	displayBuffer(Currentbuf, B_NORMAL);
   1598     }
   1599 }
   1600 
   1601 DEFUN(ctrCsrH, CENTER_H, "Move to the center line")
   1602 {
   1603     int offsetx;
   1604     if (Currentbuf->firstLine == NULL)
   1605 	return;
   1606     offsetx = Currentbuf->cursorX - Currentbuf->COLS / 2;
   1607     if (offsetx != 0) {
   1608 	columnSkip(Currentbuf, offsetx);
   1609 	arrangeCursor(Currentbuf);
   1610 	displayBuffer(Currentbuf, B_NORMAL);
   1611     }
   1612 }
   1613 
   1614 /* Redraw screen */
   1615 DEFUN(rdrwSc, REDRAW, "Redraw screen")
   1616 {
   1617     clear();
   1618     arrangeCursor(Currentbuf);
   1619     displayBuffer(Currentbuf, B_FORCE_REDRAW);
   1620 }
   1621 
   1622 static void
   1623 clear_mark(Line *l)
   1624 {
   1625     int pos;
   1626     if (!l)
   1627 	return;
   1628     for (pos = 0; pos < l->size; pos++)
   1629 	l->propBuf[pos] &= ~PE_MARK;
   1630 }
   1631 
   1632 /* search by regular expression */
   1633 static int
   1634 srchcore(char *volatile str, int (*func) (Buffer *, char *))
   1635 {
   1636     MySignalHandler(*prevtrap) ();
   1637     volatile int i, result = SR_NOTFOUND;
   1638 
   1639     if (str != NULL && str != SearchString)
   1640 	SearchString = str;
   1641     if (SearchString == NULL || *SearchString == '\0')
   1642 	return SR_NOTFOUND;
   1643 
   1644     str = conv_search_string(SearchString, DisplayCharset);
   1645     prevtrap = mySignal(SIGINT, intTrap);
   1646     crmode();
   1647     if (SETJMP(IntReturn) == 0) {
   1648 	for (i = 0; i < PREC_NUM; i++) {
   1649 	    result = func(Currentbuf, str);
   1650 	    if (i < PREC_NUM - 1 && result & SR_FOUND)
   1651 		clear_mark(Currentbuf->currentLine);
   1652 	}
   1653     }
   1654     mySignal(SIGINT, prevtrap);
   1655     term_raw();
   1656     return result;
   1657 }
   1658 
   1659 static void
   1660 disp_srchresult(int result, char *prompt, char *str)
   1661 {
   1662     if (str == NULL)
   1663 	str = "";
   1664     if (result & SR_NOTFOUND)
   1665 	disp_message(Sprintf("Not found: %s", str)->ptr, TRUE);
   1666     else if (result & SR_WRAPPED)
   1667 	disp_message(Sprintf("Search wrapped: %s", str)->ptr, TRUE);
   1668     else if (show_srch_str)
   1669 	disp_message(Sprintf("%s%s", prompt, str)->ptr, TRUE);
   1670 }
   1671 
   1672 static int
   1673 dispincsrch(int ch, Str buf, Lineprop *prop)
   1674 {
   1675     static Buffer sbuf;
   1676     static Line *currentLine;
   1677     static int pos;
   1678     char *str;
   1679     int do_next_search = FALSE;
   1680 
   1681     if (ch == 0 && buf == NULL) {
   1682 	SAVE_BUFPOSITION(&sbuf);	/* search starting point */
   1683 	currentLine = sbuf.currentLine;
   1684 	pos = sbuf.pos;
   1685 	return -1;
   1686     }
   1687 
   1688     str = buf->ptr;
   1689     switch (ch) {
   1690     case 022:			/* C-r */
   1691 	searchRoutine = backwardSearch;
   1692 	do_next_search = TRUE;
   1693 	break;
   1694     case 023:			/* C-s */
   1695 	searchRoutine = forwardSearch;
   1696 	do_next_search = TRUE;
   1697 	break;
   1698 
   1699 #ifdef USE_MIGEMO
   1700     case 034:
   1701 	migemo_active = -migemo_active;
   1702 	goto done;
   1703 #endif
   1704 
   1705     default:
   1706 	if (ch >= 0)
   1707 	    return ch;		/* use InputKeymap */
   1708     }
   1709 
   1710     if (do_next_search) {
   1711 	if (*str) {
   1712 	    if (searchRoutine == forwardSearch)
   1713 		Currentbuf->pos += 1;
   1714 	    SAVE_BUFPOSITION(&sbuf);
   1715 	    if (srchcore(str, searchRoutine) == SR_NOTFOUND
   1716 		&& searchRoutine == forwardSearch) {
   1717 		Currentbuf->pos -= 1;
   1718 		SAVE_BUFPOSITION(&sbuf);
   1719 	    }
   1720 	    arrangeCursor(Currentbuf);
   1721 	    displayBuffer(Currentbuf, B_FORCE_REDRAW);
   1722 	    clear_mark(Currentbuf->currentLine);
   1723 	    return -1;
   1724 	}
   1725 	else
   1726 	    return 020;		/* _prev completion for C-s C-s */
   1727     }
   1728     else if (*str) {
   1729 	RESTORE_BUFPOSITION(&sbuf);
   1730 	arrangeCursor(Currentbuf);
   1731 	srchcore(str, searchRoutine);
   1732 	arrangeCursor(Currentbuf);
   1733 	currentLine = Currentbuf->currentLine;
   1734 	pos = Currentbuf->pos;
   1735     }
   1736     displayBuffer(Currentbuf, B_FORCE_REDRAW);
   1737     clear_mark(Currentbuf->currentLine);
   1738 #ifdef USE_MIGEMO
   1739   done:
   1740     while (*str++ != '\0') {
   1741 	if (migemo_active > 0)
   1742 	    *prop++ |= PE_UNDER;
   1743 	else
   1744 	    *prop++ &= ~PE_UNDER;
   1745     }
   1746 #endif
   1747     return -1;
   1748 }
   1749 
   1750 void
   1751 isrch(int (*func) (Buffer *, char *), char *prompt)
   1752 {
   1753     char *str;
   1754     Buffer sbuf;
   1755     SAVE_BUFPOSITION(&sbuf);
   1756     dispincsrch(0, NULL, NULL);	/* initialize incremental search state */
   1757 
   1758     searchRoutine = func;
   1759     str = inputLineHistSearch(prompt, NULL, IN_STRING, TextHist, dispincsrch);
   1760     if (str == NULL) {
   1761 	RESTORE_BUFPOSITION(&sbuf);
   1762     }
   1763     displayBuffer(Currentbuf, B_FORCE_REDRAW);
   1764 }
   1765 
   1766 void
   1767 srch(int (*func) (Buffer *, char *), char *prompt)
   1768 {
   1769     char *str;
   1770     int result;
   1771     int disp = FALSE;
   1772     int pos;
   1773 
   1774     str = searchKeyData();
   1775     if (str == NULL || *str == '\0') {
   1776 	str = inputStrHist(prompt, NULL, TextHist);
   1777 	if (str != NULL && *str == '\0')
   1778 	    str = SearchString;
   1779 	if (str == NULL) {
   1780 	    displayBuffer(Currentbuf, B_NORMAL);
   1781 	    return;
   1782 	}
   1783 	disp = TRUE;
   1784     }
   1785     pos = Currentbuf->pos;
   1786     if (func == forwardSearch)
   1787 	Currentbuf->pos += 1;
   1788     result = srchcore(str, func);
   1789     if (result & SR_FOUND)
   1790 	clear_mark(Currentbuf->currentLine);
   1791     else
   1792 	Currentbuf->pos = pos;
   1793     displayBuffer(Currentbuf, B_NORMAL);
   1794     if (disp)
   1795 	disp_srchresult(result, prompt, str);
   1796     searchRoutine = func;
   1797 }
   1798 
   1799 /* Search regular expression forward */
   1800 
   1801 DEFUN(srchfor, SEARCH SEARCH_FORE WHEREIS, "Search forward")
   1802 {
   1803     srch(forwardSearch, "Forward: ");
   1804 }
   1805 
   1806 DEFUN(isrchfor, ISEARCH, "Incremental search forward")
   1807 {
   1808     isrch(forwardSearch, "I-search: ");
   1809 }
   1810 
   1811 /* Search regular expression backward */
   1812 
   1813 DEFUN(srchbak, SEARCH_BACK, "Search backward")
   1814 {
   1815     srch(backwardSearch, "Backward: ");
   1816 }
   1817 
   1818 DEFUN(isrchbak, ISEARCH_BACK, "Incremental search backward")
   1819 {
   1820     isrch(backwardSearch, "I-search backward: ");
   1821 }
   1822 
   1823 static void
   1824 srch_nxtprv(int reverse)
   1825 {
   1826     int result;
   1827     /* *INDENT-OFF* */
   1828     static int (*routine[2]) (Buffer *, char *) = {
   1829 	forwardSearch, backwardSearch
   1830     };
   1831     /* *INDENT-ON* */
   1832 
   1833     if (searchRoutine == NULL) {
   1834 	/* FIXME: gettextize? */
   1835 	disp_message("No previous regular expression", TRUE);
   1836 	return;
   1837     }
   1838     if (reverse != 0)
   1839 	reverse = 1;
   1840     if (searchRoutine == backwardSearch)
   1841 	reverse ^= 1;
   1842     if (reverse == 0)
   1843 	Currentbuf->pos += 1;
   1844     result = srchcore(SearchString, routine[reverse]);
   1845     if (result & SR_FOUND)
   1846 	clear_mark(Currentbuf->currentLine);
   1847     displayBuffer(Currentbuf, B_NORMAL);
   1848     disp_srchresult(result, (reverse ? "Backward: " : "Forward: "),
   1849 		    SearchString);
   1850 }
   1851 
   1852 /* Search next matching */
   1853 DEFUN(srchnxt, SEARCH_NEXT, "Search next regexp")
   1854 {
   1855     srch_nxtprv(0);
   1856 }
   1857 
   1858 /* Search previous matching */
   1859 DEFUN(srchprv, SEARCH_PREV, "Search previous regexp")
   1860 {
   1861     srch_nxtprv(1);
   1862 }
   1863 
   1864 static void
   1865 shiftvisualpos(Buffer *buf, int shift)
   1866 {
   1867     Line *l = buf->currentLine;
   1868     buf->visualpos -= shift;
   1869     if (buf->visualpos - l->bwidth >= buf->COLS)
   1870 	buf->visualpos = l->bwidth + buf->COLS - 1;
   1871     else if (buf->visualpos - l->bwidth < 0)
   1872 	buf->visualpos = l->bwidth;
   1873     arrangeLine(buf);
   1874     if (buf->visualpos - l->bwidth == -shift && buf->cursorX == 0)
   1875 	buf->visualpos = l->bwidth;
   1876 }
   1877 
   1878 /* Shift screen left */
   1879 DEFUN(shiftl, SHIFT_LEFT, "Shift screen left")
   1880 {
   1881     int column;
   1882 
   1883     if (Currentbuf->firstLine == NULL)
   1884 	return;
   1885     column = Currentbuf->currentColumn;
   1886     columnSkip(Currentbuf, searchKeyNum() * (-Currentbuf->COLS + 1) + 1);
   1887     shiftvisualpos(Currentbuf, Currentbuf->currentColumn - column);
   1888     displayBuffer(Currentbuf, B_NORMAL);
   1889 }
   1890 
   1891 /* Shift screen right */
   1892 DEFUN(shiftr, SHIFT_RIGHT, "Shift screen right")
   1893 {
   1894     int column;
   1895 
   1896     if (Currentbuf->firstLine == NULL)
   1897 	return;
   1898     column = Currentbuf->currentColumn;
   1899     columnSkip(Currentbuf, searchKeyNum() * (Currentbuf->COLS - 1) - 1);
   1900     shiftvisualpos(Currentbuf, Currentbuf->currentColumn - column);
   1901     displayBuffer(Currentbuf, B_NORMAL);
   1902 }
   1903 
   1904 DEFUN(col1R, RIGHT, "Shift screen one column right")
   1905 {
   1906     Buffer *buf = Currentbuf;
   1907     Line *l = buf->currentLine;
   1908     int j, column, n = searchKeyNum();
   1909 
   1910     if (l == NULL)
   1911 	return;
   1912     for (j = 0; j < n; j++) {
   1913 	column = buf->currentColumn;
   1914 	columnSkip(Currentbuf, 1);
   1915 	if (column == buf->currentColumn)
   1916 	    break;
   1917 	shiftvisualpos(Currentbuf, 1);
   1918     }
   1919     displayBuffer(Currentbuf, B_NORMAL);
   1920 }
   1921 
   1922 DEFUN(col1L, LEFT, "Shift screen one column")
   1923 {
   1924     Buffer *buf = Currentbuf;
   1925     Line *l = buf->currentLine;
   1926     int j, n = searchKeyNum();
   1927 
   1928     if (l == NULL)
   1929 	return;
   1930     for (j = 0; j < n; j++) {
   1931 	if (buf->currentColumn == 0)
   1932 	    break;
   1933 	columnSkip(Currentbuf, -1);
   1934 	shiftvisualpos(Currentbuf, -1);
   1935     }
   1936     displayBuffer(Currentbuf, B_NORMAL);
   1937 }
   1938 
   1939 DEFUN(setEnv, SETENV, "Set environment variable")
   1940 {
   1941     char *env;
   1942     char *var, *value;
   1943 
   1944     CurrentKeyData = NULL;	/* not allowed in w3m-control: */
   1945     env = searchKeyData();
   1946     if (env == NULL || *env == '\0' || strchr(env, '=') == NULL) {
   1947 	if (env != NULL && *env != '\0')
   1948 	    env = Sprintf("%s=", env)->ptr;
   1949 	env = inputStrHist("Set environ: ", env, TextHist);
   1950 	if (env == NULL || *env == '\0') {
   1951 	    displayBuffer(Currentbuf, B_NORMAL);
   1952 	    return;
   1953 	}
   1954     }
   1955     if ((value = strchr(env, '=')) != NULL && value > env) {
   1956 	var = allocStr(env, value - env);
   1957 	value++;
   1958 	set_environ(var, value);
   1959     }
   1960     displayBuffer(Currentbuf, B_NORMAL);
   1961 }
   1962 
   1963 DEFUN(pipeBuf, PIPE_BUF, "Send rendered document to pipe")
   1964 {
   1965     Buffer *buf;
   1966     char *cmd, *tmpf;
   1967     FILE *f;
   1968 
   1969     CurrentKeyData = NULL;	/* not allowed in w3m-control: */
   1970     cmd = searchKeyData();
   1971     if (cmd == NULL || *cmd == '\0') {
   1972 	/* FIXME: gettextize? */
   1973 	cmd = inputLineHist("Pipe buffer to: ", "", IN_COMMAND, ShellHist);
   1974     }
   1975     if (cmd != NULL)
   1976 	cmd = conv_to_system(cmd);
   1977     if (cmd == NULL || *cmd == '\0') {
   1978 	displayBuffer(Currentbuf, B_NORMAL);
   1979 	return;
   1980     }
   1981     tmpf = tmpfname(TMPF_DFL, NULL)->ptr;
   1982     f = fopen(tmpf, "w");
   1983     if (f == NULL) {
   1984 	/* FIXME: gettextize? */
   1985 	disp_message(Sprintf("Can't save buffer to %s", cmd)->ptr, TRUE);
   1986 	return;
   1987     }
   1988     saveBuffer(Currentbuf, f, TRUE);
   1989     fclose(f);
   1990     buf = getpipe(myExtCommand(cmd, shell_quote(tmpf), TRUE)->ptr);
   1991     if (buf == NULL) {
   1992 	disp_message("Execution failed", TRUE);
   1993 	return;
   1994     }
   1995     else {
   1996 	buf->filename = cmd;
   1997 	buf->buffername = Sprintf("%s %s", PIPEBUFFERNAME,
   1998 				  conv_from_system(cmd))->ptr;
   1999 	buf->bufferprop |= (BP_INTERNAL | BP_NO_URL);
   2000 	if (buf->type == NULL)
   2001 	    buf->type = "text/plain";
   2002 	buf->currentURL.file = "-";
   2003 	pushBuffer(buf);
   2004     }
   2005     displayBuffer(Currentbuf, B_FORCE_REDRAW);
   2006 }
   2007 
   2008 /* Execute shell command and read output ac pipe. */
   2009 DEFUN(pipesh, PIPE_SHELL, "Execute shell command and browse")
   2010 {
   2011     Buffer *buf;
   2012     char *cmd;
   2013 
   2014     CurrentKeyData = NULL;	/* not allowed in w3m-control: */
   2015     cmd = searchKeyData();
   2016     if (cmd == NULL || *cmd == '\0') {
   2017 	cmd = inputLineHist("(read shell[pipe])!", "", IN_COMMAND, ShellHist);
   2018     }
   2019     if (cmd != NULL)
   2020 	cmd = conv_to_system(cmd);
   2021     if (cmd == NULL || *cmd == '\0') {
   2022 	displayBuffer(Currentbuf, B_NORMAL);
   2023 	return;
   2024     }
   2025     buf = getpipe(cmd);
   2026     if (buf == NULL) {
   2027 	disp_message("Execution failed", TRUE);
   2028 	return;
   2029     }
   2030     else {
   2031 	buf->bufferprop |= (BP_INTERNAL | BP_NO_URL);
   2032 	if (buf->type == NULL)
   2033 	    buf->type = "text/plain";
   2034 	pushBuffer(buf);
   2035     }
   2036     displayBuffer(Currentbuf, B_FORCE_REDRAW);
   2037 }
   2038 
   2039 /* Execute shell command and load entire output to buffer */
   2040 DEFUN(readsh, READ_SHELL, "Execute shell command and load")
   2041 {
   2042     Buffer *buf;
   2043     MySignalHandler(*prevtrap) ();
   2044     char *cmd;
   2045 
   2046     CurrentKeyData = NULL;	/* not allowed in w3m-control: */
   2047     cmd = searchKeyData();
   2048     if (cmd == NULL || *cmd == '\0') {
   2049 	cmd = inputLineHist("(read shell)!", "", IN_COMMAND, ShellHist);
   2050     }
   2051     if (cmd != NULL)
   2052 	cmd = conv_to_system(cmd);
   2053     if (cmd == NULL || *cmd == '\0') {
   2054 	displayBuffer(Currentbuf, B_NORMAL);
   2055 	return;
   2056     }
   2057     prevtrap = mySignal(SIGINT, intTrap);
   2058     crmode();
   2059     buf = getshell(cmd);
   2060     mySignal(SIGINT, prevtrap);
   2061     term_raw();
   2062     if (buf == NULL) {
   2063 	/* FIXME: gettextize? */
   2064 	disp_message("Execution failed", TRUE);
   2065 	return;
   2066     }
   2067     else {
   2068 	buf->bufferprop |= (BP_INTERNAL | BP_NO_URL);
   2069 	if (buf->type == NULL)
   2070 	    buf->type = "text/plain";
   2071 	pushBuffer(buf);
   2072     }
   2073     displayBuffer(Currentbuf, B_FORCE_REDRAW);
   2074 }
   2075 
   2076 /* Execute shell command */
   2077 DEFUN(execsh, EXEC_SHELL SHELL, "Execute shell command")
   2078 {
   2079     char *cmd;
   2080 
   2081     CurrentKeyData = NULL;	/* not allowed in w3m-control: */
   2082     cmd = searchKeyData();
   2083     if (cmd == NULL || *cmd == '\0') {
   2084 	cmd = inputLineHist("(exec shell)!", "", IN_COMMAND, ShellHist);
   2085     }
   2086     if (cmd != NULL)
   2087 	cmd = conv_to_system(cmd);
   2088     if (cmd != NULL && *cmd != '\0') {
   2089 	fmTerm();
   2090 	printf("\n");
   2091 	system(cmd);
   2092 	/* FIXME: gettextize? */
   2093 	printf("\n[Hit any key]");
   2094 	fflush(stdout);
   2095 	fmInit();
   2096 	getch();
   2097     }
   2098     displayBuffer(Currentbuf, B_FORCE_REDRAW);
   2099 }
   2100 
   2101 /* Load file */
   2102 DEFUN(ldfile, LOAD, "Load local file")
   2103 {
   2104     char *fn;
   2105 
   2106     fn = searchKeyData();
   2107     if (fn == NULL || *fn == '\0') {
   2108 	/* FIXME: gettextize? */
   2109 	fn = inputFilenameHist("(Load)Filename? ", NULL, LoadHist);
   2110     }
   2111     if (fn != NULL)
   2112 	fn = conv_to_system(fn);
   2113     if (fn == NULL || *fn == '\0') {
   2114 	displayBuffer(Currentbuf, B_NORMAL);
   2115 	return;
   2116     }
   2117     cmd_loadfile(fn);
   2118 }
   2119 
   2120 /* Load help file */
   2121 DEFUN(ldhelp, HELP, "View help")
   2122 {
   2123 #ifdef USE_HELP_CGI
   2124     char *lang;
   2125     int n;
   2126     Str tmp;
   2127 
   2128     lang = AcceptLang;
   2129     n = strcspn(lang, ";, \t");
   2130     tmp = Sprintf("file:///$LIB/" HELP_CGI CGI_EXTENSION "?version=%s&lang=%s",
   2131 		  Str_form_quote(Strnew_charp(w3m_version))->ptr,
   2132 		  Str_form_quote(Strnew_charp_n(lang, n))->ptr);
   2133     cmd_loadURL(tmp->ptr, NULL, NO_REFERER, NULL);
   2134 #else
   2135     cmd_loadURL(helpFile(HELP_FILE), NULL, NO_REFERER, NULL);
   2136 #endif
   2137 }
   2138 
   2139 static void
   2140 cmd_loadfile(char *fn)
   2141 {
   2142     Buffer *buf;
   2143 
   2144     buf = loadGeneralFile(file_to_url(fn), NULL, NO_REFERER, 0, NULL);
   2145     if (buf == NULL) {
   2146 	/* FIXME: gettextize? */
   2147 	char *emsg = Sprintf("%s not found", conv_from_system(fn))->ptr;
   2148 	disp_err_message(emsg, FALSE);
   2149     }
   2150     else if (buf != NO_BUFFER) {
   2151 	pushBuffer(buf);
   2152 	if (RenderFrame && Currentbuf->frameset != NULL)
   2153 	    rFrame();
   2154     }
   2155     displayBuffer(Currentbuf, B_NORMAL);
   2156 }
   2157 
   2158 /* Move cursor left */
   2159 static void
   2160 _movL(int n)
   2161 {
   2162     int i, m = searchKeyNum();
   2163     if (Currentbuf->firstLine == NULL)
   2164 	return;
   2165     for (i = 0; i < m; i++)
   2166 	cursorLeft(Currentbuf, n);
   2167     displayBuffer(Currentbuf, B_NORMAL);
   2168 }
   2169 
   2170 DEFUN(movL, MOVE_LEFT,
   2171       "Move cursor left (a half screen shift at the left edge)")
   2172 {
   2173     _movL(Currentbuf->COLS / 2);
   2174 }
   2175 
   2176 DEFUN(movL1, MOVE_LEFT1, "Move cursor left (1 columns shift at the left edge)")
   2177 {
   2178     _movL(1);
   2179 }
   2180 
   2181 /* Move cursor downward */
   2182 static void
   2183 _movD(int n)
   2184 {
   2185     int i, m = searchKeyNum();
   2186     if (Currentbuf->firstLine == NULL)
   2187 	return;
   2188     for (i = 0; i < m; i++)
   2189 	cursorDown(Currentbuf, n);
   2190     displayBuffer(Currentbuf, B_NORMAL);
   2191 }
   2192 
   2193 DEFUN(movD, MOVE_DOWN,
   2194       "Move cursor down (a half screen scroll at the end of screen)")
   2195 {
   2196     _movD((Currentbuf->LINES + 1) / 2);
   2197 }
   2198 
   2199 DEFUN(movD1, MOVE_DOWN1,
   2200       "Move cursor down (1 line scroll at the end of screen)")
   2201 {
   2202     _movD(1);
   2203 }
   2204 
   2205 /* move cursor upward */
   2206 static void
   2207 _movU(int n)
   2208 {
   2209     int i, m = searchKeyNum();
   2210     if (Currentbuf->firstLine == NULL)
   2211 	return;
   2212     for (i = 0; i < m; i++)
   2213 	cursorUp(Currentbuf, n);
   2214     displayBuffer(Currentbuf, B_NORMAL);
   2215 }
   2216 
   2217 DEFUN(movU, MOVE_UP,
   2218       "Move cursor up (a half screen scroll at the top of screen)")
   2219 {
   2220     _movU((Currentbuf->LINES + 1) / 2);
   2221 }
   2222 
   2223 DEFUN(movU1, MOVE_UP1, "Move cursor up (1 line scrol at the top of screen)")
   2224 {
   2225     _movU(1);
   2226 }
   2227 
   2228 /* Move cursor right */
   2229 static void
   2230 _movR(int n)
   2231 {
   2232     int i, m = searchKeyNum();
   2233     if (Currentbuf->firstLine == NULL)
   2234 	return;
   2235     for (i = 0; i < m; i++)
   2236 	cursorRight(Currentbuf, n);
   2237     displayBuffer(Currentbuf, B_NORMAL);
   2238 }
   2239 
   2240 DEFUN(movR, MOVE_RIGHT,
   2241       "Move cursor right (a half screen shift at the right edge)")
   2242 {
   2243     _movR(Currentbuf->COLS / 2);
   2244 }
   2245 
   2246 DEFUN(movR1, MOVE_RIGHT1,
   2247       "Move cursor right (1 columns shift at the right edge)")
   2248 {
   2249     _movR(1);
   2250 }
   2251 
   2252 /* movLW, movRW */
   2253 /* 
   2254  * From: Takashi Nishimoto <g96p0935@mse.waseda.ac.jp> Date: Mon, 14 Jun
   2255  * 1999 09:29:56 +0900 
   2256  */
   2257 #if defined(USE_M17N) && defined(USE_UNICODE)
   2258 #define nextChar(s, l)	do { (s)++; } while ((s) < (l)->len && (l)->propBuf[s] & PC_WCHAR2)
   2259 #define prevChar(s, l)	do { (s)--; } while ((s) > 0 && (l)->propBuf[s] & PC_WCHAR2)
   2260 
   2261 static wc_uint32
   2262 getChar(char *p)
   2263 {
   2264     return wc_any_to_ucs(wtf_parse1(&p));
   2265 }
   2266 
   2267 static int
   2268 is_wordchar(wc_uint32 c)
   2269 {
   2270     return wc_is_ucs_alnum(c);
   2271 }
   2272 #else 
   2273 #define nextChar(s, l)	(s)++
   2274 #define prevChar(s, l)	(s)--
   2275 #define getChar(p)	((int)*(p))
   2276 
   2277 static int
   2278 is_wordchar(int c)
   2279 {
   2280     return IS_ALNUM(c);
   2281 }
   2282 #endif
   2283 
   2284 static int
   2285 prev_nonnull_line(Line *line)
   2286 {
   2287     Line *l;
   2288 
   2289     for (l = line; l != NULL && l->len == 0; l = l->prev) ;
   2290     if (l == NULL || l->len == 0)
   2291 	return -1;
   2292 
   2293     Currentbuf->currentLine = l;
   2294     if (l != line)
   2295 	Currentbuf->pos = Currentbuf->currentLine->len;
   2296     return 0;
   2297 }
   2298 
   2299 DEFUN(movLW, PREV_WORD, "Move to previous word")
   2300 {
   2301     char *lb;
   2302     Line *pline, *l;
   2303     int ppos;
   2304     int i, n = searchKeyNum();
   2305 
   2306     if (Currentbuf->firstLine == NULL)
   2307 	return;
   2308 
   2309     for (i = 0; i < n; i++) {
   2310 	pline = Currentbuf->currentLine;
   2311 	ppos = Currentbuf->pos;
   2312 
   2313 	if (prev_nonnull_line(Currentbuf->currentLine) < 0)
   2314 	    goto end;
   2315 
   2316 	while (1) {
   2317 	    l = Currentbuf->currentLine;
   2318 	    lb = l->lineBuf;
   2319 	    while (Currentbuf->pos > 0) {
   2320 		int tmp = Currentbuf->pos;
   2321 		prevChar(tmp, l);
   2322 		if (is_wordchar(getChar(&lb[tmp])))
   2323 		    break;
   2324 		Currentbuf->pos = tmp;
   2325 	    }
   2326 	    if (Currentbuf->pos > 0)
   2327 		break;
   2328 	    if (prev_nonnull_line(Currentbuf->currentLine->prev) < 0) {
   2329 		Currentbuf->currentLine = pline;
   2330 		Currentbuf->pos = ppos;
   2331 		goto end;
   2332 	    }
   2333 	    Currentbuf->pos = Currentbuf->currentLine->len;
   2334 	}
   2335 
   2336 	l = Currentbuf->currentLine;
   2337 	lb = l->lineBuf;
   2338 	while (Currentbuf->pos > 0) {
   2339 	    int tmp = Currentbuf->pos;
   2340 	    prevChar(tmp, l);
   2341 	    if (!is_wordchar(getChar(&lb[tmp])))
   2342 		break;
   2343 	    Currentbuf->pos = tmp;
   2344 	}
   2345     }
   2346   end:
   2347     arrangeCursor(Currentbuf);
   2348     displayBuffer(Currentbuf, B_NORMAL);
   2349 }
   2350 
   2351 static int
   2352 next_nonnull_line(Line *line)
   2353 {
   2354     Line *l;
   2355 
   2356     for (l = line; l != NULL && l->len == 0; l = l->next) ;
   2357 
   2358     if (l == NULL || l->len == 0)
   2359 	return -1;
   2360 
   2361     Currentbuf->currentLine = l;
   2362     if (l != line)
   2363 	Currentbuf->pos = 0;
   2364     return 0;
   2365 }
   2366 
   2367 DEFUN(movRW, NEXT_WORD, "Move to next word")
   2368 {
   2369     char *lb;
   2370     Line *pline, *l;
   2371     int ppos;
   2372     int i, n = searchKeyNum();
   2373 
   2374     if (Currentbuf->firstLine == NULL)
   2375 	return;
   2376 
   2377     for (i = 0; i < n; i++) {
   2378 	pline = Currentbuf->currentLine;
   2379 	ppos = Currentbuf->pos;
   2380 
   2381 	if (next_nonnull_line(Currentbuf->currentLine) < 0)
   2382 	    goto end;
   2383 
   2384 	l = Currentbuf->currentLine;
   2385 	lb = l->lineBuf;
   2386 	while (Currentbuf->pos < l->len &&
   2387 	       is_wordchar(getChar(&lb[Currentbuf->pos])))
   2388 	    nextChar(Currentbuf->pos, l);
   2389 
   2390 	while (1) {
   2391 	    while (Currentbuf->pos < l->len &&
   2392 		   !is_wordchar(getChar(&lb[Currentbuf->pos])))
   2393 		nextChar(Currentbuf->pos, l);
   2394 	    if (Currentbuf->pos < l->len)
   2395 		break;
   2396 	    if (next_nonnull_line(Currentbuf->currentLine->next) < 0) {
   2397 		Currentbuf->currentLine = pline;
   2398 		Currentbuf->pos = ppos;
   2399 		goto end;
   2400 	    }
   2401 	    Currentbuf->pos = 0;
   2402 	    l = Currentbuf->currentLine;
   2403 	    lb = l->lineBuf;
   2404 	}
   2405     }
   2406   end:
   2407     arrangeCursor(Currentbuf);
   2408     displayBuffer(Currentbuf, B_NORMAL);
   2409 }
   2410 
   2411 static void
   2412 _quitfm(int confirm)
   2413 {
   2414     char *ans = "y";
   2415 
   2416     if (checkDownloadList())
   2417 	/* FIXME: gettextize? */
   2418 	ans = inputChar("Download process retains. "
   2419 			"Do you want to exit w3m? (y/n)");
   2420     else if (confirm)
   2421 	/* FIXME: gettextize? */
   2422 	ans = inputChar("Do you want to exit w3m? (y/n)");
   2423     if (!(ans && TOLOWER(*ans) == 'y')) {
   2424 	displayBuffer(Currentbuf, B_NORMAL);
   2425 	return;
   2426     }
   2427 
   2428     term_title("");		/* XXX */
   2429 #ifdef USE_IMAGE
   2430     if (activeImage)
   2431 	termImage();
   2432 #endif
   2433     fmTerm();
   2434 #ifdef USE_COOKIE
   2435     save_cookies();
   2436 #endif				/* USE_COOKIE */
   2437 #ifdef USE_HISTORY
   2438     if (UseHistory && SaveURLHist)
   2439 	saveHistory(URLHist, URLHistSize);
   2440 #endif				/* USE_HISTORY */
   2441     w3m_exit(0);
   2442 }
   2443 
   2444 /* Quit */
   2445 DEFUN(quitfm, ABORT EXIT, "Quit w3m without confirmation")
   2446 {
   2447     _quitfm(FALSE);
   2448 }
   2449 
   2450 /* Question and Quit */
   2451 DEFUN(qquitfm, QUIT, "Quit w3m")
   2452 {
   2453     _quitfm(confirm_on_quit);
   2454 }
   2455 
   2456 /* Select buffer */
   2457 DEFUN(selBuf, SELECT, "Go to buffer selection panel")
   2458 {
   2459     Buffer *buf;
   2460     int ok;
   2461     char cmd;
   2462 
   2463     ok = FALSE;
   2464     do {
   2465 	buf = selectBuffer(Firstbuf, Currentbuf, &cmd);
   2466 	switch (cmd) {
   2467 	case 'B':
   2468 	    ok = TRUE;
   2469 	    break;
   2470 	case '\n':
   2471 	case ' ':
   2472 	    Currentbuf = buf;
   2473 	    ok = TRUE;
   2474 	    break;
   2475 	case 'D':
   2476 	    delBuffer(buf);
   2477 	    if (Firstbuf == NULL) {
   2478 		/* No more buffer */
   2479 		Firstbuf = nullBuffer();
   2480 		Currentbuf = Firstbuf;
   2481 	    }
   2482 	    break;
   2483 	case 'q':
   2484 	    qquitfm();
   2485 	    break;
   2486 	case 'Q':
   2487 	    quitfm();
   2488 	    break;
   2489 	}
   2490     } while (!ok);
   2491 
   2492     for (buf = Firstbuf; buf != NULL; buf = buf->nextBuffer) {
   2493 	if (buf == Currentbuf)
   2494 	    continue;
   2495 #ifdef USE_IMAGE
   2496 	deleteImage(buf);
   2497 #endif
   2498 	if (clear_buffer)
   2499 	    tmpClearBuffer(buf);
   2500     }
   2501     displayBuffer(Currentbuf, B_FORCE_REDRAW);
   2502 }
   2503 
   2504 /* Suspend (on BSD), or run interactive shell (on SysV) */
   2505 DEFUN(susp, INTERRUPT SUSPEND, "Stop loading document")
   2506 {
   2507 #ifndef SIGSTOP
   2508     char *shell;
   2509 #endif				/* not SIGSTOP */
   2510     move(LASTLINE, 0);
   2511     clrtoeolx();
   2512     refresh();
   2513     fmTerm();
   2514 #ifndef SIGSTOP
   2515     shell = getenv("SHELL");
   2516     if (shell == NULL)
   2517 	shell = "/bin/sh";
   2518     system(shell);
   2519 #else				/* SIGSTOP */
   2520     kill((pid_t) 0, SIGSTOP);
   2521 #endif				/* SIGSTOP */
   2522     fmInit();
   2523     displayBuffer(Currentbuf, B_FORCE_REDRAW);
   2524 }
   2525 
   2526 /* Go to specified line */
   2527 static void
   2528 _goLine(char *l)
   2529 {
   2530     if (l == NULL || *l == '\0' || Currentbuf->currentLine == NULL) {
   2531 	displayBuffer(Currentbuf, B_FORCE_REDRAW);
   2532 	return;
   2533     }
   2534     Currentbuf->pos = 0;
   2535     if (((*l == '^') || (*l == '$')) && prec_num) {
   2536 	gotoRealLine(Currentbuf, prec_num);
   2537     }
   2538     else if (*l == '^') {
   2539 	Currentbuf->topLine = Currentbuf->currentLine = Currentbuf->firstLine;
   2540     }
   2541     else if (*l == '$') {
   2542 	Currentbuf->topLine =
   2543 	    lineSkip(Currentbuf, Currentbuf->lastLine,
   2544 		     -(Currentbuf->LINES + 1) / 2, TRUE);
   2545 	Currentbuf->currentLine = Currentbuf->lastLine;
   2546     }
   2547     else
   2548 	gotoRealLine(Currentbuf, atoi(l));
   2549     arrangeCursor(Currentbuf);
   2550     displayBuffer(Currentbuf, B_FORCE_REDRAW);
   2551 }
   2552 
   2553 DEFUN(goLine, GOTO_LINE, "Go to specified line")
   2554 {
   2555 
   2556     char *str = searchKeyData();
   2557     if (prec_num)
   2558 	_goLine("^");
   2559     else if (str)
   2560 	_goLine(str);
   2561     else
   2562 	/* FIXME: gettextize? */
   2563 	_goLine(inputStr("Goto line: ", ""));
   2564 }
   2565 
   2566 
   2567 DEFUN(goLineF, BEGIN, "Go to the first line")
   2568 {
   2569     _goLine("^");
   2570 }
   2571 
   2572 DEFUN(goLineL, END, "Go to the last line")
   2573 {
   2574     _goLine("$");
   2575 }
   2576 
   2577 /* Go to the beginning of the line */
   2578 DEFUN(linbeg, LINE_BEGIN, "Go to the beginning of line")
   2579 {
   2580     if (Currentbuf->firstLine == NULL)
   2581 	return;
   2582     while (Currentbuf->currentLine->prev && Currentbuf->currentLine->bpos)
   2583 	cursorUp0(Currentbuf, 1);
   2584     Currentbuf->pos = 0;
   2585     arrangeCursor(Currentbuf);
   2586     displayBuffer(Currentbuf, B_NORMAL);
   2587 }
   2588 
   2589 /* Go to the bottom of the line */
   2590 DEFUN(linend, LINE_END, "Go to the end of line")
   2591 {
   2592     if (Currentbuf->firstLine == NULL)
   2593 	return;
   2594     while (Currentbuf->currentLine->next
   2595 	   && Currentbuf->currentLine->next->bpos)
   2596 	cursorDown0(Currentbuf, 1);
   2597     Currentbuf->pos = Currentbuf->currentLine->len - 1;
   2598     arrangeCursor(Currentbuf);
   2599     displayBuffer(Currentbuf, B_NORMAL);
   2600 }
   2601 
   2602 static int
   2603 cur_real_linenumber(Buffer *buf)
   2604 {
   2605     Line *l, *cur = buf->currentLine;
   2606     int n;
   2607 
   2608     if (!cur)
   2609 	return 1;
   2610     n = cur->real_linenumber ? cur->real_linenumber : 1;
   2611     for (l = buf->firstLine; l && l != cur && l->real_linenumber == 0; l = l->next) {	/* header */
   2612 	if (l->bpos == 0)
   2613 	    n++;
   2614     }
   2615     return n;
   2616 }
   2617 
   2618 /* Run editor on the current buffer */
   2619 DEFUN(editBf, EDIT, "Edit current document")
   2620 {
   2621     char *fn = Currentbuf->filename;
   2622     Str cmd;
   2623 
   2624     if (fn == NULL || Currentbuf->pagerSource != NULL ||	/* Behaving as a pager */
   2625 	(Currentbuf->type == NULL && Currentbuf->edit == NULL) ||	/* Reading shell */
   2626 	Currentbuf->real_scheme != SCM_LOCAL || !strcmp(Currentbuf->currentURL.file, "-") ||	/* file is std input  */
   2627 	Currentbuf->bufferprop & BP_FRAME) {	/* Frame */
   2628 	disp_err_message("Can't edit other than local file", TRUE);
   2629 	return;
   2630     }
   2631     if (Currentbuf->edit)
   2632 	cmd = unquote_mailcap(Currentbuf->edit, Currentbuf->real_type, fn,
   2633 			      checkHeader(Currentbuf, "Content-Type:"), NULL);
   2634     else
   2635 	cmd = myEditor(Editor, shell_quote(fn),
   2636 		       cur_real_linenumber(Currentbuf));
   2637     fmTerm();
   2638     system(cmd->ptr);
   2639     fmInit();
   2640 
   2641     displayBuffer(Currentbuf, B_FORCE_REDRAW);
   2642     reload();
   2643 }
   2644 
   2645 /* Run editor on the current screen */
   2646 DEFUN(editScr, EDIT_SCREEN, "Edit currently rendered document")
   2647 {
   2648     char *tmpf;
   2649     FILE *f;
   2650 
   2651     tmpf = tmpfname(TMPF_DFL, NULL)->ptr;
   2652     f = fopen(tmpf, "w");
   2653     if (f == NULL) {
   2654 	/* FIXME: gettextize? */
   2655 	disp_err_message(Sprintf("Can't open %s", tmpf)->ptr, TRUE);
   2656 	return;
   2657     }
   2658     saveBuffer(Currentbuf, f, TRUE);
   2659     fclose(f);
   2660     fmTerm();
   2661     system(myEditor(Editor, shell_quote(tmpf),
   2662 		    cur_real_linenumber(Currentbuf))->ptr);
   2663     fmInit();
   2664     unlink(tmpf);
   2665     displayBuffer(Currentbuf, B_FORCE_REDRAW);
   2666 }
   2667 
   2668 #ifdef USE_MARK
   2669 
   2670 /* Set / unset mark */
   2671 DEFUN(_mark, MARK, "Set/unset mark")
   2672 {
   2673     Line *l;
   2674     if (!use_mark)
   2675 	return;
   2676     if (Currentbuf->firstLine == NULL)
   2677 	return;
   2678     l = Currentbuf->currentLine;
   2679     l->propBuf[Currentbuf->pos] ^= PE_MARK;
   2680     displayBuffer(Currentbuf, B_FORCE_REDRAW);
   2681 }
   2682 
   2683 /* Go to next mark */
   2684 DEFUN(nextMk, NEXT_MARK, "Move to next word")
   2685 {
   2686     Line *l;
   2687     int i;
   2688 
   2689     if (!use_mark)
   2690 	return;
   2691     if (Currentbuf->firstLine == NULL)
   2692 	return;
   2693     i = Currentbuf->pos + 1;
   2694     l = Currentbuf->currentLine;
   2695     if (i >= l->len) {
   2696 	i = 0;
   2697 	l = l->next;
   2698     }
   2699     while (l != NULL) {
   2700 	for (; i < l->len; i++) {
   2701 	    if (l->propBuf[i] & PE_MARK) {
   2702 		Currentbuf->currentLine = l;
   2703 		Currentbuf->pos = i;
   2704 		arrangeCursor(Currentbuf);
   2705 		displayBuffer(Currentbuf, B_NORMAL);
   2706 		return;
   2707 	    }
   2708 	}
   2709 	l = l->next;
   2710 	i = 0;
   2711     }
   2712     /* FIXME: gettextize? */
   2713     disp_message("No mark exist after here", TRUE);
   2714 }
   2715 
   2716 /* Go to previous mark */
   2717 DEFUN(prevMk, PREV_MARK, "Move to previous mark")
   2718 {
   2719     Line *l;
   2720     int i;
   2721 
   2722     if (!use_mark)
   2723 	return;
   2724     if (Currentbuf->firstLine == NULL)
   2725 	return;
   2726     i = Currentbuf->pos - 1;
   2727     l = Currentbuf->currentLine;
   2728     if (i < 0) {
   2729 	l = l->prev;
   2730 	if (l != NULL)
   2731 	    i = l->len - 1;
   2732     }
   2733     while (l != NULL) {
   2734 	for (; i >= 0; i--) {
   2735 	    if (l->propBuf[i] & PE_MARK) {
   2736 		Currentbuf->currentLine = l;
   2737 		Currentbuf->pos = i;
   2738 		arrangeCursor(Currentbuf);
   2739 		displayBuffer(Currentbuf, B_NORMAL);
   2740 		return;
   2741 	    }
   2742 	}
   2743 	l = l->prev;
   2744 	if (l != NULL)
   2745 	    i = l->len - 1;
   2746     }
   2747     /* FIXME: gettextize? */
   2748     disp_message("No mark exist before here", TRUE);
   2749 }
   2750 
   2751 /* Mark place to which the regular expression matches */
   2752 DEFUN(reMark, REG_MARK, "Set mark using regexp")
   2753 {
   2754     Line *l;
   2755     char *str;
   2756     char *p, *p1, *p2;
   2757 
   2758     if (!use_mark)
   2759 	return;
   2760     str = searchKeyData();
   2761     if (str == NULL || *str == '\0') {
   2762 	str = inputStrHist("(Mark)Regexp: ", MarkString, TextHist);
   2763 	if (str == NULL || *str == '\0') {
   2764 	    displayBuffer(Currentbuf, B_NORMAL);
   2765 	    return;
   2766 	}
   2767     }
   2768     str = conv_search_string(str, DisplayCharset);
   2769     if ((str = regexCompile(str, 1)) != NULL) {
   2770 	disp_message(str, TRUE);
   2771 	return;
   2772     }
   2773     MarkString = str;
   2774     for (l = Currentbuf->firstLine; l != NULL; l = l->next) {
   2775 	p = l->lineBuf;
   2776 	for (;;) {
   2777 	    if (regexMatch(p, &l->lineBuf[l->len] - p, p == l->lineBuf) == 1) {
   2778 		matchedPosition(&p1, &p2);
   2779 		l->propBuf[p1 - l->lineBuf] |= PE_MARK;
   2780 		p = p2;
   2781 	    }
   2782 	    else
   2783 		break;
   2784 	}
   2785     }
   2786 
   2787     displayBuffer(Currentbuf, B_FORCE_REDRAW);
   2788 }
   2789 #endif				/* USE_MARK */
   2790 
   2791 static Buffer *
   2792 loadNormalBuf(Buffer *buf, int renderframe)
   2793 {
   2794     pushBuffer(buf);
   2795     if (renderframe && RenderFrame && Currentbuf->frameset != NULL)
   2796 	rFrame();
   2797     return buf;
   2798 }
   2799 
   2800 static Buffer *
   2801 loadLink(char *url, char *target, char *referer, FormList *request)
   2802 {
   2803     Buffer *buf, *nfbuf;
   2804     union frameset_element *f_element = NULL;
   2805     int flag = 0;
   2806     ParsedURL *base, pu;
   2807 
   2808     message(Sprintf("loading %s", url)->ptr, 0, 0);
   2809     refresh();
   2810 
   2811     base = baseURL(Currentbuf);
   2812     if (base == NULL ||
   2813 	base->scheme == SCM_LOCAL || base->scheme == SCM_LOCAL_CGI)
   2814 	referer = NO_REFERER;
   2815     if (referer == NULL)
   2816 	referer = parsedURL2Str(&Currentbuf->currentURL)->ptr;
   2817     buf = loadGeneralFile(url, baseURL(Currentbuf), referer, flag, request);
   2818     if (buf == NULL) {
   2819 	char *emsg = Sprintf("Can't load %s", url)->ptr;
   2820 	disp_err_message(emsg, FALSE);
   2821 	return NULL;
   2822     }
   2823 
   2824     parseURL2(url, &pu, base);
   2825     pushHashHist(URLHist, parsedURL2Str(&pu)->ptr);
   2826 
   2827     if (buf == NO_BUFFER) {
   2828 	return NULL;
   2829     }
   2830     if (!on_target)		/* open link as an indivisual page */
   2831 	return loadNormalBuf(buf, TRUE);
   2832 
   2833     if (do_download)		/* download (thus no need to render frame) */
   2834 	return loadNormalBuf(buf, FALSE);
   2835 
   2836     if (target == NULL ||	/* no target specified (that means this page is not a frame page) */
   2837 	!strcmp(target, "_top") ||	/* this link is specified to be opened as an indivisual * page */
   2838 	!(Currentbuf->bufferprop & BP_FRAME)	/* This page is not a frame page */
   2839 	) {
   2840 	return loadNormalBuf(buf, TRUE);
   2841     }
   2842     nfbuf = Currentbuf->linkBuffer[LB_N_FRAME];
   2843     if (nfbuf == NULL) {
   2844 	/* original page (that contains <frameset> tag) doesn't exist */
   2845 	return loadNormalBuf(buf, TRUE);
   2846     }
   2847 
   2848     f_element = search_frame(nfbuf->frameset, target);
   2849     if (f_element == NULL) {
   2850 	/* specified target doesn't exist in this frameset */
   2851 	return loadNormalBuf(buf, TRUE);
   2852     }
   2853 
   2854     /* frame page */
   2855 
   2856     /* stack current frameset */
   2857     pushFrameTree(&(nfbuf->frameQ), copyFrameSet(nfbuf->frameset), Currentbuf);
   2858     /* delete frame view buffer */
   2859     delBuffer(Currentbuf);
   2860     Currentbuf = nfbuf;
   2861     /* nfbuf->frameset = copyFrameSet(nfbuf->frameset); */
   2862     resetFrameElement(f_element, buf, referer, request);
   2863     discardBuffer(buf);
   2864     rFrame();
   2865     {
   2866 	Anchor *al = NULL;
   2867 	char *label = pu.label;
   2868 
   2869 	if (label && f_element->element->attr == F_BODY) {
   2870 	    al = searchAnchor(f_element->body->nameList, label);
   2871 	}
   2872 	if (!al) {
   2873 	    label = Strnew_m_charp("_", target, NULL)->ptr;
   2874 	    al = searchURLLabel(Currentbuf, label);
   2875 	}
   2876 	if (al) {
   2877 	    gotoLine(Currentbuf, al->start.line);
   2878 	    if (label_topline)
   2879 		Currentbuf->topLine = lineSkip(Currentbuf, Currentbuf->topLine,
   2880 					       Currentbuf->currentLine->
   2881 					       linenumber -
   2882 					       Currentbuf->topLine->linenumber,
   2883 					       FALSE);
   2884 	    Currentbuf->pos = al->start.pos;
   2885 	    arrangeCursor(Currentbuf);
   2886 	}
   2887     }
   2888     displayBuffer(Currentbuf, B_NORMAL);
   2889     return buf;
   2890 }
   2891 
   2892 static void
   2893 gotoLabel(char *label)
   2894 {
   2895     Buffer *buf;
   2896     Anchor *al;
   2897     int i;
   2898 
   2899     al = searchURLLabel(Currentbuf, label);
   2900     if (al == NULL) {
   2901 	/* FIXME: gettextize? */
   2902 	disp_message(Sprintf("%s is not found", label)->ptr, TRUE);
   2903 	return;
   2904     }
   2905     buf = newBuffer(Currentbuf->width);
   2906     copyBuffer(buf, Currentbuf);
   2907     for (i = 0; i < MAX_LB; i++)
   2908 	buf->linkBuffer[i] = NULL;
   2909     buf->currentURL.label = allocStr(label, -1);
   2910     pushHashHist(URLHist, parsedURL2Str(&buf->currentURL)->ptr);
   2911     (*buf->clone)++;
   2912     pushBuffer(buf);
   2913     gotoLine(Currentbuf, al->start.line);
   2914     if (label_topline)
   2915 	Currentbuf->topLine = lineSkip(Currentbuf, Currentbuf->topLine,
   2916 				       Currentbuf->currentLine->linenumber
   2917 				       - Currentbuf->topLine->linenumber,
   2918 				       FALSE);
   2919     Currentbuf->pos = al->start.pos;
   2920     arrangeCursor(Currentbuf);
   2921     displayBuffer(Currentbuf, B_FORCE_REDRAW);
   2922     return;
   2923 }
   2924 
   2925 static int
   2926 handleMailto(char *url)
   2927 {
   2928     Str to;
   2929     char *pos;
   2930 
   2931     if (strncasecmp(url, "mailto:", 7))
   2932 	return 0;
   2933 #ifdef USE_W3MMAILER
   2934     if (! non_null(Mailer) || MailtoOptions == MAILTO_OPTIONS_USE_W3MMAILER)
   2935 	return 0;
   2936 #else
   2937     if (!non_null(Mailer)) {
   2938 	/* FIXME: gettextize? */
   2939 	disp_err_message("no mailer is specified", TRUE);
   2940 	return 1;
   2941     }
   2942 #endif
   2943 	
   2944     /* invoke external mailer */
   2945     if (MailtoOptions == MAILTO_OPTIONS_USE_MAILTO_URL) {
   2946 	to = Strnew_charp(html_unquote(url));
   2947     } else {
   2948 	to = Strnew_charp(url + 7);
   2949 	if ((pos = strchr(to->ptr, '?')) != NULL)
   2950 	    Strtruncate(to, pos - to->ptr);
   2951     }
   2952     fmTerm();
   2953     system(myExtCommand(Mailer, shell_quote(file_unquote(to->ptr)),
   2954 			FALSE)->ptr);
   2955     fmInit();
   2956     displayBuffer(Currentbuf, B_FORCE_REDRAW);
   2957     pushHashHist(URLHist, url);
   2958     return 1;
   2959 }
   2960 
   2961 /* follow HREF link */
   2962 DEFUN(followA, GOTO_LINK, "Go to current link")
   2963 {
   2964     Line *l;
   2965     Anchor *a;
   2966     ParsedURL u;
   2967 #ifdef USE_IMAGE
   2968     int x = 0, y = 0, map = 0;
   2969 #endif
   2970     char *url;
   2971 
   2972     if (Currentbuf->firstLine == NULL)
   2973 	return;
   2974     l = Currentbuf->currentLine;
   2975 
   2976 #ifdef USE_IMAGE
   2977     a = retrieveCurrentImg(Currentbuf);
   2978     if (a && a->image && a->image->map) {
   2979 	_followForm(FALSE);
   2980 	return;
   2981     }
   2982     if (a && a->image && a->image->ismap) {
   2983 	getMapXY(Currentbuf, a, &x, &y);
   2984 	map = 1;
   2985     }
   2986 #else
   2987     a = retrieveCurrentMap(Currentbuf);
   2988     if (a) {
   2989 	_followForm(FALSE);
   2990 	return;
   2991     }
   2992 #endif
   2993     a = retrieveCurrentAnchor(Currentbuf);
   2994     if (a == NULL) {
   2995 	_followForm(FALSE);
   2996 	return;
   2997     }
   2998     if (*a->url == '#') {	/* index within this buffer */
   2999 	gotoLabel(a->url + 1);
   3000 	return;
   3001     }
   3002     parseURL2(a->url, &u, baseURL(Currentbuf));
   3003     if (Strcmp(parsedURL2Str(&u), parsedURL2Str(&Currentbuf->currentURL)) == 0) {
   3004 	/* index within this buffer */
   3005 	if (u.label) {
   3006 	    gotoLabel(u.label);
   3007 	    return;
   3008 	}
   3009     }
   3010     if (handleMailto(a->url))
   3011 	return;
   3012 #if 0
   3013     else if (!strncasecmp(a->url, "news:", 5) && strchr(a->url, '@') == NULL) {
   3014 	/* news:newsgroup is not supported */
   3015 	/* FIXME: gettextize? */
   3016 	disp_err_message("news:newsgroup_name is not supported", TRUE);
   3017 	return;
   3018     }
   3019 #endif				/* USE_NNTP */
   3020     url = a->url;
   3021 #ifdef USE_IMAGE
   3022     if (map)
   3023 	url = Sprintf("%s?%d,%d", a->url, x, y)->ptr;
   3024 #endif
   3025 
   3026     if (check_target && open_tab_blank && a->target &&
   3027 	(!strcasecmp(a->target, "_new") || !strcasecmp(a->target, "_blank"))) {
   3028 	Buffer *buf;
   3029 
   3030 	_newT();
   3031 	buf = Currentbuf;
   3032 	loadLink(url, a->target, a->referer, NULL);
   3033 	if (buf != Currentbuf)
   3034 	    delBuffer(buf);
   3035 	else
   3036 	    deleteTab(CurrentTab);
   3037 	displayBuffer(Currentbuf, B_FORCE_REDRAW);
   3038 	return;
   3039     }
   3040     loadLink(url, a->target, a->referer, NULL);
   3041     displayBuffer(Currentbuf, B_NORMAL);
   3042 }
   3043 
   3044 /* follow HREF link in the buffer */
   3045 void
   3046 bufferA(void)
   3047 {
   3048     on_target = FALSE;
   3049     followA();
   3050     on_target = TRUE;
   3051 }
   3052 
   3053 /* view inline image */
   3054 DEFUN(followI, VIEW_IMAGE, "View image")
   3055 {
   3056     Line *l;
   3057     Anchor *a;
   3058     Buffer *buf;
   3059 
   3060     if (Currentbuf->firstLine == NULL)
   3061 	return;
   3062     l = Currentbuf->currentLine;
   3063 
   3064     a = retrieveCurrentImg(Currentbuf);
   3065     if (a == NULL)
   3066 	return;
   3067     /* FIXME: gettextize? */
   3068     message(Sprintf("loading %s", a->url)->ptr, 0, 0);
   3069     refresh();
   3070     buf = loadGeneralFile(a->url, baseURL(Currentbuf), NULL, 0, NULL);
   3071     if (buf == NULL) {
   3072 	/* FIXME: gettextize? */
   3073 	char *emsg = Sprintf("Can't load %s", a->url)->ptr;
   3074 	disp_err_message(emsg, FALSE);
   3075     }
   3076     else if (buf != NO_BUFFER) {
   3077 	pushBuffer(buf);
   3078     }
   3079     displayBuffer(Currentbuf, B_NORMAL);
   3080 }
   3081 
   3082 static FormItemList *
   3083 save_submit_formlist(FormItemList *src)
   3084 {
   3085     FormList *list;
   3086     FormList *srclist;
   3087     FormItemList *srcitem;
   3088     FormItemList *item;
   3089     FormItemList *ret = NULL;
   3090 #ifdef MENU_SELECT
   3091     FormSelectOptionItem *opt;
   3092     FormSelectOptionItem *curopt;
   3093     FormSelectOptionItem *srcopt;
   3094 #endif				/* MENU_SELECT */
   3095 
   3096     if (src == NULL)
   3097 	return NULL;
   3098     srclist = src->parent;
   3099     list = New(FormList);
   3100     list->method = srclist->method;
   3101     list->action = Strdup(srclist->action);
   3102 #ifdef USE_M17N
   3103     list->charset = srclist->charset;
   3104 #endif
   3105     list->enctype = srclist->enctype;
   3106     list->nitems = srclist->nitems;
   3107     list->body = srclist->body;
   3108     list->boundary = srclist->boundary;
   3109     list->length = srclist->length;
   3110 
   3111     for (srcitem = srclist->item; srcitem; srcitem = srcitem->next) {
   3112 	item = New(FormItemList);
   3113 	item->type = srcitem->type;
   3114 	item->name = Strdup(srcitem->name);
   3115 	item->value = Strdup(srcitem->value);
   3116 	item->checked = srcitem->checked;
   3117 	item->accept = srcitem->accept;
   3118 	item->size = srcitem->size;
   3119 	item->rows = srcitem->rows;
   3120 	item->maxlength = srcitem->maxlength;
   3121 	item->readonly = srcitem->readonly;
   3122 #ifdef MENU_SELECT
   3123 	opt = curopt = NULL;
   3124 	for (srcopt = srcitem->select_option; srcopt; srcopt = srcopt->next) {
   3125 	    if (!srcopt->checked)
   3126 		continue;
   3127 	    opt = New(FormSelectOptionItem);
   3128 	    opt->value = Strdup(srcopt->value);
   3129 	    opt->label = Strdup(srcopt->label);
   3130 	    opt->checked = srcopt->checked;
   3131 	    if (item->select_option == NULL) {
   3132 		item->select_option = curopt = opt;
   3133 	    }
   3134 	    else {
   3135 		curopt->next = opt;
   3136 		curopt = curopt->next;
   3137 	    }
   3138 	}
   3139 	item->select_option = opt;
   3140 	if (srcitem->label)
   3141 	    item->label = Strdup(srcitem->label);
   3142 #endif				/* MENU_SELECT */
   3143 	item->parent = list;
   3144 	item->next = NULL;
   3145 
   3146 	if (list->lastitem == NULL) {
   3147 	    list->item = list->lastitem = item;
   3148 	}
   3149 	else {
   3150 	    list->lastitem->next = item;
   3151 	    list->lastitem = item;
   3152 	}
   3153 
   3154 	if (srcitem == src)
   3155 	    ret = item;
   3156     }
   3157 
   3158     return ret;
   3159 }
   3160 
   3161 #ifdef USE_M17N
   3162 static Str
   3163 conv_form_encoding(Str val, FormItemList *fi, Buffer *buf)
   3164 {
   3165     wc_ces charset = SystemCharset;
   3166 
   3167     if (fi->parent->charset)
   3168 	charset = fi->parent->charset;
   3169     else if (buf->document_charset && buf->document_charset != WC_CES_US_ASCII)
   3170 	charset = buf->document_charset;
   3171     return wc_Str_conv_strict(val, InnerCharset, charset);
   3172 }
   3173 #else
   3174 #define conv_form_encoding(val, fi, buf) (val)
   3175 #endif
   3176 
   3177 static void
   3178 query_from_followform(Str *query, FormItemList *fi, int multipart)
   3179 {
   3180     FormItemList *f2;
   3181     FILE *body = NULL;
   3182 
   3183     if (multipart) {
   3184 	*query = tmpfname(TMPF_DFL, NULL);
   3185 	body = fopen((*query)->ptr, "w");
   3186 	if (body == NULL) {
   3187 	    return;
   3188 	}
   3189 	fi->parent->body = (*query)->ptr;
   3190 	fi->parent->boundary =
   3191 	    Sprintf("------------------------------%d%ld%ld%ld", CurrentPid,
   3192 		    fi->parent, fi->parent->body, fi->parent->boundary)->ptr;
   3193     }
   3194     *query = Strnew();
   3195     for (f2 = fi->parent->item; f2; f2 = f2->next) {
   3196 	if (f2->name == NULL)
   3197 	    continue;
   3198 	/* <ISINDEX> is translated into single text form */
   3199 	if (f2->name->length == 0 &&
   3200 	    (multipart || f2->type != FORM_INPUT_TEXT))
   3201 	    continue;
   3202 	switch (f2->type) {
   3203 	case FORM_INPUT_RESET:
   3204 	    /* do nothing */
   3205 	    continue;
   3206 	case FORM_INPUT_SUBMIT:
   3207 	case FORM_INPUT_IMAGE:
   3208 	    if (f2 != fi || f2->value == NULL)
   3209 		continue;
   3210 	    break;
   3211 	case FORM_INPUT_RADIO:
   3212 	case FORM_INPUT_CHECKBOX:
   3213 	    if (!f2->checked)
   3214 		continue;
   3215 	}
   3216 	if (multipart) {
   3217 	    if (f2->type == FORM_INPUT_IMAGE) {
   3218 		int x = 0, y = 0;
   3219 #ifdef USE_IMAGE
   3220 		getMapXY(Currentbuf, retrieveCurrentImg(Currentbuf), &x, &y);
   3221 #endif
   3222 		*query = Strdup(conv_form_encoding(f2->name, fi, Currentbuf));
   3223 		Strcat_charp(*query, ".x");
   3224 		form_write_data(body, fi->parent->boundary, (*query)->ptr,
   3225 				Sprintf("%d", x)->ptr);
   3226 		*query = Strdup(conv_form_encoding(f2->name, fi, Currentbuf));
   3227 		Strcat_charp(*query, ".y");
   3228 		form_write_data(body, fi->parent->boundary, (*query)->ptr,
   3229 				Sprintf("%d", y)->ptr);
   3230 	    }
   3231 	    else if (f2->name && f2->name->length > 0 && f2->value != NULL) {
   3232 		/* not IMAGE */
   3233 		*query = conv_form_encoding(f2->value, fi, Currentbuf);
   3234 		if (f2->type == FORM_INPUT_FILE)
   3235 		    form_write_from_file(body, fi->parent->boundary,
   3236 					 conv_form_encoding(f2->name, fi,
   3237 							    Currentbuf)->ptr,
   3238 					 (*query)->ptr,
   3239 					 Str_conv_to_system(f2->value)->ptr);
   3240 		else
   3241 		    form_write_data(body, fi->parent->boundary,
   3242 				    conv_form_encoding(f2->name, fi,
   3243 						       Currentbuf)->ptr,
   3244 				    (*query)->ptr);
   3245 	    }
   3246 	}
   3247 	else {
   3248 	    /* not multipart */
   3249 	    if (f2->type == FORM_INPUT_IMAGE) {
   3250 		int x = 0, y = 0;
   3251 #ifdef USE_IMAGE
   3252 		getMapXY(Currentbuf, retrieveCurrentImg(Currentbuf), &x, &y);
   3253 #endif
   3254 		Strcat(*query,
   3255 		       Str_form_quote(conv_form_encoding
   3256 				      (f2->name, fi, Currentbuf)));
   3257 		Strcat(*query, Sprintf(".x=%d&", x));
   3258 		Strcat(*query,
   3259 		       Str_form_quote(conv_form_encoding
   3260 				      (f2->name, fi, Currentbuf)));
   3261 		Strcat(*query, Sprintf(".y=%d", y));
   3262 	    }
   3263 	    else {
   3264 		/* not IMAGE */
   3265 		if (f2->name && f2->name->length > 0) {
   3266 		    Strcat(*query,
   3267 			   Str_form_quote(conv_form_encoding
   3268 					  (f2->name, fi, Currentbuf)));
   3269 		    Strcat_char(*query, '=');
   3270 		}
   3271 		if (f2->value != NULL) {
   3272 		    if (fi->parent->method == FORM_METHOD_INTERNAL)
   3273 			Strcat(*query, Str_form_quote(f2->value));
   3274 		    else {
   3275 			Strcat(*query,
   3276 			       Str_form_quote(conv_form_encoding
   3277 					      (f2->value, fi, Currentbuf)));
   3278 		    }
   3279 		}
   3280 	    }
   3281 	    if (f2->next)
   3282 		Strcat_char(*query, '&');
   3283 	}
   3284     }
   3285     if (multipart) {
   3286 	fprintf(body, "--%s--\r\n", fi->parent->boundary);
   3287 	fclose(body);
   3288     }
   3289     else {
   3290 	/* remove trailing & */
   3291 	while (Strlastchar(*query) == '&')
   3292 	    Strshrink(*query, 1);
   3293     }
   3294 }
   3295 
   3296 /* submit form */
   3297 DEFUN(submitForm, SUBMIT, "Submit form")
   3298 {
   3299     _followForm(TRUE);
   3300 }
   3301 
   3302 /* process form */
   3303 void
   3304 followForm(void)
   3305 {
   3306     _followForm(FALSE);
   3307 }
   3308 
   3309 static void
   3310 _followForm(int submit)
   3311 {
   3312     Line *l;
   3313     Anchor *a, *a2;
   3314     char *p;
   3315     FormItemList *fi, *f2;
   3316     Str tmp, tmp2;
   3317     int multipart = 0, i;
   3318 
   3319     if (Currentbuf->firstLine == NULL)
   3320 	return;
   3321     l = Currentbuf->currentLine;
   3322 
   3323     a = retrieveCurrentForm(Currentbuf);
   3324     if (a == NULL)
   3325 	return;
   3326     fi = (FormItemList *)a->url;
   3327     switch (fi->type) {
   3328     case FORM_INPUT_TEXT:
   3329 	if (submit)
   3330 	    goto do_submit;
   3331 	if (fi->readonly)
   3332 	    /* FIXME: gettextize? */
   3333 	    disp_message_nsec("Read only field!", FALSE, 1, TRUE, FALSE);
   3334 	/* FIXME: gettextize? */
   3335 	p = inputStrHist("TEXT:", fi->value ? fi->value->ptr : NULL, TextHist);
   3336 	if (p == NULL || fi->readonly)
   3337 	    break;
   3338 	fi->value = Strnew_charp(p);
   3339 	formUpdateBuffer(a, Currentbuf, fi);
   3340 	if (fi->accept || fi->parent->nitems == 1)
   3341 	    goto do_submit;
   3342 	break;
   3343     case FORM_INPUT_FILE:
   3344 	if (submit)
   3345 	    goto do_submit;
   3346 	if (fi->readonly)
   3347 	    /* FIXME: gettextize? */
   3348 	    disp_message_nsec("Read only field!", FALSE, 1, TRUE, FALSE);
   3349 	/* FIXME: gettextize? */
   3350 	p = inputFilenameHist("Filename:", fi->value ? fi->value->ptr : NULL,
   3351 			      NULL);
   3352 	if (p == NULL || fi->readonly)
   3353 	    break;
   3354 	fi->value = Strnew_charp(p);
   3355 	formUpdateBuffer(a, Currentbuf, fi);
   3356 	if (fi->accept || fi->parent->nitems == 1)
   3357 	    goto do_submit;
   3358 	break;
   3359     case FORM_INPUT_PASSWORD:
   3360 	if (submit)
   3361 	    goto do_submit;
   3362 	if (fi->readonly) {
   3363 	    /* FIXME: gettextize? */
   3364 	    disp_message_nsec("Read only field!", FALSE, 1, TRUE, FALSE);
   3365 	    break;
   3366 	}
   3367 	/* FIXME: gettextize? */
   3368 	p = inputLine("Password:", fi->value ? fi->value->ptr : NULL,
   3369 		      IN_PASSWORD);
   3370 	if (p == NULL)
   3371 	    break;
   3372 	fi->value = Strnew_charp(p);
   3373 	formUpdateBuffer(a, Currentbuf, fi);
   3374 	if (fi->accept)
   3375 	    goto do_submit;
   3376 	break;
   3377     case FORM_TEXTAREA:
   3378 	if (submit)
   3379 	    goto do_submit;
   3380 	if (fi->readonly)
   3381 	    /* FIXME: gettextize? */
   3382 	    disp_message_nsec("Read only field!", FALSE, 1, TRUE, FALSE);
   3383 	input_textarea(fi);
   3384 	formUpdateBuffer(a, Currentbuf, fi);
   3385 	break;
   3386     case FORM_INPUT_RADIO:
   3387 	if (submit)
   3388 	    goto do_submit;
   3389 	if (fi->readonly) {
   3390 	    /* FIXME: gettextize? */
   3391 	    disp_message_nsec("Read only field!", FALSE, 1, TRUE, FALSE);
   3392 	    break;
   3393 	}
   3394 	formRecheckRadio(a, Currentbuf, fi);
   3395 	break;
   3396     case FORM_INPUT_CHECKBOX:
   3397 	if (submit)
   3398 	    goto do_submit;
   3399 	if (fi->readonly) {
   3400 	    /* FIXME: gettextize? */
   3401 	    disp_message_nsec("Read only field!", FALSE, 1, TRUE, FALSE);
   3402 	    break;
   3403 	}
   3404 	fi->checked = !fi->checked;
   3405 	formUpdateBuffer(a, Currentbuf, fi);
   3406 	break;
   3407 #ifdef MENU_SELECT
   3408     case FORM_SELECT:
   3409 	if (submit)
   3410 	    goto do_submit;
   3411 	if (!formChooseOptionByMenu(fi,
   3412 				    Currentbuf->cursorX - Currentbuf->pos +
   3413 				    a->start.pos + Currentbuf->rootX,
   3414 				    Currentbuf->cursorY + Currentbuf->rootY))
   3415 	    break;
   3416 	formUpdateBuffer(a, Currentbuf, fi);
   3417 	if (fi->parent->nitems == 1)
   3418 	    goto do_submit;
   3419 	break;
   3420 #endif				/* MENU_SELECT */
   3421     case FORM_INPUT_IMAGE:
   3422     case FORM_INPUT_SUBMIT:
   3423     case FORM_INPUT_BUTTON:
   3424       do_submit:
   3425 	tmp = Strnew();
   3426 	tmp2 = Strnew();
   3427 	multipart = (fi->parent->method == FORM_METHOD_POST &&
   3428 		     fi->parent->enctype == FORM_ENCTYPE_MULTIPART);
   3429 	query_from_followform(&tmp, fi, multipart);
   3430 
   3431 	tmp2 = Strdup(fi->parent->action);
   3432 	if (!Strcmp_charp(tmp2, "!CURRENT_URL!")) {
   3433 	    /* It means "current URL" */
   3434 	    tmp2 = parsedURL2Str(&Currentbuf->currentURL);
   3435 	    if ((p = strchr(tmp2->ptr, '?')) != NULL)
   3436 		Strshrink(tmp2, (tmp2->ptr + tmp2->length) - p);
   3437 	}
   3438 
   3439 	if (fi->parent->method == FORM_METHOD_GET) {
   3440 	    if ((p = strchr(tmp2->ptr, '?')) != NULL)
   3441 		Strshrink(tmp2, (tmp2->ptr + tmp2->length) - p);
   3442 	    Strcat_charp(tmp2, "?");
   3443 	    Strcat(tmp2, tmp);
   3444 	    loadLink(tmp2->ptr, a->target, NULL, NULL);
   3445 	}
   3446 	else if (fi->parent->method == FORM_METHOD_POST) {
   3447 	    Buffer *buf;
   3448 	    if (multipart) {
   3449 		struct stat st;
   3450 		stat(fi->parent->body, &st);
   3451 		fi->parent->length = st.st_size;
   3452 	    }
   3453 	    else {
   3454 		fi->parent->body = tmp->ptr;
   3455 		fi->parent->length = tmp->length;
   3456 	    }
   3457 	    buf = loadLink(tmp2->ptr, a->target, NULL, fi->parent);
   3458 	    if (multipart) {
   3459 		unlink(fi->parent->body);
   3460 	    }
   3461 	    if (buf && !(buf->bufferprop & BP_REDIRECTED)) {	/* buf must be Currentbuf */
   3462 		/* BP_REDIRECTED means that the buffer is obtained through
   3463 		 * Location: header. In this case, buf->form_submit must not be set
   3464 		 * because the page is not loaded by POST method but GET method.
   3465 		 */
   3466 		buf->form_submit = save_submit_formlist(fi);
   3467 	    }
   3468 	}
   3469 	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 */
   3470 	    do_internal(tmp2->ptr, tmp->ptr);
   3471 	}
   3472 	else {
   3473 	    disp_err_message("Can't send form because of illegal method.",
   3474 			     FALSE);
   3475 	}
   3476 	break;
   3477     case FORM_INPUT_RESET:
   3478 	for (i = 0; i < Currentbuf->formitem->nanchor; i++) {
   3479 	    a2 = &Currentbuf->formitem->anchors[i];
   3480 	    f2 = (FormItemList *)a2->url;
   3481 	    if (f2->parent == fi->parent &&
   3482 		f2->name && f2->value &&
   3483 		f2->type != FORM_INPUT_SUBMIT &&
   3484 		f2->type != FORM_INPUT_HIDDEN &&
   3485 		f2->type != FORM_INPUT_RESET) {
   3486 		f2->value = f2->init_value;
   3487 		f2->checked = f2->init_checked;
   3488 #ifdef MENU_SELECT
   3489 		f2->label = f2->init_label;
   3490 		f2->selected = f2->init_selected;
   3491 #endif				/* MENU_SELECT */
   3492 		formUpdateBuffer(a2, Currentbuf, f2);
   3493 	    }
   3494 	}
   3495 	break;
   3496     case FORM_INPUT_HIDDEN:
   3497     default:
   3498 	break;
   3499     }
   3500     displayBuffer(Currentbuf, B_FORCE_REDRAW);
   3501 }
   3502 
   3503 /* go to the top anchor */
   3504 DEFUN(topA, LINK_BEGIN, "Go to the first link")
   3505 {
   3506     HmarkerList *hl = Currentbuf->hmarklist;
   3507     BufferPoint *po;
   3508     Anchor *an;
   3509     int hseq = 0;
   3510 
   3511     if (Currentbuf->firstLine == NULL)
   3512 	return;
   3513     if (!hl || hl->nmark == 0)
   3514 	return;
   3515 
   3516     if (prec_num > hl->nmark)
   3517 	hseq = hl->nmark - 1;
   3518     else if (prec_num > 0)
   3519 	hseq = prec_num - 1;
   3520     do {
   3521 	if (hseq >= hl->nmark)
   3522 	    return;
   3523 	po = hl->marks + hseq;
   3524 	an = retrieveAnchor(Currentbuf->href, po->line, po->pos);
   3525 	if (an == NULL)
   3526 	    an = retrieveAnchor(Currentbuf->formitem, po->line, po->pos);
   3527 	hseq++;
   3528     } while (an == NULL);
   3529 
   3530     gotoLine(Currentbuf, po->line);
   3531     Currentbuf->pos = po->pos;
   3532     arrangeCursor(Currentbuf);
   3533     displayBuffer(Currentbuf, B_NORMAL);
   3534 }
   3535 
   3536 /* go to the last anchor */
   3537 DEFUN(lastA, LINK_END, "Go to the last link")
   3538 {
   3539     HmarkerList *hl = Currentbuf->hmarklist;
   3540     BufferPoint *po;
   3541     Anchor *an;
   3542     int hseq;
   3543 
   3544     if (Currentbuf->firstLine == NULL)
   3545 	return;
   3546     if (!hl || hl->nmark == 0)
   3547 	return;
   3548 
   3549     if (prec_num >= hl->nmark)
   3550 	hseq = 0;
   3551     else if (prec_num > 0)
   3552 	hseq = hl->nmark - prec_num;
   3553     else
   3554 	hseq = hl->nmark - 1;
   3555     do {
   3556 	if (hseq < 0)
   3557 	    return;
   3558 	po = hl->marks + hseq;
   3559 	an = retrieveAnchor(Currentbuf->href, po->line, po->pos);
   3560 	if (an == NULL)
   3561 	    an = retrieveAnchor(Currentbuf->formitem, po->line, po->pos);
   3562 	hseq--;
   3563     } while (an == NULL);
   3564 
   3565     gotoLine(Currentbuf, po->line);
   3566     Currentbuf->pos = po->pos;
   3567     arrangeCursor(Currentbuf);
   3568     displayBuffer(Currentbuf, B_NORMAL);
   3569 }
   3570 
   3571 /* go to the next anchor */
   3572 DEFUN(nextA, NEXT_LINK, "Move to next link")
   3573 {
   3574     _nextA(FALSE);
   3575 }
   3576 
   3577 /* go to the previous anchor */
   3578 DEFUN(prevA, PREV_LINK, "Move to previous link")
   3579 {
   3580     _prevA(FALSE);
   3581 }
   3582 
   3583 /* go to the next visited anchor */
   3584 DEFUN(nextVA, NEXT_VISITED, "Move to next visited link")
   3585 {
   3586     _nextA(TRUE);
   3587 }
   3588 
   3589 /* go to the previous visited anchor */
   3590 DEFUN(prevVA, PREV_VISITED, "Move to previous visited link")
   3591 {
   3592     _prevA(TRUE);
   3593 }
   3594 
   3595 /* go to the next [visited] anchor */
   3596 static void
   3597 _nextA(int visited)
   3598 {
   3599     HmarkerList *hl = Currentbuf->hmarklist;
   3600     BufferPoint *po;
   3601     Anchor *an, *pan;
   3602     int i, x, y, n = searchKeyNum();
   3603     ParsedURL url;
   3604 
   3605     if (Currentbuf->firstLine == NULL)
   3606 	return;
   3607     if (!hl || hl->nmark == 0)
   3608 	return;
   3609 
   3610     an = retrieveCurrentAnchor(Currentbuf);
   3611     if (visited != TRUE && an == NULL)
   3612 	an = retrieveCurrentForm(Currentbuf);
   3613 
   3614     y = Currentbuf->currentLine->linenumber;
   3615     x = Currentbuf->pos;
   3616 
   3617     if (visited == TRUE) {
   3618 	n = hl->nmark;
   3619     }
   3620 
   3621     for (i = 0; i < n; i++) {
   3622 	pan = an;
   3623 	if (an && an->hseq >= 0) {
   3624 	    int hseq = an->hseq + 1;
   3625 	    do {
   3626 		if (hseq >= hl->nmark) {
   3627 		    if (visited == TRUE)
   3628 			return;
   3629 		    an = pan;
   3630 		    goto _end;
   3631 		}
   3632 		po = &hl->marks[hseq];
   3633 		an = retrieveAnchor(Currentbuf->href, po->line, po->pos);
   3634 		if (visited != TRUE && an == NULL)
   3635 		    an = retrieveAnchor(Currentbuf->formitem, po->line,
   3636 					po->pos);
   3637 		hseq++;
   3638 		if (visited == TRUE && an) {
   3639 		    parseURL2(an->url, &url, baseURL(Currentbuf));
   3640 		    if (getHashHist(URLHist, parsedURL2Str(&url)->ptr)) {
   3641 			goto _end;
   3642 		    }
   3643 		}
   3644 	    } while (an == NULL || an == pan);
   3645 	}
   3646 	else {
   3647 	    an = closest_next_anchor(Currentbuf->href, NULL, x, y);
   3648 	    if (visited != TRUE)
   3649 		an = closest_next_anchor(Currentbuf->formitem, an, x, y);
   3650 	    if (an == NULL) {
   3651 		if (visited == TRUE)
   3652 		    return;
   3653 		an = pan;
   3654 		break;
   3655 	    }
   3656 	    x = an->start.pos;
   3657 	    y = an->start.line;
   3658 	    if (visited == TRUE) {
   3659 		parseURL2(an->url, &url, baseURL(Currentbuf));
   3660 		if (getHashHist(URLHist, parsedURL2Str(&url)->ptr)) {
   3661 		    goto _end;
   3662 		}
   3663 	    }
   3664 	}
   3665     }
   3666     if (visited == TRUE)
   3667 	return;
   3668 
   3669   _end:
   3670     if (an == NULL || an->hseq < 0)
   3671 	return;
   3672     po = &hl->marks[an->hseq];
   3673     gotoLine(Currentbuf, po->line);
   3674     Currentbuf->pos = po->pos;
   3675     arrangeCursor(Currentbuf);
   3676     displayBuffer(Currentbuf, B_NORMAL);
   3677 }
   3678 
   3679 /* go to the previous anchor */
   3680 static void
   3681 _prevA(int visited)
   3682 {
   3683     HmarkerList *hl = Currentbuf->hmarklist;
   3684     BufferPoint *po;
   3685     Anchor *an, *pan;
   3686     int i, x, y, n = searchKeyNum();
   3687     ParsedURL url;
   3688 
   3689     if (Currentbuf->firstLine == NULL)
   3690 	return;
   3691     if (!hl || hl->nmark == 0)
   3692 	return;
   3693 
   3694     an = retrieveCurrentAnchor(Currentbuf);
   3695     if (visited != TRUE && an == NULL)
   3696 	an = retrieveCurrentForm(Currentbuf);
   3697 
   3698     y = Currentbuf->currentLine->linenumber;
   3699     x = Currentbuf->pos;
   3700 
   3701     if (visited == TRUE) {
   3702 	n = hl->nmark;
   3703     }
   3704 
   3705     for (i = 0; i < n; i++) {
   3706 	pan = an;
   3707 	if (an && an->hseq >= 0) {
   3708 	    int hseq = an->hseq - 1;
   3709 	    do {
   3710 		if (hseq < 0) {
   3711 		    if (visited == TRUE)
   3712 			return;
   3713 		    an = pan;
   3714 		    goto _end;
   3715 		}
   3716 		po = hl->marks + hseq;
   3717 		an = retrieveAnchor(Currentbuf->href, po->line, po->pos);
   3718 		if (visited != TRUE && an == NULL)
   3719 		    an = retrieveAnchor(Currentbuf->formitem, po->line,
   3720 					po->pos);
   3721 		hseq--;
   3722 		if (visited == TRUE && an) {
   3723 		    parseURL2(an->url, &url, baseURL(Currentbuf));
   3724 		    if (getHashHist(URLHist, parsedURL2Str(&url)->ptr)) {
   3725 			goto _end;
   3726 		    }
   3727 		}
   3728 	    } while (an == NULL || an == pan);
   3729 	}
   3730 	else {
   3731 	    an = closest_prev_anchor(Currentbuf->href, NULL, x, y);
   3732 	    if (visited != TRUE)
   3733 		an = closest_prev_anchor(Currentbuf->formitem, an, x, y);
   3734 	    if (an == NULL) {
   3735 		if (visited == TRUE)
   3736 		    return;
   3737 		an = pan;
   3738 		break;
   3739 	    }
   3740 	    x = an->start.pos;
   3741 	    y = an->start.line;
   3742 	    if (visited == TRUE && an) {
   3743 		parseURL2(an->url, &url, baseURL(Currentbuf));
   3744 		if (getHashHist(URLHist, parsedURL2Str(&url)->ptr)) {
   3745 		    goto _end;
   3746 		}
   3747 	    }
   3748 	}
   3749     }
   3750     if (visited == TRUE)
   3751 	return;
   3752 
   3753   _end:
   3754     if (an == NULL || an->hseq < 0)
   3755 	return;
   3756     po = hl->marks + an->hseq;
   3757     gotoLine(Currentbuf, po->line);
   3758     Currentbuf->pos = po->pos;
   3759     arrangeCursor(Currentbuf);
   3760     displayBuffer(Currentbuf, B_NORMAL);
   3761 }
   3762 
   3763 /* go to the next left/right anchor */
   3764 static void
   3765 nextX(int d, int dy)
   3766 {
   3767     HmarkerList *hl = Currentbuf->hmarklist;
   3768     Anchor *an, *pan;
   3769     Line *l;
   3770     int i, x, y, n = searchKeyNum();
   3771 
   3772     if (Currentbuf->firstLine == NULL)
   3773 	return;
   3774     if (!hl || hl->nmark == 0)
   3775 	return;
   3776 
   3777     an = retrieveCurrentAnchor(Currentbuf);
   3778     if (an == NULL)
   3779 	an = retrieveCurrentForm(Currentbuf);
   3780 
   3781     l = Currentbuf->currentLine;
   3782     x = Currentbuf->pos;
   3783     y = l->linenumber;
   3784     pan = NULL;
   3785     for (i = 0; i < n; i++) {
   3786 	if (an)
   3787 	    x = (d > 0) ? an->end.pos : an->start.pos - 1;
   3788 	an = NULL;
   3789 	while (1) {
   3790 	    for (; x >= 0 && x < l->len; x += d) {
   3791 		an = retrieveAnchor(Currentbuf->href, y, x);
   3792 		if (!an)
   3793 		    an = retrieveAnchor(Currentbuf->formitem, y, x);
   3794 		if (an) {
   3795 		    pan = an;
   3796 		    break;
   3797 		}
   3798 	    }
   3799 	    if (!dy || an)
   3800 		break;
   3801 	    l = (dy > 0) ? l->next : l->prev;
   3802 	    if (!l)
   3803 		break;
   3804 	    x = (d > 0) ? 0 : l->len - 1;
   3805 	    y = l->linenumber;
   3806 	}
   3807 	if (!an)
   3808 	    break;
   3809     }
   3810 
   3811     if (pan == NULL)
   3812 	return;
   3813     gotoLine(Currentbuf, y);
   3814     Currentbuf->pos = pan->start.pos;
   3815     arrangeCursor(Currentbuf);
   3816     displayBuffer(Currentbuf, B_NORMAL);
   3817 }
   3818 
   3819 /* go to the next downward/upward anchor */
   3820 static void
   3821 nextY(int d)
   3822 {
   3823     HmarkerList *hl = Currentbuf->hmarklist;
   3824     Anchor *an, *pan;
   3825     int i, x, y, n = searchKeyNum();
   3826     int hseq;
   3827 
   3828     if (Currentbuf->firstLine == NULL)
   3829 	return;
   3830     if (!hl || hl->nmark == 0)
   3831 	return;
   3832 
   3833     an = retrieveCurrentAnchor(Currentbuf);
   3834     if (an == NULL)
   3835 	an = retrieveCurrentForm(Currentbuf);
   3836 
   3837     x = Currentbuf->pos;
   3838     y = Currentbuf->currentLine->linenumber + d;
   3839     pan = NULL;
   3840     hseq = -1;
   3841     for (i = 0; i < n; i++) {
   3842 	if (an)
   3843 	    hseq = abs(an->hseq);
   3844 	an = NULL;
   3845 	for (; y >= 0 && y <= Currentbuf->lastLine->linenumber; y += d) {
   3846 	    an = retrieveAnchor(Currentbuf->href, y, x);
   3847 	    if (!an)
   3848 		an = retrieveAnchor(Currentbuf->formitem, y, x);
   3849 	    if (an && hseq != abs(an->hseq)) {
   3850 		pan = an;
   3851 		break;
   3852 	    }
   3853 	}
   3854 	if (!an)
   3855 	    break;
   3856     }
   3857 
   3858     if (pan == NULL)
   3859 	return;
   3860     gotoLine(Currentbuf, pan->start.line);
   3861     arrangeLine(Currentbuf);
   3862     displayBuffer(Currentbuf, B_NORMAL);
   3863 }
   3864 
   3865 /* go to the next left anchor */
   3866 DEFUN(nextL, NEXT_LEFT, "Move to next left link")
   3867 {
   3868     nextX(-1, 0);
   3869 }
   3870 
   3871 /* go to the next left-up anchor */
   3872 DEFUN(nextLU, NEXT_LEFT_UP, "Move to next left (or upward) link")
   3873 {
   3874     nextX(-1, -1);
   3875 }
   3876 
   3877 /* go to the next right anchor */
   3878 DEFUN(nextR, NEXT_RIGHT, "Move to next right link")
   3879 {
   3880     nextX(1, 0);
   3881 }
   3882 
   3883 /* go to the next right-down anchor */
   3884 DEFUN(nextRD, NEXT_RIGHT_DOWN, "Move to next right (or downward) link")
   3885 {
   3886     nextX(1, 1);
   3887 }
   3888 
   3889 /* go to the next downward anchor */
   3890 DEFUN(nextD, NEXT_DOWN, "Move to next downward link")
   3891 {
   3892     nextY(1);
   3893 }
   3894 
   3895 /* go to the next upward anchor */
   3896 DEFUN(nextU, NEXT_UP, "Move to next upward link")
   3897 {
   3898     nextY(-1);
   3899 }
   3900 
   3901 /* go to the next bufferr */
   3902 DEFUN(nextBf, NEXT, "Move to next buffer")
   3903 {
   3904     Buffer *buf;
   3905     int i;
   3906 
   3907     for (i = 0; i < PREC_NUM; i++) {
   3908 	buf = prevBuffer(Firstbuf, Currentbuf);
   3909 	if (!buf) {
   3910 	    if (i == 0)
   3911 		return;
   3912 	    break;
   3913 	}
   3914 	Currentbuf = buf;
   3915     }
   3916     displayBuffer(Currentbuf, B_FORCE_REDRAW);
   3917 }
   3918 
   3919 /* go to the previous bufferr */
   3920 DEFUN(prevBf, PREV, "Move to previous buffer")
   3921 {
   3922     Buffer *buf;
   3923     int i;
   3924 
   3925     for (i = 0; i < PREC_NUM; i++) {
   3926 	buf = Currentbuf->nextBuffer;
   3927 	if (!buf) {
   3928 	    if (i == 0)
   3929 		return;
   3930 	    break;
   3931 	}
   3932 	Currentbuf = buf;
   3933     }
   3934     displayBuffer(Currentbuf, B_FORCE_REDRAW);
   3935 }
   3936 
   3937 static int
   3938 checkBackBuffer(Buffer *buf)
   3939 {
   3940     Buffer *fbuf = buf->linkBuffer[LB_N_FRAME];
   3941 
   3942     if (fbuf) {
   3943 	if (fbuf->frameQ)
   3944 	    return TRUE;	/* Currentbuf has stacked frames */
   3945 	/* when no frames stacked and next is frame source, try next's
   3946 	 * nextBuffer */
   3947 	if (RenderFrame && fbuf == buf->nextBuffer) {
   3948 	    if (fbuf->nextBuffer != NULL)
   3949 		return TRUE;
   3950 	    else
   3951 		return FALSE;
   3952 	}
   3953     }
   3954 
   3955     if (buf->nextBuffer)
   3956 	return TRUE;
   3957 
   3958     return FALSE;
   3959 }
   3960 
   3961 /* delete current buffer and back to the previous buffer */
   3962 DEFUN(backBf, BACK, "Back to previous buffer")
   3963 {
   3964     Buffer *buf = Currentbuf->linkBuffer[LB_N_FRAME];
   3965 
   3966     if (!checkBackBuffer(Currentbuf)) {
   3967 	if (close_tab_back && nTab >= 1) {
   3968 	    deleteTab(CurrentTab);
   3969 	    displayBuffer(Currentbuf, B_FORCE_REDRAW);
   3970 	}
   3971 	else
   3972 	    /* FIXME: gettextize? */
   3973 	    disp_message("Can't back...", TRUE);
   3974 	return;
   3975     }
   3976 
   3977     delBuffer(Currentbuf);
   3978 
   3979     if (buf) {
   3980 	if (buf->frameQ) {
   3981 	    struct frameset *fs;
   3982 	    long linenumber = buf->frameQ->linenumber;
   3983 	    long top = buf->frameQ->top_linenumber;
   3984 	    int pos = buf->frameQ->pos;
   3985 	    int currentColumn = buf->frameQ->currentColumn;
   3986 	    AnchorList *formitem = buf->frameQ->formitem;
   3987 
   3988 	    fs = popFrameTree(&(buf->frameQ));
   3989 	    deleteFrameSet(buf->frameset);
   3990 	    buf->frameset = fs;
   3991 
   3992 	    if (buf == Currentbuf) {
   3993 		rFrame();
   3994 		Currentbuf->topLine = lineSkip(Currentbuf,
   3995 					       Currentbuf->firstLine, top - 1,
   3996 					       FALSE);
   3997 		gotoLine(Currentbuf, linenumber);
   3998 		Currentbuf->pos = pos;
   3999 		Currentbuf->currentColumn = currentColumn;
   4000 		arrangeCursor(Currentbuf);
   4001 		formResetBuffer(Currentbuf, formitem);
   4002 	    }
   4003 	}
   4004 	else if (RenderFrame && buf == Currentbuf) {
   4005 	    delBuffer(Currentbuf);
   4006 	}
   4007     }
   4008     displayBuffer(Currentbuf, B_FORCE_REDRAW);
   4009 }
   4010 
   4011 DEFUN(deletePrevBuf, DELETE_PREVBUF,
   4012       "Delete previous buffer (mainly for local-CGI)")
   4013 {
   4014     Buffer *buf = Currentbuf->nextBuffer;
   4015     if (buf)
   4016 	delBuffer(buf);
   4017 }
   4018 
   4019 static void
   4020 cmd_loadURL(char *url, ParsedURL *current, char *referer, FormList *request)
   4021 {
   4022     Buffer *buf;
   4023 
   4024     if (handleMailto(url))
   4025 	return;
   4026 #if 0
   4027     if (!strncasecmp(url, "news:", 5) && strchr(url, '@') == NULL) {
   4028 	/* news:newsgroup is not supported */
   4029 	/* FIXME: gettextize? */
   4030 	disp_err_message("news:newsgroup_name is not supported", TRUE);
   4031 	return;
   4032     }
   4033 #endif				/* USE_NNTP */
   4034 
   4035     refresh();
   4036     buf = loadGeneralFile(url, current, referer, 0, request);
   4037     if (buf == NULL) {
   4038 	/* FIXME: gettextize? */
   4039 	char *emsg = Sprintf("Can't load %s", conv_from_system(url))->ptr;
   4040 	disp_err_message(emsg, FALSE);
   4041     }
   4042     else if (buf != NO_BUFFER) {
   4043 	pushBuffer(buf);
   4044 	if (RenderFrame && Currentbuf->frameset != NULL)
   4045 	    rFrame();
   4046     }
   4047     displayBuffer(Currentbuf, B_NORMAL);
   4048 }
   4049 
   4050 
   4051 /* go to specified URL */
   4052 static void
   4053 goURL0(char *prompt, int relative)
   4054 {
   4055     char *url, *referer;
   4056     ParsedURL p_url, *current;
   4057     Buffer *cur_buf = Currentbuf;
   4058 
   4059     url = searchKeyData();
   4060     if (url == NULL) {
   4061 	Hist *hist = copyHist(URLHist);
   4062 	Anchor *a;
   4063 
   4064 	current = baseURL(Currentbuf);
   4065 	if (current) {
   4066 	    char *c_url = parsedURL2Str(current)->ptr;
   4067 	    if (DefaultURLString == DEFAULT_URL_CURRENT) {
   4068 		url = c_url;
   4069 		if (DecodeURL)
   4070 		    url = url_unquote_conv(url, 0);
   4071 	    }
   4072 	    else
   4073 		pushHist(hist, c_url);
   4074 	}
   4075 	a = retrieveCurrentAnchor(Currentbuf);
   4076 	if (a) {
   4077 	    char *a_url;
   4078 	    parseURL2(a->url, &p_url, current);
   4079 	    a_url = parsedURL2Str(&p_url)->ptr;
   4080 	    if (DefaultURLString == DEFAULT_URL_LINK) {
   4081 		url = a_url;
   4082 		if (DecodeURL)
   4083 		    url = url_unquote_conv(url, Currentbuf->document_charset);
   4084 	    }
   4085 	    else
   4086 		pushHist(hist, a_url);
   4087 	}
   4088 	url = inputLineHist(prompt, url, IN_URL, hist);
   4089 	if (url != NULL)
   4090 	    SKIP_BLANKS(url);
   4091     }
   4092 #ifdef USE_M17N
   4093     if (url != NULL) {
   4094 	if ((relative || *url == '#') && Currentbuf->document_charset)
   4095 	    url = wc_conv_strict(url, InnerCharset,
   4096 				 Currentbuf->document_charset)->ptr;
   4097 	else
   4098 	    url = conv_to_system(url);
   4099     }
   4100 #endif
   4101     if (url == NULL || *url == '\0') {
   4102 	displayBuffer(Currentbuf, B_FORCE_REDRAW);
   4103 	return;
   4104     }
   4105     if (*url == '#') {
   4106 	gotoLabel(url + 1);
   4107 	return;
   4108     }
   4109     if (relative) {
   4110 	current = baseURL(Currentbuf);
   4111 	referer = parsedURL2Str(&Currentbuf->currentURL)->ptr;
   4112     }
   4113     else {
   4114 	current = NULL;
   4115 	referer = NULL;
   4116     }
   4117     parseURL2(url, &p_url, current);
   4118     pushHashHist(URLHist, parsedURL2Str(&p_url)->ptr);
   4119     cmd_loadURL(url, current, referer, NULL);
   4120     if (Currentbuf != cur_buf)	/* success */
   4121 	pushHashHist(URLHist, parsedURL2Str(&Currentbuf->currentURL)->ptr);
   4122 }
   4123 
   4124 DEFUN(goURL, GOTO, "Go to URL")
   4125 {
   4126     goURL0("Goto URL: ", FALSE);
   4127 }
   4128 
   4129 DEFUN(gorURL, GOTO_RELATIVE, "Go to relative URL")
   4130 {
   4131     goURL0("Goto relative URL: ", TRUE);
   4132 }
   4133 
   4134 static void
   4135 cmd_loadBuffer(Buffer *buf, int prop, int linkid)
   4136 {
   4137     if (buf == NULL) {
   4138 	disp_err_message("Can't load string", FALSE);
   4139     }
   4140     else if (buf != NO_BUFFER) {
   4141 	buf->bufferprop |= (BP_INTERNAL | prop);
   4142 	if (!(buf->bufferprop & BP_NO_URL))
   4143 	    copyParsedURL(&buf->currentURL, &Currentbuf->currentURL);
   4144 	if (linkid != LB_NOLINK) {
   4145 	    buf->linkBuffer[REV_LB[linkid]] = Currentbuf;
   4146 	    Currentbuf->linkBuffer[linkid] = buf;
   4147 	}
   4148 	pushBuffer(buf);
   4149     }
   4150     displayBuffer(Currentbuf, B_FORCE_REDRAW);
   4151 }
   4152 
   4153 /* load bookmark */
   4154 DEFUN(ldBmark, BOOKMARK VIEW_BOOKMARK, "Read bookmark")
   4155 {
   4156     cmd_loadURL(BookmarkFile, NULL, NO_REFERER, NULL);
   4157 }
   4158 
   4159 
   4160 /* Add current to bookmark */
   4161 DEFUN(adBmark, ADD_BOOKMARK, "Add current page to bookmark")
   4162 {
   4163     Str tmp;
   4164     FormList *request;
   4165 
   4166     tmp = Sprintf("mode=panel&cookie=%s&bmark=%s&url=%s&title=%s"
   4167 #ifdef USE_M17N
   4168 		    "&charset=%s"
   4169 #endif
   4170 		    ,
   4171 		  (Str_form_quote(localCookie()))->ptr,
   4172 		  (Str_form_quote(Strnew_charp(BookmarkFile)))->ptr,
   4173 		  (Str_form_quote(parsedURL2Str(&Currentbuf->currentURL)))->
   4174 		  ptr,
   4175 #ifdef USE_M17N
   4176 		  (Str_form_quote(wc_conv_strict(Currentbuf->buffername,
   4177 						 InnerCharset,
   4178 						 BookmarkCharset)))->ptr,
   4179 		  wc_ces_to_charset(BookmarkCharset));
   4180 #else
   4181 		  (Str_form_quote(Strnew_charp(Currentbuf->buffername)))->ptr);
   4182 #endif
   4183     request = newFormList(NULL, "post", NULL, NULL, NULL, NULL, NULL);
   4184     request->body = tmp->ptr;
   4185     request->length = tmp->length;
   4186     cmd_loadURL("file:///$LIB/" W3MBOOKMARK_CMDNAME, NULL, NO_REFERER,
   4187 		request);
   4188 }
   4189 
   4190 /* option setting */
   4191 DEFUN(ldOpt, OPTIONS, "Option setting panel")
   4192 {
   4193     cmd_loadBuffer(load_option_panel(), BP_NO_URL, LB_NOLINK);
   4194 }
   4195 
   4196 /* set an option */
   4197 DEFUN(setOpt, SET_OPTION, "Set option")
   4198 {
   4199     char *opt;
   4200 
   4201     CurrentKeyData = NULL;	/* not allowed in w3m-control: */
   4202     opt = searchKeyData();
   4203     if (opt == NULL || *opt == '\0' || strchr(opt, '=') == NULL) {
   4204 	if (opt != NULL && *opt != '\0') {
   4205 	    char *v = get_param_option(opt);
   4206 	    opt = Sprintf("%s=%s", opt, v ? v : "")->ptr;
   4207 	}
   4208 	opt = inputStrHist("Set option: ", opt, TextHist);
   4209 	if (opt == NULL || *opt == '\0') {
   4210 	    displayBuffer(Currentbuf, B_NORMAL);
   4211 	    return;
   4212 	}
   4213     }
   4214     if (set_param_option(opt))
   4215 	sync_with_option();
   4216     displayBuffer(Currentbuf, B_REDRAW_IMAGE);
   4217 }
   4218 
   4219 /* error message list */
   4220 DEFUN(msgs, MSGS, "Display error messages")
   4221 {
   4222     cmd_loadBuffer(message_list_panel(), BP_NO_URL, LB_NOLINK);
   4223 }
   4224 
   4225 /* page info */
   4226 DEFUN(pginfo, INFO, "View info of current document")
   4227 {
   4228     Buffer *buf;
   4229 
   4230     if ((buf = Currentbuf->linkBuffer[LB_N_INFO]) != NULL) {
   4231 	Currentbuf = buf;
   4232 	displayBuffer(Currentbuf, B_NORMAL);
   4233 	return;
   4234     }
   4235     if ((buf = Currentbuf->linkBuffer[LB_INFO]) != NULL)
   4236 	delBuffer(buf);
   4237     buf = page_info_panel(Currentbuf);
   4238     cmd_loadBuffer(buf, BP_NORMAL, LB_INFO);
   4239 }
   4240 
   4241 void
   4242 follow_map(struct parsed_tagarg *arg)
   4243 {
   4244     char *name = tag_get_value(arg, "link");
   4245 #if defined(MENU_MAP) || defined(USE_IMAGE)
   4246     Anchor *an;
   4247     MapArea *a;
   4248     int x, y;
   4249     ParsedURL p_url;
   4250 
   4251     an = retrieveCurrentImg(Currentbuf);
   4252     x = Currentbuf->cursorX + Currentbuf->rootX;
   4253     y = Currentbuf->cursorY + Currentbuf->rootY;
   4254     a = follow_map_menu(Currentbuf, name, an, x, y);
   4255     if (a == NULL || a->url == NULL || *(a->url) == '\0') {
   4256 #endif
   4257 #ifndef MENU_MAP
   4258 	Buffer *buf = follow_map_panel(Currentbuf, name);
   4259 
   4260 	if (buf != NULL)
   4261 	    cmd_loadBuffer(buf, BP_NORMAL, LB_NOLINK);
   4262 #endif
   4263 #if defined(MENU_MAP) || defined(USE_IMAGE)
   4264 	return;
   4265     }
   4266     if (*(a->url) == '#') {
   4267 	gotoLabel(a->url + 1);
   4268 	return;
   4269     }
   4270     parseURL2(a->url, &p_url, baseURL(Currentbuf));
   4271     pushHashHist(URLHist, parsedURL2Str(&p_url)->ptr);
   4272     if (check_target && open_tab_blank && a->target &&
   4273 	(!strcasecmp(a->target, "_new") || !strcasecmp(a->target, "_blank"))) {
   4274 	Buffer *buf;
   4275 
   4276 	_newT();
   4277 	buf = Currentbuf;
   4278 	cmd_loadURL(a->url, baseURL(Currentbuf),
   4279 		    parsedURL2Str(&Currentbuf->currentURL)->ptr, NULL);
   4280 	if (buf != Currentbuf)
   4281 	    delBuffer(buf);
   4282 	else
   4283 	    deleteTab(CurrentTab);
   4284 	displayBuffer(Currentbuf, B_FORCE_REDRAW);
   4285 	return;
   4286     }
   4287     cmd_loadURL(a->url, baseURL(Currentbuf),
   4288 		parsedURL2Str(&Currentbuf->currentURL)->ptr, NULL);
   4289 #endif
   4290 }
   4291 
   4292 #ifdef USE_MENU
   4293 /* link menu */
   4294 DEFUN(linkMn, LINK_MENU, "Popup link element menu")
   4295 {
   4296     LinkList *l = link_menu(Currentbuf);
   4297     ParsedURL p_url;
   4298 
   4299     if (!l || !l->url)
   4300 	return;
   4301     if (*(l->url) == '#') {
   4302 	gotoLabel(l->url + 1);
   4303 	return;
   4304     }
   4305     parseURL2(l->url, &p_url, baseURL(Currentbuf));
   4306     pushHashHist(URLHist, parsedURL2Str(&p_url)->ptr);
   4307     cmd_loadURL(l->url, baseURL(Currentbuf),
   4308 		parsedURL2Str(&Currentbuf->currentURL)->ptr, NULL);
   4309 }
   4310 
   4311 static void
   4312 anchorMn(Anchor *(*menu_func) (Buffer *), int go)
   4313 {
   4314     Anchor *a;
   4315     BufferPoint *po;
   4316 
   4317     if (!Currentbuf->href || !Currentbuf->hmarklist)
   4318 	return;
   4319     a = menu_func(Currentbuf);
   4320     if (!a || a->hseq < 0)
   4321 	return;
   4322     po = &Currentbuf->hmarklist->marks[a->hseq];
   4323     gotoLine(Currentbuf, po->line);
   4324     Currentbuf->pos = po->pos;
   4325     arrangeCursor(Currentbuf);
   4326     displayBuffer(Currentbuf, B_NORMAL);
   4327     if (go)
   4328 	followA();
   4329 }
   4330 
   4331 /* accesskey */
   4332 DEFUN(accessKey, ACCESSKEY, "Popup acceskey menu")
   4333 {
   4334     anchorMn(accesskey_menu, TRUE);
   4335 }
   4336 
   4337 /* list menu */
   4338 DEFUN(listMn, LIST_MENU, "Popup link list menu and go to selected link")
   4339 {
   4340     anchorMn(list_menu, TRUE);
   4341 }
   4342 
   4343 DEFUN(movlistMn, MOVE_LIST_MENU,
   4344       "Popup link list menu and move cursor to selected link")
   4345 {
   4346     anchorMn(list_menu, FALSE);
   4347 }
   4348 #endif
   4349 
   4350 /* link,anchor,image list */
   4351 DEFUN(linkLst, LIST, "Show all links and images")
   4352 {
   4353     Buffer *buf;
   4354 
   4355     buf = link_list_panel(Currentbuf);
   4356     if (buf != NULL) {
   4357 #ifdef USE_M17N
   4358 	buf->document_charset = Currentbuf->document_charset;
   4359 #endif
   4360 	cmd_loadBuffer(buf, BP_NORMAL, LB_NOLINK);
   4361     }
   4362 }
   4363 
   4364 #ifdef USE_COOKIE
   4365 /* cookie list */
   4366 DEFUN(cooLst, COOKIE, "View cookie list")
   4367 {
   4368     Buffer *buf;
   4369 
   4370     buf = cookie_list_panel();
   4371     if (buf != NULL)
   4372 	cmd_loadBuffer(buf, BP_NO_URL, LB_NOLINK);
   4373 }
   4374 #endif				/* USE_COOKIE */
   4375 
   4376 #ifdef USE_HISTORY
   4377 /* History page */
   4378 DEFUN(ldHist, HISTORY, "View history of URL")
   4379 {
   4380     cmd_loadBuffer(historyBuffer(URLHist), BP_NO_URL, LB_NOLINK);
   4381 }
   4382 #endif				/* USE_HISTORY */
   4383 
   4384 /* download HREF link */
   4385 DEFUN(svA, SAVE_LINK, "Save link to file")
   4386 {
   4387     CurrentKeyData = NULL;	/* not allowed in w3m-control: */
   4388     do_download = TRUE;
   4389     followA();
   4390     do_download = FALSE;
   4391 }
   4392 
   4393 /* download IMG link */
   4394 DEFUN(svI, SAVE_IMAGE, "Save image to file")
   4395 {
   4396     CurrentKeyData = NULL;	/* not allowed in w3m-control: */
   4397     do_download = TRUE;
   4398     followI();
   4399     do_download = FALSE;
   4400 }
   4401 
   4402 /* save buffer */
   4403 DEFUN(svBuf, PRINT SAVE_SCREEN, "Save rendered document to file")
   4404 {
   4405     char *qfile = NULL, *file;
   4406     FILE *f;
   4407     int is_pipe;
   4408 
   4409     CurrentKeyData = NULL;	/* not allowed in w3m-control: */
   4410     file = searchKeyData();
   4411     if (file == NULL || *file == '\0') {
   4412 	/* FIXME: gettextize? */
   4413 	qfile = inputLineHist("Save buffer to: ", NULL, IN_COMMAND, SaveHist);
   4414 	if (qfile == NULL || *qfile == '\0') {
   4415 	    displayBuffer(Currentbuf, B_NORMAL);
   4416 	    return;
   4417 	}
   4418     }
   4419     file = conv_to_system(qfile ? qfile : file);
   4420     if (*file == '|') {
   4421 	is_pipe = TRUE;
   4422 	f = popen(file + 1, "w");
   4423     }
   4424     else {
   4425 	if (qfile) {
   4426 	    file = unescape_spaces(Strnew_charp(qfile))->ptr;
   4427 	    file = conv_to_system(file);
   4428 	}
   4429 	file = expandPath(file);
   4430 	if (checkOverWrite(file) < 0) {
   4431 	    displayBuffer(Currentbuf, B_NORMAL);
   4432 	    return;
   4433 	}
   4434 	f = fopen(file, "w");
   4435 	is_pipe = FALSE;
   4436     }
   4437     if (f == NULL) {
   4438 	/* FIXME: gettextize? */
   4439 	char *emsg = Sprintf("Can't open %s", conv_from_system(file))->ptr;
   4440 	disp_err_message(emsg, TRUE);
   4441 	return;
   4442     }
   4443     saveBuffer(Currentbuf, f, TRUE);
   4444     if (is_pipe)
   4445 	pclose(f);
   4446     else
   4447 	fclose(f);
   4448     displayBuffer(Currentbuf, B_NORMAL);
   4449 }
   4450 
   4451 /* save source */
   4452 DEFUN(svSrc, DOWNLOAD SAVE, "Save document source to file")
   4453 {
   4454     char *file;
   4455 
   4456     if (Currentbuf->sourcefile == NULL)
   4457 	return;
   4458     CurrentKeyData = NULL;	/* not allowed in w3m-control: */
   4459     PermitSaveToPipe = TRUE;
   4460     if (Currentbuf->real_scheme == SCM_LOCAL)
   4461 	file = conv_from_system(guess_save_name(NULL,
   4462 						Currentbuf->currentURL.
   4463 						real_file));
   4464     else
   4465 	file = guess_save_name(Currentbuf, Currentbuf->currentURL.file);
   4466     doFileCopy(Currentbuf->sourcefile, file);
   4467     PermitSaveToPipe = FALSE;
   4468     displayBuffer(Currentbuf, B_NORMAL);
   4469 }
   4470 
   4471 static void
   4472 _peekURL(int only_img)
   4473 {
   4474 
   4475     Anchor *a;
   4476     ParsedURL pu;
   4477     static Str s = NULL;
   4478 #ifdef USE_M17N
   4479     static Lineprop *p = NULL;
   4480     Lineprop *pp;
   4481 #endif
   4482     static int offset = 0, n;
   4483 
   4484     if (Currentbuf->firstLine == NULL)
   4485 	return;
   4486     if (CurrentKey == prev_key && s != NULL) {
   4487 	if (s->length - offset >= COLS)
   4488 	    offset++;
   4489 	else if (s->length <= offset)	/* bug ? */
   4490 	    offset = 0;
   4491 	goto disp;
   4492     }
   4493     else {
   4494 	offset = 0;
   4495     }
   4496     s = NULL;
   4497     a = (only_img ? NULL : retrieveCurrentAnchor(Currentbuf));
   4498     if (a == NULL) {
   4499 	a = (only_img ? NULL : retrieveCurrentForm(Currentbuf));
   4500 	if (a == NULL) {
   4501 	    a = retrieveCurrentImg(Currentbuf);
   4502 	    if (a == NULL)
   4503 		return;
   4504 	}
   4505 	else
   4506 	    s = Strnew_charp(form2str((FormItemList *)a->url));
   4507     }
   4508     if (s == NULL) {
   4509 	parseURL2(a->url, &pu, baseURL(Currentbuf));
   4510 	s = parsedURL2Str(&pu);
   4511     }
   4512     if (DecodeURL)
   4513 	s = Strnew_charp(url_unquote_conv
   4514 			 (s->ptr, Currentbuf->document_charset));
   4515 #ifdef USE_M17N
   4516     s = checkType(s, &pp, NULL);
   4517     p = NewAtom_N(Lineprop, s->length);
   4518     bcopy((void *)pp, (void *)p, s->length * sizeof(Lineprop));
   4519 #endif
   4520   disp:
   4521     n = searchKeyNum();
   4522     if (n > 1 && s->length > (n - 1) * (COLS - 1))
   4523 	offset = (n - 1) * (COLS - 1);
   4524 #ifdef USE_M17N
   4525     while (offset < s->length && p[offset] & PC_WCHAR2)
   4526 	offset++;
   4527 #endif
   4528     disp_message_nomouse(&s->ptr[offset], TRUE);
   4529 }
   4530 
   4531 /* peek URL */
   4532 DEFUN(peekURL, PEEK_LINK, "Peek link URL")
   4533 {
   4534     _peekURL(0);
   4535 }
   4536 
   4537 /* peek URL of image */
   4538 DEFUN(peekIMG, PEEK_IMG, "Peek image URL")
   4539 {
   4540     _peekURL(1);
   4541 }
   4542 
   4543 /* show current URL */
   4544 static Str
   4545 currentURL(void)
   4546 {
   4547     if (Currentbuf->bufferprop & BP_INTERNAL)
   4548 	return Strnew_size(0);
   4549     return parsedURL2Str(&Currentbuf->currentURL);
   4550 }
   4551 
   4552 DEFUN(curURL, PEEK, "Peek current URL")
   4553 {
   4554     static Str s = NULL;
   4555 #ifdef USE_M17N
   4556     static Lineprop *p = NULL;
   4557     Lineprop *pp;
   4558 #endif
   4559     static int offset = 0, n;
   4560 
   4561     if (Currentbuf->bufferprop & BP_INTERNAL)
   4562 	return;
   4563     if (CurrentKey == prev_key && s != NULL) {
   4564 	if (s->length - offset >= COLS)
   4565 	    offset++;
   4566 	else if (s->length <= offset)	/* bug ? */
   4567 	    offset = 0;
   4568     }
   4569     else {
   4570 	offset = 0;
   4571 	s = currentURL();
   4572 	if (DecodeURL)
   4573 	    s = Strnew_charp(url_unquote_conv(s->ptr, 0));
   4574 #ifdef USE_M17N
   4575 	s = checkType(s, &pp, NULL);
   4576 	p = NewAtom_N(Lineprop, s->length);
   4577 	bcopy((void *)pp, (void *)p, s->length * sizeof(Lineprop));
   4578 #endif
   4579     }
   4580     n = searchKeyNum();
   4581     if (n > 1 && s->length > (n - 1) * (COLS - 1))
   4582 	offset = (n - 1) * (COLS - 1);
   4583 #ifdef USE_M17N
   4584     while (offset < s->length && p[offset] & PC_WCHAR2)
   4585 	offset++;
   4586 #endif
   4587     disp_message_nomouse(&s->ptr[offset], TRUE);
   4588 }
   4589 /* view HTML source */
   4590 
   4591 DEFUN(vwSrc, SOURCE VIEW, "View HTML source")
   4592 {
   4593     Buffer *buf;
   4594 
   4595     if (Currentbuf->type == NULL || Currentbuf->bufferprop & BP_FRAME)
   4596 	return;
   4597     if ((buf = Currentbuf->linkBuffer[LB_SOURCE]) != NULL ||
   4598 	(buf = Currentbuf->linkBuffer[LB_N_SOURCE]) != NULL) {
   4599 	Currentbuf = buf;
   4600 	displayBuffer(Currentbuf, B_NORMAL);
   4601 	return;
   4602     }
   4603     if (Currentbuf->sourcefile == NULL) {
   4604 	if (Currentbuf->pagerSource &&
   4605 	    !strcasecmp(Currentbuf->type, "text/plain")) {
   4606 #ifdef USE_M17N
   4607 	    wc_ces old_charset;
   4608 	    wc_bool old_fix_width_conv;
   4609 #endif
   4610 	    FILE *f;
   4611 	    Str tmpf = tmpfname(TMPF_SRC, NULL);
   4612 	    f = fopen(tmpf->ptr, "w");
   4613 	    if (f == NULL)
   4614 		return;
   4615 #ifdef USE_M17N
   4616 	    old_charset = DisplayCharset;
   4617 	    old_fix_width_conv = WcOption.fix_width_conv;
   4618 	    DisplayCharset = (Currentbuf->document_charset != WC_CES_US_ASCII)
   4619 		? Currentbuf->document_charset : 0;
   4620 	    WcOption.fix_width_conv = WC_FALSE;
   4621 #endif
   4622 	    saveBufferBody(Currentbuf, f, TRUE);
   4623 #ifdef USE_M17N
   4624 	    DisplayCharset = old_charset;
   4625 	    WcOption.fix_width_conv = old_fix_width_conv;
   4626 #endif
   4627 	    fclose(f);
   4628 	    Currentbuf->sourcefile = tmpf->ptr;
   4629 	}
   4630 	else {
   4631 	    return;
   4632 	}
   4633     }
   4634 
   4635     buf = newBuffer(INIT_BUFFER_WIDTH);
   4636 
   4637     if (is_html_type(Currentbuf->type)) {
   4638 	buf->type = "text/plain";
   4639 	if (Currentbuf->real_type &&
   4640 	    is_html_type(Currentbuf->real_type))
   4641 	    buf->real_type = "text/plain";
   4642 	else
   4643 	    buf->real_type = Currentbuf->real_type;
   4644 	buf->buffername = Sprintf("source of %s", Currentbuf->buffername)->ptr;
   4645 	buf->linkBuffer[LB_N_SOURCE] = Currentbuf;
   4646 	Currentbuf->linkBuffer[LB_SOURCE] = buf;
   4647     }
   4648     else if (!strcasecmp(Currentbuf->type, "text/plain")) {
   4649 	buf->type = "text/html";
   4650 	if (Currentbuf->real_type &&
   4651 	    !strcasecmp(Currentbuf->real_type, "text/plain"))
   4652 	    buf->real_type = "text/html";
   4653 	else
   4654 	    buf->real_type = Currentbuf->real_type;
   4655 	buf->buffername = Sprintf("HTML view of %s",
   4656 				  Currentbuf->buffername)->ptr;
   4657 	buf->linkBuffer[LB_SOURCE] = Currentbuf;
   4658 	Currentbuf->linkBuffer[LB_N_SOURCE] = buf;
   4659     }
   4660     else {
   4661 	return;
   4662     }
   4663     buf->currentURL = Currentbuf->currentURL;
   4664     buf->real_scheme = Currentbuf->real_scheme;
   4665     buf->filename = Currentbuf->filename;
   4666     buf->sourcefile = Currentbuf->sourcefile;
   4667     buf->header_source = Currentbuf->header_source;
   4668     buf->search_header = Currentbuf->search_header;
   4669 #ifdef USE_M17N
   4670     buf->document_charset = Currentbuf->document_charset;
   4671 #endif
   4672     buf->clone = Currentbuf->clone;
   4673     (*buf->clone)++;
   4674 
   4675     buf->need_reshape = TRUE;
   4676     reshapeBuffer(buf);
   4677     pushBuffer(buf);
   4678     displayBuffer(Currentbuf, B_NORMAL);
   4679 }
   4680 
   4681 /* reload */
   4682 DEFUN(reload, RELOAD, "Reload buffer")
   4683 {
   4684     Buffer *buf, *fbuf = NULL, sbuf;
   4685 #ifdef USE_M17N
   4686     wc_ces old_charset;
   4687 #endif
   4688     Str url;
   4689     FormList *request;
   4690     int multipart;
   4691 
   4692     if (Currentbuf->bufferprop & BP_INTERNAL) {
   4693 	if (!strcmp(Currentbuf->buffername, DOWNLOAD_LIST_TITLE)) {
   4694 	    ldDL();
   4695 	    return;
   4696 	}
   4697 	/* FIXME: gettextize? */
   4698 	disp_err_message("Can't reload...", TRUE);
   4699 	return;
   4700     }
   4701     if (Currentbuf->currentURL.scheme == SCM_LOCAL &&
   4702 	!strcmp(Currentbuf->currentURL.file, "-")) {
   4703 	/* file is std input */
   4704 	/* FIXME: gettextize? */
   4705 	disp_err_message("Can't reload stdin", TRUE);
   4706 	return;
   4707     }
   4708     copyBuffer(&sbuf, Currentbuf);
   4709     if (Currentbuf->bufferprop & BP_FRAME &&
   4710 	(fbuf = Currentbuf->linkBuffer[LB_N_FRAME])) {
   4711 	if (fmInitialized) {
   4712 	    message("Rendering frame", 0, 0);
   4713 	    refresh();
   4714 	}
   4715 	if (!(buf = renderFrame(fbuf, 1))) {
   4716 	    displayBuffer(Currentbuf, B_NORMAL);
   4717 	    return;
   4718 	}
   4719 	if (fbuf->linkBuffer[LB_FRAME]) {
   4720 	    if (buf->sourcefile &&
   4721 		fbuf->linkBuffer[LB_FRAME]->sourcefile &&
   4722 		!strcmp(buf->sourcefile,
   4723 			fbuf->linkBuffer[LB_FRAME]->sourcefile))
   4724 		fbuf->linkBuffer[LB_FRAME]->sourcefile = NULL;
   4725 	    delBuffer(fbuf->linkBuffer[LB_FRAME]);
   4726 	}
   4727 	fbuf->linkBuffer[LB_FRAME] = buf;
   4728 	buf->linkBuffer[LB_N_FRAME] = fbuf;
   4729 	pushBuffer(buf);
   4730 	Currentbuf = buf;
   4731 	if (Currentbuf->firstLine) {
   4732 	    COPY_BUFROOT(Currentbuf, &sbuf);
   4733 	    restorePosition(Currentbuf, &sbuf);
   4734 	}
   4735 	displayBuffer(Currentbuf, B_FORCE_REDRAW);
   4736 	return;
   4737     }
   4738     else if (Currentbuf->frameset != NULL)
   4739 	fbuf = Currentbuf->linkBuffer[LB_FRAME];
   4740     multipart = 0;
   4741     if (Currentbuf->form_submit) {
   4742 	request = Currentbuf->form_submit->parent;
   4743 	if (request->method == FORM_METHOD_POST
   4744 	    && request->enctype == FORM_ENCTYPE_MULTIPART) {
   4745 	    Str query;
   4746 	    struct stat st;
   4747 	    multipart = 1;
   4748 	    query_from_followform(&query, Currentbuf->form_submit, multipart);
   4749 	    stat(request->body, &st);
   4750 	    request->length = st.st_size;
   4751 	}
   4752     }
   4753     else {
   4754 	request = NULL;
   4755     }
   4756     url = parsedURL2Str(&Currentbuf->currentURL);
   4757     /* FIXME: gettextize? */
   4758     message("Reloading...", 0, 0);
   4759     refresh();
   4760 #ifdef USE_M17N
   4761     old_charset = DocumentCharset;
   4762     if (Currentbuf->document_charset != WC_CES_US_ASCII)
   4763 	DocumentCharset = Currentbuf->document_charset;
   4764 #endif
   4765     SearchHeader = Currentbuf->search_header;
   4766     DefaultType = Currentbuf->real_type;
   4767     buf = loadGeneralFile(url->ptr, NULL, NO_REFERER, RG_NOCACHE, request);
   4768 #ifdef USE_M17N
   4769     DocumentCharset = old_charset;
   4770 #endif
   4771     SearchHeader = FALSE;
   4772     DefaultType = NULL;
   4773 
   4774     if (multipart)
   4775 	unlink(request->body);
   4776     if (buf == NULL) {
   4777 	/* FIXME: gettextize? */
   4778 	disp_err_message("Can't reload...", TRUE);
   4779 	return;
   4780     }
   4781     else if (buf == NO_BUFFER) {
   4782 	displayBuffer(Currentbuf, B_NORMAL);
   4783 	return;
   4784     }
   4785     if (fbuf != NULL)
   4786 	Firstbuf = deleteBuffer(Firstbuf, fbuf);
   4787     repBuffer(Currentbuf, buf);
   4788     if ((buf->type != NULL) && (sbuf.type != NULL) &&
   4789 	((!strcasecmp(buf->type, "text/plain") &&
   4790 	  is_html_type(sbuf.type)) ||
   4791 	 (is_html_type(buf->type) &&
   4792 	  !strcasecmp(sbuf.type, "text/plain")))) {
   4793 	vwSrc();
   4794 	if (Currentbuf != buf)
   4795 	    Firstbuf = deleteBuffer(Firstbuf, buf);
   4796     }
   4797     Currentbuf->search_header = sbuf.search_header;
   4798     Currentbuf->form_submit = sbuf.form_submit;
   4799     if (Currentbuf->firstLine) {
   4800 	COPY_BUFROOT(Currentbuf, &sbuf);
   4801 	restorePosition(Currentbuf, &sbuf);
   4802     }
   4803     displayBuffer(Currentbuf, B_FORCE_REDRAW);
   4804 }
   4805 
   4806 /* reshape */
   4807 DEFUN(reshape, RESHAPE, "Re-render buffer")
   4808 {
   4809     Currentbuf->need_reshape = TRUE;
   4810     reshapeBuffer(Currentbuf);
   4811     displayBuffer(Currentbuf, B_FORCE_REDRAW);
   4812 }
   4813 
   4814 #ifdef USE_M17N
   4815 static void
   4816 _docCSet(wc_ces charset)
   4817 {
   4818     if (Currentbuf->bufferprop & BP_INTERNAL)
   4819 	return;
   4820     if (Currentbuf->sourcefile == NULL) {
   4821 	disp_message("Can't reload...", FALSE);
   4822 	return;
   4823     }
   4824     Currentbuf->document_charset = charset;
   4825     Currentbuf->need_reshape = TRUE;
   4826     displayBuffer(Currentbuf, B_FORCE_REDRAW);
   4827 }
   4828 
   4829 void
   4830 change_charset(struct parsed_tagarg *arg)
   4831 {
   4832     Buffer *buf = Currentbuf->linkBuffer[LB_N_INFO];
   4833     wc_ces charset;
   4834 
   4835     if (buf == NULL)
   4836 	return;
   4837     delBuffer(Currentbuf);
   4838     Currentbuf = buf;
   4839     if (Currentbuf->bufferprop & BP_INTERNAL)
   4840 	return;
   4841     charset = Currentbuf->document_charset;
   4842     for (; arg; arg = arg->next) {
   4843 	if (!strcmp(arg->arg, "charset"))
   4844 	    charset = atoi(arg->value);
   4845     }
   4846     _docCSet(charset);
   4847 }
   4848 
   4849 DEFUN(docCSet, CHARSET, "Change the current document charset")
   4850 {
   4851     char *cs;
   4852     wc_ces charset;
   4853 
   4854     cs = searchKeyData();
   4855     if (cs == NULL || *cs == '\0')
   4856 	/* FIXME: gettextize? */
   4857 	cs = inputStr("Document charset: ",
   4858 		      wc_ces_to_charset(Currentbuf->document_charset));
   4859     charset = wc_guess_charset_short(cs, 0);
   4860     if (charset == 0) {
   4861 	displayBuffer(Currentbuf, B_NORMAL);
   4862 	return;
   4863     }
   4864     _docCSet(charset);
   4865 }
   4866 
   4867 DEFUN(defCSet, DEFAULT_CHARSET, "Change the default document charset")
   4868 {
   4869     char *cs;
   4870     wc_ces charset;
   4871 
   4872     cs = searchKeyData();
   4873     if (cs == NULL || *cs == '\0')
   4874 	/* FIXME: gettextize? */
   4875 	cs = inputStr("Default document charset: ",
   4876 		      wc_ces_to_charset(DocumentCharset));
   4877     charset = wc_guess_charset_short(cs, 0);
   4878     if (charset != 0)
   4879 	DocumentCharset = charset;
   4880     displayBuffer(Currentbuf, B_NORMAL);
   4881 }
   4882 #endif
   4883 
   4884 /* mark URL-like patterns as anchors */
   4885 void
   4886 chkURLBuffer(Buffer *buf)
   4887 {
   4888     static char *url_like_pat[] = {
   4889 	"https?://[a-zA-Z0-9][a-zA-Z0-9:%\\-\\./?=~_\\&+@#,\\$;]*[a-zA-Z0-9_/=\\-]",
   4890 	"file:/[a-zA-Z0-9:%\\-\\./=_\\+@#,\\$;]*",
   4891 #ifdef USE_GOPHER
   4892 	"gopher://[a-zA-Z0-9][a-zA-Z0-9:%\\-\\./_]*",
   4893 #endif				/* USE_GOPHER */
   4894 	"ftp://[a-zA-Z0-9][a-zA-Z0-9:%\\-\\./=_+@#,\\$]*[a-zA-Z0-9_/]",
   4895 #ifdef USE_NNTP
   4896 	"news:[^<> 	][^<> 	]*",
   4897 	"nntp://[a-zA-Z0-9][a-zA-Z0-9:%\\-\\./_]*",
   4898 #endif				/* USE_NNTP */
   4899 #ifndef USE_W3MMAILER		/* see also chkExternalURIBuffer() */
   4900 	"mailto:[^<> 	][^<> 	]*@[a-zA-Z0-9][a-zA-Z0-9\\-\\._]*[a-zA-Z0-9]",
   4901 #endif
   4902 #ifdef INET6
   4903 	"https?://[a-zA-Z0-9:%\\-\\./_@]*\\[[a-fA-F0-9:][a-fA-F0-9:\\.]*\\][a-zA-Z0-9:%\\-\\./?=~_\\&+@#,\\$;]*",
   4904 	"ftp://[a-zA-Z0-9:%\\-\\./_@]*\\[[a-fA-F0-9:][a-fA-F0-9:\\.]*\\][a-zA-Z0-9:%\\-\\./=_+@#,\\$]*",
   4905 #endif				/* INET6 */
   4906 	NULL
   4907     };
   4908     int i;
   4909     for (i = 0; url_like_pat[i]; i++) {
   4910 	reAnchor(buf, url_like_pat[i]);
   4911     }
   4912 #ifdef USE_EXTERNAL_URI_LOADER
   4913     chkExternalURIBuffer(buf);
   4914 #endif
   4915     buf->check_url |= CHK_URL;
   4916 }
   4917 
   4918 DEFUN(chkURL, MARK_URL, "Mark URL-like strings as anchors")
   4919 {
   4920     chkURLBuffer(Currentbuf);
   4921     displayBuffer(Currentbuf, B_FORCE_REDRAW);
   4922 }
   4923 
   4924 DEFUN(chkWORD, MARK_WORD, "Mark current word as anchor")
   4925 {
   4926     char *p;
   4927     int spos, epos;
   4928     p = getCurWord(Currentbuf, &spos, &epos);
   4929     if (p == NULL)
   4930 	return;
   4931     reAnchorWord(Currentbuf, Currentbuf->currentLine, spos, epos);
   4932     displayBuffer(Currentbuf, B_FORCE_REDRAW);
   4933 }
   4934 
   4935 #ifdef USE_NNTP
   4936 /* mark Message-ID-like patterns as NEWS anchors */
   4937 void
   4938 chkNMIDBuffer(Buffer *buf)
   4939 {
   4940     static char *url_like_pat[] = {
   4941 	"<[!-;=?-~]+@[a-zA-Z0-9\\.\\-_]+>",
   4942 	NULL,
   4943     };
   4944     int i;
   4945     for (i = 0; url_like_pat[i]; i++) {
   4946 	reAnchorNews(buf, url_like_pat[i]);
   4947     }
   4948     buf->check_url |= CHK_NMID;
   4949 }
   4950 
   4951 DEFUN(chkNMID, MARK_MID, "Mark Message-ID-like strings as anchors")
   4952 {
   4953     chkNMIDBuffer(Currentbuf);
   4954     displayBuffer(Currentbuf, B_FORCE_REDRAW);
   4955 }
   4956 #endif				/* USE_NNTP */
   4957 
   4958 /* render frame */
   4959 DEFUN(rFrame, FRAME, "Render frame")
   4960 {
   4961     Buffer *buf;
   4962 
   4963     if ((buf = Currentbuf->linkBuffer[LB_FRAME]) != NULL) {
   4964 	Currentbuf = buf;
   4965 	displayBuffer(Currentbuf, B_NORMAL);
   4966 	return;
   4967     }
   4968     if (Currentbuf->frameset == NULL) {
   4969 	if ((buf = Currentbuf->linkBuffer[LB_N_FRAME]) != NULL) {
   4970 	    Currentbuf = buf;
   4971 	    displayBuffer(Currentbuf, B_NORMAL);
   4972 	}
   4973 	return;
   4974     }
   4975     if (fmInitialized) {
   4976 	message("Rendering frame", 0, 0);
   4977 	refresh();
   4978     }
   4979     buf = renderFrame(Currentbuf, 0);
   4980     if (buf == NULL) {
   4981 	displayBuffer(Currentbuf, B_NORMAL);
   4982 	return;
   4983     }
   4984     buf->linkBuffer[LB_N_FRAME] = Currentbuf;
   4985     Currentbuf->linkBuffer[LB_FRAME] = buf;
   4986     pushBuffer(buf);
   4987     if (fmInitialized && display_ok)
   4988 	displayBuffer(Currentbuf, B_FORCE_REDRAW);
   4989 }
   4990 
   4991 /* spawn external browser */
   4992 static void
   4993 invoke_browser(char *url)
   4994 {
   4995     Str cmd;
   4996     char *browser = NULL;
   4997     int bg = 0, len;
   4998 
   4999     CurrentKeyData = NULL;	/* not allowed in w3m-control: */
   5000     browser = searchKeyData();
   5001     if (browser == NULL || *browser == '\0') {
   5002 	switch (prec_num) {
   5003 	case 0:
   5004 	case 1:
   5005 	    browser = ExtBrowser;
   5006 	    break;
   5007 	case 2:
   5008 	    browser = ExtBrowser2;
   5009 	    break;
   5010 	case 3:
   5011 	    browser = ExtBrowser3;
   5012 	    break;
   5013 	}
   5014 	if (browser == NULL || *browser == '\0') {
   5015 	    browser = inputStr("Browse command: ", NULL);
   5016 	    if (browser != NULL)
   5017 		browser = conv_to_system(browser);
   5018 	}
   5019     }
   5020     else {
   5021 	browser = conv_to_system(browser);
   5022     }
   5023     if (browser == NULL || *browser == '\0') {
   5024 	displayBuffer(Currentbuf, B_NORMAL);
   5025 	return;
   5026     }
   5027 
   5028     if ((len = strlen(browser)) >= 2 && browser[len - 1] == '&' &&
   5029 	browser[len - 2] != '\\') {
   5030 	browser = allocStr(browser, len - 2);
   5031 	bg = 1;
   5032     }
   5033     cmd = myExtCommand(browser, shell_quote(url), FALSE);
   5034     Strremovetrailingspaces(cmd);
   5035     fmTerm();
   5036     mySystem(cmd->ptr, bg);
   5037     fmInit();
   5038     displayBuffer(Currentbuf, B_FORCE_REDRAW);
   5039 }
   5040 
   5041 DEFUN(extbrz, EXTERN, "Execute external browser")
   5042 {
   5043     if (Currentbuf->bufferprop & BP_INTERNAL) {
   5044 	/* FIXME: gettextize? */
   5045 	disp_err_message("Can't browse...", TRUE);
   5046 	return;
   5047     }
   5048     if (Currentbuf->currentURL.scheme == SCM_LOCAL &&
   5049 	!strcmp(Currentbuf->currentURL.file, "-")) {
   5050 	/* file is std input */
   5051 	/* FIXME: gettextize? */
   5052 	disp_err_message("Can't browse stdin", TRUE);
   5053 	return;
   5054     }
   5055     invoke_browser(parsedURL2Str(&Currentbuf->currentURL)->ptr);
   5056 }
   5057 
   5058 DEFUN(linkbrz, EXTERN_LINK, "View current link using external browser")
   5059 {
   5060     Anchor *a;
   5061     ParsedURL pu;
   5062 
   5063     if (Currentbuf->firstLine == NULL)
   5064 	return;
   5065     a = retrieveCurrentAnchor(Currentbuf);
   5066     if (a == NULL)
   5067 	return;
   5068     parseURL2(a->url, &pu, baseURL(Currentbuf));
   5069     invoke_browser(parsedURL2Str(&pu)->ptr);
   5070 }
   5071 
   5072 /* show current line number and number of lines in the entire document */
   5073 DEFUN(curlno, LINE_INFO, "Show current line number")
   5074 {
   5075     Line *l = Currentbuf->currentLine;
   5076     Str tmp;
   5077     int cur = 0, all = 0, col = 0, len = 0;
   5078 
   5079     if (l != NULL) {
   5080 	cur = l->real_linenumber;
   5081 	col = l->bwidth + Currentbuf->currentColumn + Currentbuf->cursorX + 1;
   5082 	while (l->next && l->next->bpos)
   5083 	    l = l->next;
   5084 	if (l->width < 0)
   5085 	    l->width = COLPOS(l, l->len);
   5086 	len = l->bwidth + l->width;
   5087     }
   5088     if (Currentbuf->lastLine)
   5089 	all = Currentbuf->lastLine->real_linenumber;
   5090     if (Currentbuf->pagerSource && !(Currentbuf->bufferprop & BP_CLOSE))
   5091 	tmp = Sprintf("line %d col %d/%d", cur, col, len);
   5092     else
   5093 	tmp = Sprintf("line %d/%d (%d%%) col %d/%d", cur, all,
   5094 		      (int)((double)cur * 100.0 / (double)(all ? all : 1)
   5095 			    + 0.5), col, len);
   5096 #ifdef USE_M17N
   5097     Strcat_charp(tmp, "  ");
   5098     Strcat_charp(tmp, wc_ces_to_charset_desc(Currentbuf->document_charset));
   5099 #endif
   5100 
   5101     disp_message(tmp->ptr, FALSE);
   5102 }
   5103 
   5104 #ifdef USE_IMAGE
   5105 DEFUN(dispI, DISPLAY_IMAGE, "Restart loading and drawing of images")
   5106 {
   5107     if (!displayImage)
   5108 	initImage();
   5109     if (!activeImage)
   5110 	return;
   5111     displayImage = TRUE;
   5112     /*
   5113      * if (!(Currentbuf->type && is_html_type(Currentbuf->type)))
   5114      * return;
   5115      */
   5116     Currentbuf->image_flag = IMG_FLAG_AUTO;
   5117     Currentbuf->need_reshape = TRUE;
   5118     displayBuffer(Currentbuf, B_REDRAW_IMAGE);
   5119 }
   5120 
   5121 DEFUN(stopI, STOP_IMAGE, "Stop loading and drawing of images")
   5122 {
   5123     if (!activeImage)
   5124 	return;
   5125     /*
   5126      * if (!(Currentbuf->type && is_html_type(Currentbuf->type)))
   5127      * return;
   5128      */
   5129     Currentbuf->image_flag = IMG_FLAG_SKIP;
   5130     displayBuffer(Currentbuf, B_REDRAW_IMAGE);
   5131 }
   5132 #endif
   5133 
   5134 #ifdef USE_MOUSE
   5135 
   5136 static int
   5137 mouse_scroll_line(void)
   5138 {
   5139     if (relative_wheel_scroll)
   5140 	return (relative_wheel_scroll_ratio * LASTLINE + 99) / 100;
   5141     else
   5142 	return fixed_wheel_scroll_count;
   5143 }
   5144 
   5145 static TabBuffer *
   5146 posTab(int x, int y)
   5147 {
   5148     TabBuffer *tab;
   5149 
   5150     if (mouse_action.menu_str && x < mouse_action.menu_width && y == 0)
   5151 	return NO_TABBUFFER;
   5152     if (y > LastTab->y)
   5153 	return NULL;
   5154     for (tab = FirstTab; tab; tab = tab->nextTab) {
   5155 	if (tab->x1 <= x && x <= tab->x2 && tab->y == y)
   5156 	    return tab;
   5157     }
   5158     return NULL;
   5159 }
   5160 
   5161 static void
   5162 do_mouse_action(int btn, int x, int y)
   5163 {
   5164     MouseActionMap *map = NULL;
   5165     int ny = -1;
   5166 
   5167     if (nTab > 1 || mouse_action.menu_str)
   5168 	ny = LastTab->y + 1;
   5169 
   5170     switch (btn) {
   5171     case MOUSE_BTN1_DOWN:
   5172 	btn = 0;
   5173 	break;
   5174     case MOUSE_BTN2_DOWN:
   5175 	btn = 1;
   5176 	break;
   5177     case MOUSE_BTN3_DOWN:
   5178 	btn = 2;
   5179 	break;
   5180     default:
   5181 	return;
   5182     }
   5183     if (y < ny) {
   5184 	if (mouse_action.menu_str && x >= 0 && x < mouse_action.menu_width) {
   5185 	    if (mouse_action.menu_map[btn])
   5186 		map = &mouse_action.menu_map[btn][x];
   5187 	}
   5188 	else
   5189 	    map = &mouse_action.tab_map[btn];
   5190     }
   5191     else if (y == LASTLINE) {
   5192 	if (mouse_action.lastline_str && x >= 0 &&
   5193 	    x < mouse_action.lastline_width) {
   5194 	    if (mouse_action.lastline_map[btn])
   5195 		map = &mouse_action.lastline_map[btn][x];
   5196 	}
   5197     }
   5198     else if (y > ny) {
   5199 	if (y == Currentbuf->cursorY + Currentbuf->rootY &&
   5200 	    (x == Currentbuf->cursorX + Currentbuf->rootX
   5201 #ifdef USE_M17N
   5202 	     || (WcOption.use_wide && Currentbuf->currentLine != NULL &&
   5203 		 (CharType(Currentbuf->currentLine->propBuf[Currentbuf->pos])
   5204 		  == PC_KANJI1)
   5205 		 && x == Currentbuf->cursorX + Currentbuf->rootX + 1)
   5206 #endif
   5207 	    )) {
   5208 	    if (retrieveCurrentAnchor(Currentbuf) ||
   5209 		retrieveCurrentForm(Currentbuf)) {
   5210 		map = &mouse_action.active_map[btn];
   5211 		if (!(map && map->func))
   5212 		    map = &mouse_action.anchor_map[btn];
   5213 	    }
   5214 	}
   5215 	else {
   5216 	    int cx = Currentbuf->cursorX, cy = Currentbuf->cursorY;
   5217 	    cursorXY(Currentbuf, x - Currentbuf->rootX, y - Currentbuf->rootY);
   5218 	    if (y == Currentbuf->cursorY + Currentbuf->rootY &&
   5219 		(x == Currentbuf->cursorX + Currentbuf->rootX
   5220 #ifdef USE_M17N
   5221 		 || (WcOption.use_wide && Currentbuf->currentLine != NULL &&
   5222 		     (CharType(Currentbuf->currentLine->
   5223 			       propBuf[Currentbuf->pos]) == PC_KANJI1)
   5224 		     && x == Currentbuf->cursorX + Currentbuf->rootX + 1)
   5225 #endif
   5226 		) &&
   5227 		(retrieveCurrentAnchor(Currentbuf) ||
   5228 		 retrieveCurrentForm(Currentbuf)))
   5229 		map = &mouse_action.anchor_map[btn];
   5230 	    cursorXY(Currentbuf, cx, cy);
   5231 	}
   5232     }
   5233     else {
   5234 	return;
   5235     }
   5236     if (!(map && map->func))
   5237 	map = &mouse_action.default_map[btn];
   5238     if (map && map->func) {
   5239 	mouse_action.in_action = TRUE;
   5240 	mouse_action.cursorX = x;
   5241 	mouse_action.cursorY = y;
   5242 	CurrentKey = -1;
   5243 	CurrentKeyData = NULL;
   5244 	CurrentCmdData = map->data;
   5245 	(*map->func) ();
   5246 	CurrentCmdData = NULL;
   5247     }
   5248 }
   5249 
   5250 static void
   5251 process_mouse(int btn, int x, int y)
   5252 {
   5253     int delta_x, delta_y, i;
   5254     static int press_btn = MOUSE_BTN_RESET, press_x, press_y;
   5255     TabBuffer *t;
   5256     int ny = -1;
   5257 
   5258     if (nTab > 1 || mouse_action.menu_str)
   5259 	ny = LastTab->y + 1;
   5260     if (btn == MOUSE_BTN_UP) {
   5261 	switch (press_btn) {
   5262 	case MOUSE_BTN1_DOWN:
   5263 	    if (press_y == y && press_x == x)
   5264 		do_mouse_action(press_btn, x, y);
   5265 	    else if (ny > 0 && y < ny) {
   5266 		if (press_y < ny) {
   5267 		    moveTab(posTab(press_x, press_y), posTab(x, y),
   5268 			    (press_y == y) ? (press_x < x) : (press_y < y));
   5269 		    return;
   5270 		}
   5271 		else if (press_x >= Currentbuf->rootX) {
   5272 		    Buffer *buf = Currentbuf;
   5273 		    int cx = Currentbuf->cursorX, cy = Currentbuf->cursorY;
   5274 
   5275 		    t = posTab(x, y);
   5276 		    if (t == NULL)
   5277 			return;
   5278 		    if (t == NO_TABBUFFER)
   5279 			t = NULL;	/* open new tab */
   5280 		    cursorXY(Currentbuf, press_x - Currentbuf->rootX,
   5281 			     press_y - Currentbuf->rootY);
   5282 		    if (Currentbuf->cursorY == press_y - Currentbuf->rootY &&
   5283 			(Currentbuf->cursorX == press_x - Currentbuf->rootX
   5284 #ifdef USE_M17N
   5285 			 || (WcOption.use_wide &&
   5286 			     Currentbuf->currentLine != NULL &&
   5287 			     (CharType(Currentbuf->currentLine->
   5288 				       propBuf[Currentbuf->pos]) == PC_KANJI1)
   5289 			     && Currentbuf->cursorX == press_x
   5290 			     - Currentbuf->rootX - 1)
   5291 #endif
   5292 			)) {
   5293 			displayBuffer(Currentbuf, B_NORMAL);
   5294 			followTab(t);
   5295 		    }
   5296 		    if (buf == Currentbuf)
   5297 			cursorXY(Currentbuf, cx, cy);
   5298 		}
   5299 		return;
   5300 	    }
   5301 	    else {
   5302 		delta_x = x - press_x;
   5303 		delta_y = y - press_y;
   5304 
   5305 		if (abs(delta_x) < abs(delta_y) / 3)
   5306 		    delta_x = 0;
   5307 		if (abs(delta_y) < abs(delta_x) / 3)
   5308 		    delta_y = 0;
   5309 		if (reverse_mouse) {
   5310 		    delta_y = -delta_y;
   5311 		    delta_x = -delta_x;
   5312 		}
   5313 		if (delta_y > 0) {
   5314 		    prec_num = delta_y;
   5315 		    ldown1();
   5316 		}
   5317 		else if (delta_y < 0) {
   5318 		    prec_num = -delta_y;
   5319 		    lup1();
   5320 		}
   5321 		if (delta_x > 0) {
   5322 		    prec_num = delta_x;
   5323 		    col1L();
   5324 		}
   5325 		else if (delta_x < 0) {
   5326 		    prec_num = -delta_x;
   5327 		    col1R();
   5328 		}
   5329 	    }
   5330 	    break;
   5331 	case MOUSE_BTN2_DOWN:
   5332 	case MOUSE_BTN3_DOWN:
   5333 	    if (press_y == y && press_x == x)
   5334 		do_mouse_action(press_btn, x, y);
   5335 	    break;
   5336 	case MOUSE_BTN4_DOWN_RXVT:
   5337 	    for (i = 0; i < mouse_scroll_line(); i++)
   5338 		ldown1();
   5339 	    break;
   5340 	case MOUSE_BTN5_DOWN_RXVT:
   5341 	    for (i = 0; i < mouse_scroll_line(); i++)
   5342 		lup1();
   5343 	    break;
   5344 	}
   5345     }
   5346     else if (btn == MOUSE_BTN4_DOWN_XTERM) {
   5347 	for (i = 0; i < mouse_scroll_line(); i++)
   5348 	    ldown1();
   5349     }
   5350     else if (btn == MOUSE_BTN5_DOWN_XTERM) {
   5351 	for (i = 0; i < mouse_scroll_line(); i++)
   5352 	    lup1();
   5353     }
   5354 
   5355     if (btn != MOUSE_BTN4_DOWN_RXVT || press_btn == MOUSE_BTN_RESET) {
   5356 	press_btn = btn;
   5357 	press_x = x;
   5358 	press_y = y;
   5359     }
   5360     else {
   5361 	press_btn = MOUSE_BTN_RESET;
   5362     }
   5363 }
   5364 
   5365 DEFUN(msToggle, MOUSE_TOGGLE, "Toggle activity of mouse")
   5366 {
   5367     if (use_mouse) {
   5368 	use_mouse = FALSE;
   5369     }
   5370     else {
   5371 	use_mouse = TRUE;
   5372     }
   5373     displayBuffer(Currentbuf, B_FORCE_REDRAW);
   5374 }
   5375 
   5376 DEFUN(mouse, MOUSE, "mouse operation")
   5377 {
   5378     int btn, x, y;
   5379 
   5380     btn = (unsigned char)getch() - 32;
   5381 #if defined(__CYGWIN__) && CYGWIN_VERSION_DLL_MAJOR < 1005
   5382     if (cygwin_mouse_btn_swapped) {
   5383 	if (btn == MOUSE_BTN2_DOWN)
   5384 	    btn = MOUSE_BTN3_DOWN;
   5385 	else if (btn == MOUSE_BTN3_DOWN)
   5386 	    btn = MOUSE_BTN2_DOWN;
   5387     }
   5388 #endif
   5389     x = (unsigned char)getch() - 33;
   5390     if (x < 0)
   5391 	x += 0x100;
   5392     y = (unsigned char)getch() - 33;
   5393     if (y < 0)
   5394 	y += 0x100;
   5395 
   5396     if (x < 0 || x >= COLS || y < 0 || y > LASTLINE)
   5397 	return;
   5398     process_mouse(btn, x, y);
   5399 }
   5400 
   5401 #ifdef USE_GPM
   5402 int
   5403 gpm_process_mouse(Gpm_Event * event, void *data)
   5404 {
   5405     int btn = MOUSE_BTN_RESET, x, y;
   5406     if (event->type & GPM_UP)
   5407 	btn = MOUSE_BTN_UP;
   5408     else if (event->type & GPM_DOWN) {
   5409 	switch (event->buttons) {
   5410 	case GPM_B_LEFT:
   5411 	    btn = MOUSE_BTN1_DOWN;
   5412 	    break;
   5413 	case GPM_B_MIDDLE:
   5414 	    btn = MOUSE_BTN2_DOWN;
   5415 	    break;
   5416 	case GPM_B_RIGHT:
   5417 	    btn = MOUSE_BTN3_DOWN;
   5418 	    break;
   5419 	}
   5420     }
   5421     else {
   5422 	GPM_DRAWPOINTER(event);
   5423 	return 0;
   5424     }
   5425     x = event->x;
   5426     y = event->y;
   5427     process_mouse(btn, x - 1, y - 1);
   5428     return 0;
   5429 }
   5430 #endif				/* USE_GPM */
   5431 
   5432 #ifdef USE_SYSMOUSE
   5433 int
   5434 sysm_process_mouse(int x, int y, int nbs, int obs)
   5435 {
   5436     int btn;
   5437     int bits;
   5438 
   5439     if (obs & ~nbs)
   5440 	btn = MOUSE_BTN_UP;
   5441     else if (nbs & ~obs) {
   5442 	bits = nbs & ~obs;
   5443 	btn = bits & 0x1 ? MOUSE_BTN1_DOWN :
   5444 	    (bits & 0x2 ? MOUSE_BTN2_DOWN :
   5445 	     (bits & 0x4 ? MOUSE_BTN3_DOWN : 0));
   5446     }
   5447     else			/* nbs == obs */
   5448 	return 0;
   5449     process_mouse(btn, x, y);
   5450     return 0;
   5451 }
   5452 #endif				/* USE_SYSMOUSE */
   5453 
   5454 DEFUN(movMs, MOVE_MOUSE, "Move cursor to mouse cursor (for mouse action)")
   5455 {
   5456     if (!mouse_action.in_action)
   5457 	return;
   5458     if ((nTab > 1 || mouse_action.menu_str) &&
   5459 	mouse_action.cursorY < LastTab->y + 1)
   5460 	return;
   5461     else if (mouse_action.cursorX >= Currentbuf->rootX &&
   5462 	     mouse_action.cursorY < LASTLINE) {
   5463 	cursorXY(Currentbuf, mouse_action.cursorX - Currentbuf->rootX,
   5464 		 mouse_action.cursorY - Currentbuf->rootY);
   5465     }
   5466     displayBuffer(Currentbuf, B_NORMAL);
   5467 }
   5468 
   5469 #ifdef USE_MENU
   5470 #ifdef KANJI_SYMBOLS
   5471 #define FRAME_WIDTH 2
   5472 #else
   5473 #define FRAME_WIDTH 1
   5474 #endif
   5475 
   5476 DEFUN(menuMs, MENU_MOUSE, "Popup menu at mouse cursor (for mouse action)")
   5477 {
   5478     if (!mouse_action.in_action)
   5479 	return;
   5480     if ((nTab > 1 || mouse_action.menu_str) &&
   5481 	mouse_action.cursorY < LastTab->y + 1)
   5482 	mouse_action.cursorX -= FRAME_WIDTH + 1;
   5483     else if (mouse_action.cursorX >= Currentbuf->rootX &&
   5484 	     mouse_action.cursorY < LASTLINE) {
   5485 	cursorXY(Currentbuf, mouse_action.cursorX - Currentbuf->rootX,
   5486 		 mouse_action.cursorY - Currentbuf->rootY);
   5487 	displayBuffer(Currentbuf, B_NORMAL);
   5488     }
   5489     mainMn();
   5490 }
   5491 #endif
   5492 
   5493 DEFUN(tabMs, TAB_MOUSE, "Move to tab on mouse cursor (for mouse action)")
   5494 {
   5495     TabBuffer *tab;
   5496 
   5497     if (!mouse_action.in_action)
   5498 	return;
   5499     tab = posTab(mouse_action.cursorX, mouse_action.cursorY);
   5500     if (!tab || tab == NO_TABBUFFER)
   5501 	return;
   5502     CurrentTab = tab;
   5503     displayBuffer(Currentbuf, B_FORCE_REDRAW);
   5504 }
   5505 
   5506 DEFUN(closeTMs, CLOSE_TAB_MOUSE,
   5507       "Close tab on mouse cursor (for mouse action)")
   5508 {
   5509     TabBuffer *tab;
   5510 
   5511     if (!mouse_action.in_action)
   5512 	return;
   5513     tab = posTab(mouse_action.cursorX, mouse_action.cursorY);
   5514     if (!tab || tab == NO_TABBUFFER)
   5515 	return;
   5516     deleteTab(tab);
   5517     displayBuffer(Currentbuf, B_FORCE_REDRAW);
   5518 }
   5519 #endif				/* USE_MOUSE */
   5520 
   5521 DEFUN(dispVer, VERSION, "Display version of w3m")
   5522 {
   5523     disp_message(Sprintf("w3m version %s", w3m_version)->ptr, TRUE);
   5524 }
   5525 
   5526 DEFUN(wrapToggle, WRAP_TOGGLE, "Toggle wrap search mode")
   5527 {
   5528     if (WrapSearch) {
   5529 	WrapSearch = FALSE;
   5530 	/* FIXME: gettextize? */
   5531 	disp_message("Wrap search off", TRUE);
   5532     }
   5533     else {
   5534 	WrapSearch = TRUE;
   5535 	/* FIXME: gettextize? */
   5536 	disp_message("Wrap search on", TRUE);
   5537     }
   5538 }
   5539 
   5540 static char *
   5541 getCurWord(Buffer *buf, int *spos, int *epos)
   5542 {
   5543     char *p;
   5544     Line *l = buf->currentLine;
   5545     int b, e;
   5546 
   5547     *spos = 0;
   5548     *epos = 0;
   5549     if (l == NULL)
   5550 	return NULL;
   5551     p = l->lineBuf;
   5552     e = buf->pos;
   5553     while (e > 0 && !is_wordchar(getChar(&p[e])))
   5554 	prevChar(e, l);
   5555     if (!is_wordchar(getChar(&p[e])))
   5556 	return NULL;
   5557     b = e;
   5558     while (b > 0) {
   5559 	int tmp = b;
   5560 	prevChar(tmp, l);
   5561 	if (!is_wordchar(getChar(&p[tmp])))
   5562 	    break;
   5563 	b = tmp;
   5564     }
   5565     while (e < l->len && is_wordchar(getChar(&p[e])))
   5566 	nextChar(e, l);
   5567     *spos = b;
   5568     *epos = e;
   5569     return &p[b];
   5570 }
   5571 
   5572 static char *
   5573 GetWord(Buffer *buf)
   5574 {
   5575     int b, e;
   5576     char *p;
   5577 
   5578     if ((p = getCurWord(buf, &b, &e)) != NULL) {
   5579 	return Strnew_charp_n(p, e - b)->ptr;
   5580     }
   5581     return NULL;
   5582 }
   5583 
   5584 #ifdef USE_DICT
   5585 static void
   5586 execdict(char *word)
   5587 {
   5588     char *w, *dictcmd;
   5589     Buffer *buf;
   5590 
   5591     if (!UseDictCommand || word == NULL || *word == '\0') {
   5592 	displayBuffer(Currentbuf, B_NORMAL);
   5593 	return;
   5594     }
   5595     w = conv_to_system(word);
   5596     if (*w == '\0') {
   5597 	displayBuffer(Currentbuf, B_NORMAL);
   5598 	return;
   5599     }
   5600     dictcmd = Sprintf("%s?%s", DictCommand,
   5601 		      Str_form_quote(Strnew_charp(w))->ptr)->ptr;
   5602     buf = loadGeneralFile(dictcmd, NULL, NO_REFERER, 0, NULL);
   5603     if (buf == NULL) {
   5604 	disp_message("Execution failed", TRUE);
   5605 	return;
   5606     }
   5607     else {
   5608 	buf->filename = w;
   5609 	buf->buffername = Sprintf("%s %s", DICTBUFFERNAME, word)->ptr;
   5610 	if (buf->type == NULL)
   5611 	    buf->type = "text/plain";
   5612 	pushBuffer(buf);
   5613     }
   5614     displayBuffer(Currentbuf, B_FORCE_REDRAW);
   5615 }
   5616 
   5617 DEFUN(dictword, DICT_WORD, "Execute dictionary command (see README.dict)")
   5618 {
   5619     execdict(inputStr("(dictionary)!", ""));
   5620 }
   5621 
   5622 DEFUN(dictwordat, DICT_WORD_AT,
   5623       "Execute dictionary command for word at cursor")
   5624 {
   5625     execdict(GetWord(Currentbuf));
   5626 }
   5627 #endif				/* USE_DICT */
   5628 
   5629 void
   5630 set_buffer_environ(Buffer *buf)
   5631 {
   5632     static Buffer *prev_buf = NULL;
   5633     static Line *prev_line = NULL;
   5634     static int prev_pos = -1;
   5635     Line *l;
   5636 
   5637     if (buf == NULL)
   5638 	return;
   5639     if (buf != prev_buf) {
   5640 	set_environ("W3M_SOURCEFILE", buf->sourcefile);
   5641 	set_environ("W3M_FILENAME", buf->filename);
   5642 	set_environ("W3M_TITLE", buf->buffername);
   5643 	set_environ("W3M_URL", parsedURL2Str(&buf->currentURL)->ptr);
   5644 	set_environ("W3M_TYPE", buf->real_type ? buf->real_type : "unknown");
   5645 #ifdef USE_M17N
   5646 	set_environ("W3M_CHARSET", wc_ces_to_charset(buf->document_charset));
   5647 #endif
   5648     }
   5649     l = buf->currentLine;
   5650     if (l && (buf != prev_buf || l != prev_line || buf->pos != prev_pos)) {
   5651 	Anchor *a;
   5652 	ParsedURL pu;
   5653 	char *s = GetWord(buf);
   5654 	set_environ("W3M_CURRENT_WORD", s ? s : "");
   5655 	a = retrieveCurrentAnchor(buf);
   5656 	if (a) {
   5657 	    parseURL2(a->url, &pu, baseURL(buf));
   5658 	    set_environ("W3M_CURRENT_LINK", parsedURL2Str(&pu)->ptr);
   5659 	}
   5660 	else
   5661 	    set_environ("W3M_CURRENT_LINK", "");
   5662 	a = retrieveCurrentImg(buf);
   5663 	if (a) {
   5664 	    parseURL2(a->url, &pu, baseURL(buf));
   5665 	    set_environ("W3M_CURRENT_IMG", parsedURL2Str(&pu)->ptr);
   5666 	}
   5667 	else
   5668 	    set_environ("W3M_CURRENT_IMG", "");
   5669 	a = retrieveCurrentForm(buf);
   5670 	if (a)
   5671 	    set_environ("W3M_CURRENT_FORM", form2str((FormItemList *)a->url));
   5672 	else
   5673 	    set_environ("W3M_CURRENT_FORM", "");
   5674 	set_environ("W3M_CURRENT_LINE", Sprintf("%d",
   5675 						l->real_linenumber)->ptr);
   5676 	set_environ("W3M_CURRENT_COLUMN", Sprintf("%d",
   5677 						  buf->currentColumn +
   5678 						  buf->cursorX + 1)->ptr);
   5679     }
   5680     else if (!l) {
   5681 	set_environ("W3M_CURRENT_WORD", "");
   5682 	set_environ("W3M_CURRENT_LINK", "");
   5683 	set_environ("W3M_CURRENT_IMG", "");
   5684 	set_environ("W3M_CURRENT_FORM", "");
   5685 	set_environ("W3M_CURRENT_LINE", "0");
   5686 	set_environ("W3M_CURRENT_COLUMN", "0");
   5687     }
   5688     prev_buf = buf;
   5689     prev_line = l;
   5690     prev_pos = buf->pos;
   5691 }
   5692 
   5693 char *
   5694 searchKeyData(void)
   5695 {
   5696     char *data = NULL;
   5697 
   5698     if (CurrentKeyData != NULL && *CurrentKeyData != '\0')
   5699 	data = CurrentKeyData;
   5700     else if (CurrentCmdData != NULL && *CurrentCmdData != '\0')
   5701 	data = CurrentCmdData;
   5702     else if (CurrentKey >= 0)
   5703 	data = getKeyData(CurrentKey);
   5704     CurrentKeyData = NULL;
   5705     CurrentCmdData = NULL;
   5706     if (data == NULL || *data == '\0')
   5707 	return NULL;
   5708     return allocStr(data, -1);
   5709 }
   5710 
   5711 static int
   5712 searchKeyNum(void)
   5713 {
   5714     char *d;
   5715     int n = 1;
   5716 
   5717     d = searchKeyData();
   5718     if (d != NULL)
   5719 	n = atoi(d);
   5720     return n * PREC_NUM;
   5721 }
   5722 
   5723 #ifdef __EMX__
   5724 #ifdef USE_M17N
   5725 static char *
   5726 getCodePage(void)
   5727 {
   5728     unsigned long CpList[8], CpSize;
   5729 
   5730     if (!getenv("WINDOWID") && !DosQueryCp(sizeof(CpList), CpList, &CpSize))
   5731 	return Sprintf("CP%d", *CpList)->ptr;
   5732     return NULL;
   5733 }
   5734 #endif
   5735 #endif
   5736 
   5737 void
   5738 deleteFiles()
   5739 {
   5740     Buffer *buf;
   5741     char *f;
   5742 
   5743     for (CurrentTab = FirstTab; CurrentTab; CurrentTab = CurrentTab->nextTab) {
   5744 	while (Firstbuf && Firstbuf != NO_BUFFER) {
   5745 	    buf = Firstbuf->nextBuffer;
   5746 	    discardBuffer(Firstbuf);
   5747 	    Firstbuf = buf;
   5748 	}
   5749     }
   5750     while ((f = popText(fileToDelete)) != NULL)
   5751 	unlink(f);
   5752 }
   5753 
   5754 void
   5755 w3m_exit(int i)
   5756 {
   5757 #ifdef USE_MIGEMO
   5758     init_migemo();		/* close pipe to migemo */
   5759 #endif
   5760     stopDownload();
   5761     deleteFiles();
   5762 #ifdef USE_SSL
   5763     free_ssl_ctx();
   5764 #endif
   5765     disconnectFTP();
   5766 #ifdef USE_NNTP
   5767     disconnectNews();
   5768 #endif
   5769 #ifdef __MINGW32_VERSION
   5770     WSACleanup();
   5771 #endif
   5772     exit(i);
   5773 }
   5774 
   5775 DEFUN(execCmd, COMMAND, "Execute w3m command(s)")
   5776 {
   5777     char *data, *p;
   5778     int cmd;
   5779 
   5780     CurrentKeyData = NULL;	/* not allowed in w3m-control: */
   5781     data = searchKeyData();
   5782     if (data == NULL || *data == '\0') {
   5783 	data = inputStrHist("command [; ...]: ", "", TextHist);
   5784 	if (data == NULL) {
   5785 	    displayBuffer(Currentbuf, B_NORMAL);
   5786 	    return;
   5787 	}
   5788     }
   5789     /* data: FUNC [DATA] [; FUNC [DATA] ...] */
   5790     while (*data) {
   5791 	SKIP_BLANKS(data);
   5792 	if (*data == ';') {
   5793 	    data++;
   5794 	    continue;
   5795 	}
   5796 	p = getWord(&data);
   5797 	cmd = getFuncList(p);
   5798 	if (cmd < 0)
   5799 	    break;
   5800 	p = getQWord(&data);
   5801 	CurrentKey = -1;
   5802 	CurrentKeyData = NULL;
   5803 	CurrentCmdData = *p ? p : NULL;
   5804 #ifdef USE_MOUSE
   5805 	if (use_mouse)
   5806 	    mouse_inactive();
   5807 #endif
   5808 	w3mFuncList[cmd].func();
   5809 #ifdef USE_MOUSE
   5810 	if (use_mouse)
   5811 	    mouse_active();
   5812 #endif
   5813 	CurrentCmdData = NULL;
   5814     }
   5815     displayBuffer(Currentbuf, B_NORMAL);
   5816 }
   5817 
   5818 #ifdef USE_ALARM
   5819 static MySignalHandler
   5820 SigAlarm(SIGNAL_ARG)
   5821 {
   5822     char *data;
   5823 
   5824     if (CurrentAlarm->sec > 0) {
   5825 	CurrentKey = -1;
   5826 	CurrentKeyData = NULL;
   5827 	CurrentCmdData = data = (char *)CurrentAlarm->data;
   5828 #ifdef USE_MOUSE
   5829 	if (use_mouse)
   5830 	    mouse_inactive();
   5831 #endif
   5832 	w3mFuncList[CurrentAlarm->cmd].func();
   5833 #ifdef USE_MOUSE
   5834 	if (use_mouse)
   5835 	    mouse_active();
   5836 #endif
   5837 	CurrentCmdData = NULL;
   5838 	if (CurrentAlarm->status == AL_IMPLICIT_ONCE) {
   5839 	    CurrentAlarm->sec = 0;
   5840 	    CurrentAlarm->status = AL_UNSET;
   5841 	}
   5842 	if (Currentbuf->event) {
   5843 	    if (Currentbuf->event->status != AL_UNSET)
   5844 		CurrentAlarm = Currentbuf->event;
   5845 	    else
   5846 		Currentbuf->event = NULL;
   5847 	}
   5848 	if (!Currentbuf->event)
   5849 	    CurrentAlarm = &DefaultAlarm;
   5850 	if (CurrentAlarm->sec > 0) {
   5851 	    mySignal(SIGALRM, SigAlarm);
   5852 	    alarm(CurrentAlarm->sec);
   5853 	}
   5854     }
   5855     SIGNAL_RETURN;
   5856 }
   5857 
   5858 
   5859 DEFUN(setAlarm, ALARM, "Set alarm")
   5860 {
   5861     char *data;
   5862     int sec = 0, cmd = -1;
   5863 
   5864     CurrentKeyData = NULL;	/* not allowed in w3m-control: */
   5865     data = searchKeyData();
   5866     if (data == NULL || *data == '\0') {
   5867 	data = inputStrHist("(Alarm)sec command: ", "", TextHist);
   5868 	if (data == NULL) {
   5869 	    displayBuffer(Currentbuf, B_NORMAL);
   5870 	    return;
   5871 	}
   5872     }
   5873     if (*data != '\0') {
   5874 	sec = atoi(getWord(&data));
   5875 	if (sec > 0)
   5876 	    cmd = getFuncList(getWord(&data));
   5877     }
   5878     if (cmd >= 0) {
   5879 	data = getQWord(&data);
   5880 	setAlarmEvent(&DefaultAlarm, sec, AL_EXPLICIT, cmd, data);
   5881 	disp_message_nsec(Sprintf("%dsec %s %s", sec, w3mFuncList[cmd].id,
   5882 				  data)->ptr, FALSE, 1, FALSE, TRUE);
   5883     }
   5884     else {
   5885 	setAlarmEvent(&DefaultAlarm, 0, AL_UNSET, FUNCNAME_nulcmd, NULL);
   5886     }
   5887     displayBuffer(Currentbuf, B_NORMAL);
   5888 }
   5889 
   5890 AlarmEvent *
   5891 setAlarmEvent(AlarmEvent * event, int sec, short status, int cmd, void *data)
   5892 {
   5893     if (event == NULL)
   5894 	event = New(AlarmEvent);
   5895     event->sec = sec;
   5896     event->status = status;
   5897     event->cmd = cmd;
   5898     event->data = data;
   5899     return event;
   5900 }
   5901 #endif
   5902 
   5903 DEFUN(reinit, REINIT, "Reload configuration files")
   5904 {
   5905     char *resource = searchKeyData();
   5906 
   5907     if (resource == NULL) {
   5908 	init_rc();
   5909 	sync_with_option();
   5910 #ifdef USE_COOKIE
   5911 	initCookie();
   5912 #endif
   5913 	displayBuffer(Currentbuf, B_REDRAW_IMAGE);
   5914 	return;
   5915     }
   5916 
   5917     if (!strcasecmp(resource, "CONFIG") || !strcasecmp(resource, "RC")) {
   5918 	init_rc();
   5919 	sync_with_option();
   5920 	displayBuffer(Currentbuf, B_REDRAW_IMAGE);
   5921 	return;
   5922     }
   5923 
   5924 #ifdef USE_COOKIE
   5925     if (!strcasecmp(resource, "COOKIE")) {
   5926 	initCookie();
   5927 	return;
   5928     }
   5929 #endif
   5930 
   5931     if (!strcasecmp(resource, "KEYMAP")) {
   5932 	initKeymap(TRUE);
   5933 	return;
   5934     }
   5935 
   5936     if (!strcasecmp(resource, "MAILCAP")) {
   5937 	initMailcap();
   5938 	return;
   5939     }
   5940 
   5941 #ifdef USE_MOUSE
   5942     if (!strcasecmp(resource, "MOUSE")) {
   5943 	initMouseAction();
   5944 	displayBuffer(Currentbuf, B_REDRAW_IMAGE);
   5945 	return;
   5946     }
   5947 #endif
   5948 
   5949 #ifdef USE_MENU
   5950     if (!strcasecmp(resource, "MENU")) {
   5951 	initMenu();
   5952 	return;
   5953     }
   5954 #endif
   5955 
   5956     if (!strcasecmp(resource, "MIMETYPES")) {
   5957 	initMimeTypes();
   5958 	return;
   5959     }
   5960 
   5961 #ifdef USE_EXTERNAL_URI_LOADER
   5962     if (!strcasecmp(resource, "URIMETHODS")) {
   5963 	initURIMethods();
   5964 	return;
   5965     }
   5966 #endif
   5967 
   5968     disp_err_message(Sprintf("Don't know how to reinitialize '%s'", resource)->
   5969 		     ptr, FALSE);
   5970 }
   5971 
   5972 DEFUN(defKey, DEFINE_KEY,
   5973       "Define a binding between a key stroke and a user command")
   5974 {
   5975     char *data;
   5976 
   5977     CurrentKeyData = NULL;	/* not allowed in w3m-control: */
   5978     data = searchKeyData();
   5979     if (data == NULL || *data == '\0') {
   5980 	data = inputStrHist("Key definition: ", "", TextHist);
   5981 	if (data == NULL || *data == '\0') {
   5982 	    displayBuffer(Currentbuf, B_NORMAL);
   5983 	    return;
   5984 	}
   5985     }
   5986     setKeymap(allocStr(data, -1), -1, TRUE);
   5987     displayBuffer(Currentbuf, B_NORMAL);
   5988 }
   5989 
   5990 TabBuffer *
   5991 newTab(void)
   5992 {
   5993     TabBuffer *n;
   5994 
   5995     n = New(TabBuffer);
   5996     if (n == NULL)
   5997 	return NULL;
   5998     n->nextTab = NULL;
   5999     n->currentBuffer = NULL;
   6000     n->firstBuffer = NULL;
   6001     return n;
   6002 }
   6003 
   6004 static void
   6005 _newT(void)
   6006 {
   6007     TabBuffer *tag;
   6008     Buffer *buf;
   6009     int i;
   6010 
   6011     tag = newTab();
   6012     if (!tag)
   6013 	return;
   6014 
   6015     buf = newBuffer(Currentbuf->width);
   6016     copyBuffer(buf, Currentbuf);
   6017     buf->nextBuffer = NULL;
   6018     for (i = 0; i < MAX_LB; i++)
   6019 	buf->linkBuffer[i] = NULL;
   6020     (*buf->clone)++;
   6021     tag->firstBuffer = tag->currentBuffer = buf;
   6022 
   6023     tag->nextTab = CurrentTab->nextTab;
   6024     tag->prevTab = CurrentTab;
   6025     if (CurrentTab->nextTab)
   6026 	CurrentTab->nextTab->prevTab = tag;
   6027     else
   6028 	LastTab = tag;
   6029     CurrentTab->nextTab = tag;
   6030     CurrentTab = tag;
   6031     nTab++;
   6032 }
   6033 
   6034 DEFUN(newT, NEW_TAB, "Open new tab")
   6035 {
   6036     _newT();
   6037     displayBuffer(Currentbuf, B_REDRAW_IMAGE);
   6038 }
   6039 
   6040 static TabBuffer *
   6041 numTab(int n)
   6042 {
   6043     TabBuffer *tab;
   6044     int i;
   6045 
   6046     if (n == 0)
   6047 	return CurrentTab;
   6048     if (n == 1)
   6049 	return FirstTab;
   6050     if (nTab <= 1)
   6051 	return NULL;
   6052     for (tab = FirstTab, i = 1; tab && i < n; tab = tab->nextTab, i++) ;
   6053     return tab;
   6054 }
   6055 
   6056 void
   6057 calcTabPos(void)
   6058 {
   6059     TabBuffer *tab;
   6060 #if 0
   6061     int lcol = 0, rcol = 2, col;
   6062 #else
   6063     int lcol = 0, rcol = 0, col;
   6064 #endif
   6065     int n1, n2, na, nx, ny, ix, iy;
   6066 
   6067 #ifdef USE_MOUSE
   6068     lcol = mouse_action.menu_str ? mouse_action.menu_width : 0;
   6069 #endif
   6070 
   6071     if (nTab <= 0)
   6072 	return;
   6073     n1 = (COLS - rcol - lcol) / TabCols;
   6074     if (n1 >= nTab) {
   6075 	n2 = 1;
   6076 	ny = 1;
   6077     }
   6078     else {
   6079 	if (n1 < 0)
   6080 	    n1 = 0;
   6081 	n2 = COLS / TabCols;
   6082 	if (n2 == 0)
   6083 	    n2 = 1;
   6084 	ny = (nTab - n1 - 1) / n2 + 2;
   6085     }
   6086     na = n1 + n2 * (ny - 1);
   6087     n1 -= (na - nTab) / ny;
   6088     if (n1 < 0)
   6089 	n1 = 0;
   6090     na = n1 + n2 * (ny - 1);
   6091     tab = FirstTab;
   6092     for (iy = 0; iy < ny && tab; iy++) {
   6093 	if (iy == 0) {
   6094 	    nx = n1;
   6095 	    col = COLS - rcol - lcol;
   6096 	}
   6097 	else {
   6098 	    nx = n2 - (na - nTab + (iy - 1)) / (ny - 1);
   6099 	    col = COLS;
   6100 	}
   6101 	for (ix = 0; ix < nx && tab; ix++, tab = tab->nextTab) {
   6102 	    tab->x1 = col * ix / nx;
   6103 	    tab->x2 = col * (ix + 1) / nx - 1;
   6104 	    tab->y = iy;
   6105 	    if (iy == 0) {
   6106 		tab->x1 += lcol;
   6107 		tab->x2 += lcol;
   6108 	    }
   6109 	}
   6110     }
   6111 }
   6112 
   6113 TabBuffer *
   6114 deleteTab(TabBuffer * tab)
   6115 {
   6116     Buffer *buf, *next;
   6117 
   6118     if (nTab <= 1)
   6119 	return FirstTab;
   6120     if (tab->prevTab) {
   6121 	if (tab->nextTab)
   6122 	    tab->nextTab->prevTab = tab->prevTab;
   6123 	else
   6124 	    LastTab = tab->prevTab;
   6125 	tab->prevTab->nextTab = tab->nextTab;
   6126 	if (tab == CurrentTab)
   6127 	    CurrentTab = tab->prevTab;
   6128     }
   6129     else {			/* tab == FirstTab */
   6130 	tab->nextTab->prevTab = NULL;
   6131 	FirstTab = tab->nextTab;
   6132 	if (tab == CurrentTab)
   6133 	    CurrentTab = tab->nextTab;
   6134     }
   6135     nTab--;
   6136     buf = tab->firstBuffer;
   6137     while (buf && buf != NO_BUFFER) {
   6138 	next = buf->nextBuffer;
   6139 	discardBuffer(buf);
   6140 	buf = next;
   6141     }
   6142     return FirstTab;
   6143 }
   6144 
   6145 DEFUN(closeT, CLOSE_TAB, "Close current tab")
   6146 {
   6147     TabBuffer *tab;
   6148 
   6149     if (nTab <= 1)
   6150 	return;
   6151     if (prec_num)
   6152 	tab = numTab(PREC_NUM);
   6153     else
   6154 	tab = CurrentTab;
   6155     if (tab)
   6156 	deleteTab(tab);
   6157     displayBuffer(Currentbuf, B_REDRAW_IMAGE);
   6158 }
   6159 
   6160 DEFUN(nextT, NEXT_TAB, "Move to next tab")
   6161 {
   6162     int i;
   6163 
   6164     if (nTab <= 1)
   6165 	return;
   6166     for (i = 0; i < PREC_NUM; i++) {
   6167 	if (CurrentTab->nextTab)
   6168 	    CurrentTab = CurrentTab->nextTab;
   6169 	else
   6170 	    CurrentTab = FirstTab;
   6171     }
   6172     displayBuffer(Currentbuf, B_REDRAW_IMAGE);
   6173 }
   6174 
   6175 DEFUN(prevT, PREV_TAB, "Move to previous tab")
   6176 {
   6177     int i;
   6178 
   6179     if (nTab <= 1)
   6180 	return;
   6181     for (i = 0; i < PREC_NUM; i++) {
   6182 	if (CurrentTab->prevTab)
   6183 	    CurrentTab = CurrentTab->prevTab;
   6184 	else
   6185 	    CurrentTab = LastTab;
   6186     }
   6187     displayBuffer(Currentbuf, B_REDRAW_IMAGE);
   6188 }
   6189 
   6190 static void
   6191 followTab(TabBuffer * tab)
   6192 {
   6193     Buffer *buf;
   6194     Anchor *a;
   6195 
   6196 #ifdef USE_IMAGE
   6197     a = retrieveCurrentImg(Currentbuf);
   6198     if (!(a && a->image && a->image->map))
   6199 #endif
   6200 	a = retrieveCurrentAnchor(Currentbuf);
   6201     if (a == NULL)
   6202 	return;
   6203 
   6204     if (tab == CurrentTab) {
   6205 	check_target = FALSE;
   6206 	followA();
   6207 	check_target = TRUE;
   6208 	return;
   6209     }
   6210     _newT();
   6211     buf = Currentbuf;
   6212     check_target = FALSE;
   6213     followA();
   6214     check_target = TRUE;
   6215     if (tab == NULL) {
   6216 	if (buf != Currentbuf)
   6217 	    delBuffer(buf);
   6218 	else
   6219 	    deleteTab(CurrentTab);
   6220     }
   6221     else if (buf != Currentbuf) {
   6222 	/* buf <- p <- ... <- Currentbuf = c */
   6223 	Buffer *c, *p;
   6224 
   6225 	c = Currentbuf;
   6226 	p = prevBuffer(c, buf);
   6227 	p->nextBuffer = NULL;
   6228 	Firstbuf = buf;
   6229 	deleteTab(CurrentTab);
   6230 	CurrentTab = tab;
   6231 	for (buf = p; buf; buf = p) {
   6232 	    p = prevBuffer(c, buf);
   6233 	    pushBuffer(buf);
   6234 	}
   6235     }
   6236     displayBuffer(Currentbuf, B_FORCE_REDRAW);
   6237 }
   6238 
   6239 DEFUN(tabA, TAB_LINK, "Open current link on new tab")
   6240 {
   6241     followTab(prec_num ? numTab(PREC_NUM) : NULL);
   6242 }
   6243 
   6244 static void
   6245 tabURL0(TabBuffer * tab, char *prompt, int relative)
   6246 {
   6247     Buffer *buf;
   6248 
   6249     if (tab == CurrentTab) {
   6250 	goURL0(prompt, relative);
   6251 	return;
   6252     }
   6253     _newT();
   6254     buf = Currentbuf;
   6255     goURL0(prompt, relative);
   6256     if (tab == NULL) {
   6257 	if (buf != Currentbuf)
   6258 	    delBuffer(buf);
   6259 	else
   6260 	    deleteTab(CurrentTab);
   6261     }
   6262     else if (buf != Currentbuf) {
   6263 	/* buf <- p <- ... <- Currentbuf = c */
   6264 	Buffer *c, *p;
   6265 
   6266 	c = Currentbuf;
   6267 	p = prevBuffer(c, buf);
   6268 	p->nextBuffer = NULL;
   6269 	Firstbuf = buf;
   6270 	deleteTab(CurrentTab);
   6271 	CurrentTab = tab;
   6272 	for (buf = p; buf; buf = p) {
   6273 	    p = prevBuffer(c, buf);
   6274 	    pushBuffer(buf);
   6275 	}
   6276     }
   6277     displayBuffer(Currentbuf, B_FORCE_REDRAW);
   6278 }
   6279 
   6280 DEFUN(tabURL, TAB_GOTO, "Open URL on new tab")
   6281 {
   6282     tabURL0(prec_num ? numTab(PREC_NUM) : NULL,
   6283 	    "Goto URL on new tab: ", FALSE);
   6284 }
   6285 
   6286 DEFUN(tabrURL, TAB_GOTO_RELATIVE, "Open relative URL on new tab")
   6287 {
   6288     tabURL0(prec_num ? numTab(PREC_NUM) : NULL,
   6289 	    "Goto relative URL on new tab: ", TRUE);
   6290 }
   6291 
   6292 static void
   6293 moveTab(TabBuffer * t, TabBuffer * t2, int right)
   6294 {
   6295     if (t2 == NO_TABBUFFER)
   6296 	t2 = FirstTab;
   6297     if (!t || !t2 || t == t2 || t == NO_TABBUFFER)
   6298 	return;
   6299     if (t->prevTab) {
   6300 	if (t->nextTab)
   6301 	    t->nextTab->prevTab = t->prevTab;
   6302 	else
   6303 	    LastTab = t->prevTab;
   6304 	t->prevTab->nextTab = t->nextTab;
   6305     }
   6306     else {
   6307 	t->nextTab->prevTab = NULL;
   6308 	FirstTab = t->nextTab;
   6309     }
   6310     if (right) {
   6311 	t->nextTab = t2->nextTab;
   6312 	t->prevTab = t2;
   6313 	if (t2->nextTab)
   6314 	    t2->nextTab->prevTab = t;
   6315 	else
   6316 	    LastTab = t;
   6317 	t2->nextTab = t;
   6318     }
   6319     else {
   6320 	t->prevTab = t2->prevTab;
   6321 	t->nextTab = t2;
   6322 	if (t2->prevTab)
   6323 	    t2->prevTab->nextTab = t;
   6324 	else
   6325 	    FirstTab = t;
   6326 	t2->prevTab = t;
   6327     }
   6328     displayBuffer(Currentbuf, B_FORCE_REDRAW);
   6329 }
   6330 
   6331 DEFUN(tabR, TAB_RIGHT, "Move current tab right")
   6332 {
   6333     TabBuffer *tab;
   6334     int i;
   6335 
   6336     for (tab = CurrentTab, i = 0; tab && i < PREC_NUM;
   6337 	 tab = tab->nextTab, i++) ;
   6338     moveTab(CurrentTab, tab ? tab : LastTab, TRUE);
   6339 }
   6340 
   6341 DEFUN(tabL, TAB_LEFT, "Move current tab left")
   6342 {
   6343     TabBuffer *tab;
   6344     int i;
   6345 
   6346     for (tab = CurrentTab, i = 0; tab && i < PREC_NUM;
   6347 	 tab = tab->prevTab, i++) ;
   6348     moveTab(CurrentTab, tab ? tab : FirstTab, FALSE);
   6349 }
   6350 
   6351 void
   6352 addDownloadList(pid_t pid, char *url, char *save, char *lock, clen_t size)
   6353 {
   6354     DownloadList *d;
   6355 
   6356     d = New(DownloadList);
   6357     d->pid = pid;
   6358     d->url = url;
   6359     if (save[0] != '/' && save[0] != '~')
   6360 	save = Strnew_m_charp(CurrentDir, "/", save, NULL)->ptr;
   6361     d->save = expandPath(save);
   6362     d->lock = lock;
   6363     d->size = size;
   6364     d->time = time(0);
   6365     d->running = TRUE;
   6366     d->err = 0;
   6367     d->next = NULL;
   6368     d->prev = LastDL;
   6369     if (LastDL)
   6370 	LastDL->next = d;
   6371     else
   6372 	FirstDL = d;
   6373     LastDL = d;
   6374     add_download_list = TRUE;
   6375 }
   6376 
   6377 int
   6378 checkDownloadList(void)
   6379 {
   6380     DownloadList *d;
   6381     struct stat st;
   6382 
   6383     if (!FirstDL)
   6384 	return FALSE;
   6385     for (d = FirstDL; d != NULL; d = d->next) {
   6386 	if (d->running && !lstat(d->lock, &st))
   6387 	    return TRUE;
   6388     }
   6389     return FALSE;
   6390 }
   6391 
   6392 static char *
   6393 convert_size3(clen_t size)
   6394 {
   6395     Str tmp = Strnew();
   6396     int n;
   6397 
   6398     do {
   6399 	n = size % 1000;
   6400 	size /= 1000;
   6401 	tmp = Sprintf(size ? ",%.3d%s" : "%d%s", n, tmp->ptr);
   6402     } while (size);
   6403     return tmp->ptr;
   6404 }
   6405 
   6406 static Buffer *
   6407 DownloadListBuffer(void)
   6408 {
   6409     DownloadList *d;
   6410     Str src = NULL;
   6411     struct stat st;
   6412     time_t cur_time;
   6413     int duration, rate, eta;
   6414     size_t size;
   6415 
   6416     if (!FirstDL)
   6417 	return NULL;
   6418     cur_time = time(0);
   6419     /* FIXME: gettextize? */
   6420     src = Strnew_charp("<html><head><title>" DOWNLOAD_LIST_TITLE
   6421 		       "</title></head>\n<body><h1 align=center>"
   6422 		       DOWNLOAD_LIST_TITLE "</h1>\n"
   6423 		       "<form method=internal action=download><hr>\n");
   6424     for (d = LastDL; d != NULL; d = d->prev) {
   6425 	if (lstat(d->lock, &st))
   6426 	    d->running = FALSE;
   6427 	Strcat_charp(src, "<pre>\n");
   6428 	Strcat(src, Sprintf("%s\n  --&gt; %s\n  ", html_quote(d->url),
   6429 			    html_quote(conv_from_system(d->save))));
   6430 	duration = cur_time - d->time;
   6431 	if (!stat(d->save, &st)) {
   6432 	    size = st.st_size;
   6433 	    if (!d->running) {
   6434 		if (!d->err)
   6435 		    d->size = size;
   6436 		duration = st.st_mtime - d->time;
   6437 	    }
   6438 	}
   6439 	else
   6440 	    size = 0;
   6441 	if (d->size) {
   6442 	    int i, l = COLS - 6;
   6443 	    if (size < d->size)
   6444 		i = 1.0 * l * size / d->size;
   6445 	    else
   6446 		i = l;
   6447 	    l -= i;
   6448 	    while (i-- > 0)
   6449 		Strcat_char(src, '#');
   6450 	    while (l-- > 0)
   6451 		Strcat_char(src, '_');
   6452 	    Strcat_char(src, '\n');
   6453 	}
   6454 	if ((d->running || d->err) && size < d->size)
   6455 	    Strcat(src, Sprintf("  %s / %s bytes (%d%%)",
   6456 				convert_size3(size), convert_size3(d->size),
   6457 				(int)(100.0 * size / d->size)));
   6458 	else
   6459 	    Strcat(src, Sprintf("  %s bytes loaded", convert_size3(size)));
   6460 	if (duration > 0) {
   6461 	    rate = size / duration;
   6462 	    Strcat(src, Sprintf("  %02d:%02d:%02d  rate %s/sec",
   6463 				duration / (60 * 60), (duration / 60) % 60,
   6464 				duration % 60, convert_size(rate, 1)));
   6465 	    if (d->running && size < d->size && rate) {
   6466 		eta = (d->size - size) / rate;
   6467 		Strcat(src, Sprintf("  eta %02d:%02d:%02d", eta / (60 * 60),
   6468 				    (eta / 60) % 60, eta % 60));
   6469 	    }
   6470 	}
   6471 	Strcat_char(src, '\n');
   6472 	if (!d->running) {
   6473 	    Strcat(src, Sprintf("<input type=submit name=ok%d value=OK>",
   6474 				d->pid));
   6475 	    switch (d->err) {
   6476 	    case 0: if (size < d->size)
   6477 			Strcat_charp(src, " Download ended but probably not complete");
   6478 		    else
   6479 			Strcat_charp(src, " Download complete");
   6480 		    break;
   6481 	    case 1: Strcat_charp(src, " Error: could not open destination file");
   6482 		    break;
   6483 	    case 2: Strcat_charp(src, " Error: could not write to file (disk full)");
   6484 		    break;
   6485 	    default: Strcat_charp(src, " Error: unknown reason");
   6486 	    }
   6487 	}
   6488 	else
   6489 	    Strcat(src, Sprintf("<input type=submit name=stop%d value=STOP>",
   6490 				d->pid));
   6491 	Strcat_charp(src, "\n</pre><hr>\n");
   6492     }
   6493     Strcat_charp(src, "</form></body></html>");
   6494     return loadHTMLString(src);
   6495 }
   6496 
   6497 void
   6498 download_action(struct parsed_tagarg *arg)
   6499 {
   6500     DownloadList *d;
   6501     pid_t pid;
   6502 
   6503     for (; arg; arg = arg->next) {
   6504 	if (!strncmp(arg->arg, "stop", 4)) {
   6505 	    pid = (pid_t) atoi(&arg->arg[4]);
   6506 #ifndef __MINGW32_VERSION
   6507 	    kill(pid, SIGKILL);
   6508 #endif
   6509 	}
   6510 	else if (!strncmp(arg->arg, "ok", 2))
   6511 	    pid = (pid_t) atoi(&arg->arg[2]);
   6512 	else
   6513 	    continue;
   6514 	for (d = FirstDL; d; d = d->next) {
   6515 	    if (d->pid == pid) {
   6516 		unlink(d->lock);
   6517 		if (d->prev)
   6518 		    d->prev->next = d->next;
   6519 		else
   6520 		    FirstDL = d->next;
   6521 		if (d->next)
   6522 		    d->next->prev = d->prev;
   6523 		else
   6524 		    LastDL = d->prev;
   6525 		break;
   6526 	    }
   6527 	}
   6528     }
   6529     ldDL();
   6530 }
   6531 
   6532 void
   6533 stopDownload(void)
   6534 {
   6535     DownloadList *d;
   6536 
   6537     if (!FirstDL)
   6538 	return;
   6539     for (d = FirstDL; d != NULL; d = d->next) {
   6540 	if (!d->running)
   6541 	    continue;
   6542 #ifndef __MINGW32_VERSION
   6543 	kill(d->pid, SIGKILL);
   6544 #endif
   6545 	unlink(d->lock);
   6546     }
   6547 }
   6548 
   6549 /* download panel */
   6550 DEFUN(ldDL, DOWNLOAD_LIST, "Display download list panel")
   6551 {
   6552     Buffer *buf;
   6553     int replace = FALSE, new_tab = FALSE;
   6554 #ifdef USE_ALARM
   6555     int reload;
   6556 #endif
   6557 
   6558     if (Currentbuf->bufferprop & BP_INTERNAL &&
   6559 	!strcmp(Currentbuf->buffername, DOWNLOAD_LIST_TITLE))
   6560 	replace = TRUE;
   6561     if (!FirstDL) {
   6562 	if (replace) {
   6563 	    if (Currentbuf == Firstbuf && Currentbuf->nextBuffer == NULL) {
   6564 		if (nTab > 1)
   6565 		    deleteTab(CurrentTab);
   6566 	    }
   6567 	    else
   6568 		delBuffer(Currentbuf);
   6569 	    displayBuffer(Currentbuf, B_FORCE_REDRAW);
   6570 	}
   6571 	return;
   6572     }
   6573 #ifdef USE_ALARM
   6574     reload = checkDownloadList();
   6575 #endif
   6576     buf = DownloadListBuffer();
   6577     if (!buf) {
   6578 	displayBuffer(Currentbuf, B_NORMAL);
   6579 	return;
   6580     }
   6581     buf->bufferprop |= (BP_INTERNAL | BP_NO_URL);
   6582     if (replace) {
   6583 	COPY_BUFROOT(buf, Currentbuf);
   6584 	restorePosition(buf, Currentbuf);
   6585     }
   6586     if (!replace && open_tab_dl_list) {
   6587 	_newT();
   6588 	new_tab = TRUE;
   6589     }
   6590     pushBuffer(buf);
   6591     if (replace || new_tab)
   6592 	deletePrevBuf();
   6593 #ifdef USE_ALARM
   6594     if (reload)
   6595 	Currentbuf->event = setAlarmEvent(Currentbuf->event, 1, AL_IMPLICIT,
   6596 					  FUNCNAME_reload, NULL);
   6597 #endif
   6598     displayBuffer(Currentbuf, B_FORCE_REDRAW);
   6599 }
   6600 
   6601 static void
   6602 save_buffer_position(Buffer *buf)
   6603 {
   6604     BufferPos *b = buf->undo;
   6605 
   6606     if (!buf->firstLine)
   6607 	return;
   6608     if (b && b->top_linenumber == TOP_LINENUMBER(buf) &&
   6609 	b->cur_linenumber == CUR_LINENUMBER(buf) &&
   6610 	b->currentColumn == buf->currentColumn && b->pos == buf->pos)
   6611 	return;
   6612     b = New(BufferPos);
   6613     b->top_linenumber = TOP_LINENUMBER(buf);
   6614     b->cur_linenumber = CUR_LINENUMBER(buf);
   6615     b->currentColumn = buf->currentColumn;
   6616     b->pos = buf->pos;
   6617     b->bpos = buf->currentLine ? buf->currentLine->bpos : 0;
   6618     b->next = NULL;
   6619     b->prev = buf->undo;
   6620     if (buf->undo)
   6621 	buf->undo->next = b;
   6622     buf->undo = b;
   6623 }
   6624 
   6625 static void
   6626 resetPos(BufferPos * b)
   6627 {
   6628     Buffer buf;
   6629     Line top, cur;
   6630 
   6631     top.linenumber = b->top_linenumber;
   6632     cur.linenumber = b->cur_linenumber;
   6633     cur.bpos = b->bpos;
   6634     buf.topLine = &top;
   6635     buf.currentLine = &cur;
   6636     buf.pos = b->pos;
   6637     buf.currentColumn = b->currentColumn;
   6638     restorePosition(Currentbuf, &buf);
   6639     Currentbuf->undo = b;
   6640     displayBuffer(Currentbuf, B_FORCE_REDRAW);
   6641 }
   6642 
   6643 DEFUN(undoPos, UNDO, "Cancel the last cursor movement")
   6644 {
   6645     BufferPos *b = Currentbuf->undo;
   6646     int i;
   6647 
   6648     if (!Currentbuf->firstLine)
   6649 	return;
   6650     if (!b || !b->prev)
   6651 	return;
   6652     for (i = 0; i < PREC_NUM && b->prev; i++, b = b->prev) ;
   6653     resetPos(b);
   6654 }
   6655 
   6656 DEFUN(redoPos, REDO, "Cancel the last undo")
   6657 {
   6658     BufferPos *b = Currentbuf->undo;
   6659     int i;
   6660 
   6661     if (!Currentbuf->firstLine)
   6662 	return;
   6663     if (!b || !b->next)
   6664 	return;
   6665     for (i = 0; i < PREC_NUM && b->next; i++, b = b->next) ;
   6666     resetPos(b);
   6667 }