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