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