aboutsummaryrefslogtreecommitdiffstats
path: root/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'main.c')
-rw-r--r--main.c6597
1 files changed, 6597 insertions, 0 deletions
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..79d6026
--- /dev/null
+++ b/main.c
@@ -0,0 +1,6597 @@
+/* $Id: main.c,v 1.242 2004/04/04 16:47:20 ukai Exp $ */
+#define MAINPROGRAM
+#include "fm.h"
+#include <signal.h>
+#include <setjmp.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+#if defined(HAVE_WAITPID) || defined(HAVE_WAIT3)
+#include <sys/wait.h>
+#endif
+#include <time.h>
+#include "terms.h"
+#include "myctype.h"
+#include "regex.h"
+#ifdef USE_MOUSE
+#ifdef USE_GPM
+#include <gpm.h>
+#endif /* USE_GPM */
+#if defined(USE_GPM) || defined(USE_SYSMOUSE)
+extern int do_getch();
+#define getch() do_getch()
+#endif /* defined(USE_GPM) || defined(USE_SYSMOUSE) */
+#endif
+
+#define DSTR_LEN 256
+
+Hist *LoadHist;
+Hist *SaveHist;
+Hist *URLHist;
+Hist *ShellHist;
+Hist *TextHist;
+
+typedef struct _Event {
+ int cmd;
+ void *data;
+ struct _Event *next;
+} Event;
+static Event *CurrentEvent = NULL;
+static Event *LastEvent = NULL;
+
+#ifdef USE_ALARM
+static AlarmEvent DefaultAlarm = {
+ 0, AL_UNSET, FUNCNAME_nulcmd, NULL
+};
+static AlarmEvent *CurrentAlarm = &DefaultAlarm;
+static MySignalHandler SigAlarm(SIGNAL_ARG);
+#endif
+
+#ifdef SIGWINCH
+static int need_resize_screen = FALSE;
+static MySignalHandler resize_hook(SIGNAL_ARG);
+static MySignalHandler resize_handler(SIGNAL_ARG);
+static void resize_screen(void);
+#endif
+
+#ifdef SIGPIPE
+static MySignalHandler SigPipe(SIGNAL_ARG);
+#endif
+
+#ifdef USE_MARK
+static char *MarkString = NULL;
+#endif
+static char *SearchString = NULL;
+int (*searchRoutine) (Buffer *, char *);
+
+JMP_BUF IntReturn;
+
+static void delBuffer(Buffer *buf);
+static void cmd_loadfile(char *path);
+static void cmd_loadURL(char *url, ParsedURL *current, char *referer,
+ FormList *request);
+static void cmd_loadBuffer(Buffer *buf, int prop, int linkid);
+static void keyPressEventProc(int c);
+int show_params_p = 0;
+void show_params(FILE * fp);
+
+static char *getCurWord(Buffer *buf, int *spos, int *epos,
+ const char *badchars);
+
+static int display_ok = FALSE;
+static void do_dump(Buffer *);
+int prec_num = 0;
+int prev_key = -1;
+int on_target = 1;
+static int add_download_list = FALSE;
+
+void set_buffer_environ(Buffer *);
+static void save_buffer_position(Buffer *buf);
+
+static void _followForm(int);
+static void _goLine(char *);
+static void _newT(void);
+static void followTab(TabBuffer * tab);
+static void moveTab(TabBuffer * t, TabBuffer * t2, int right);
+static void _nextA(int);
+static void _prevA(int);
+static int check_target = TRUE;
+#define PREC_NUM (prec_num ? prec_num : 1)
+#define PREC_LIMIT 10000
+static int searchKeyNum(void);
+
+#define help() fusage(stdout, 0)
+#define usage() fusage(stderr, 1)
+
+static void
+fversion(FILE * f)
+{
+ fprintf(f, "w3m version %s, options %s\n", w3m_version,
+#if LANG == JA
+ "lang=ja"
+#else
+ "lang=en"
+#endif
+#ifdef USE_M17N
+ ",m17n"
+#endif
+#ifdef USE_IMAGE
+ ",image"
+#endif
+#ifdef USE_COLOR
+ ",color"
+#ifdef USE_ANSI_COLOR
+ ",ansi-color"
+#endif
+#endif
+#ifdef USE_MOUSE
+ ",mouse"
+#ifdef USE_GPM
+ ",gpm"
+#endif
+#ifdef USE_SYSMOUSE
+ ",sysmouse"
+#endif
+#endif
+#ifdef USE_MENU
+ ",menu"
+#endif
+#ifdef USE_COOKIE
+ ",cookie"
+#endif
+#ifdef USE_SSL
+ ",ssl"
+#ifdef USE_SSL_VERIFY
+ ",ssl-verify"
+#endif
+#endif
+#ifdef USE_EXTERNAL_URI_LOADER
+ ",external-uri-loader"
+#endif
+#ifdef USE_W3MMAILER
+ ",w3mmailer"
+#endif
+#ifdef USE_NNTP
+ ",nntp"
+#endif
+#ifdef USE_GOPHER
+ ",gopher"
+#endif
+#ifdef INET6
+ ",ipv6"
+#endif
+#ifdef USE_ALARM
+ ",alarm"
+#endif
+#ifdef USE_MARK
+ ",mark"
+#endif
+#ifdef USE_MIGEMO
+ ",migemo"
+#endif
+ );
+}
+
+static void
+fusage(FILE * f, int err)
+{
+ fversion(f);
+ /* FIXME: gettextize? */
+ fprintf(f, "usage: w3m [options] [URL or filename]\noptions:\n");
+ fprintf(f, " -t tab set tab width\n");
+ fprintf(f, " -r ignore backspace effect\n");
+ fprintf(f, " -l line # of preserved line (default 10000)\n");
+#ifdef USE_M17N
+ fprintf(f, " -I charset document charset\n");
+ fprintf(f, " -O charset display/output charset\n");
+#ifndef DEBIAN /* disabled by ukai: -s is used for squeeze multi lines */
+ fprintf(f, " -e EUC-JP\n");
+ fprintf(f, " -s Shift_JIS\n");
+ fprintf(f, " -j JIS\n");
+#endif
+#endif
+ fprintf(f, " -B load bookmark\n");
+ fprintf(f, " -bookmark file specify bookmark file\n");
+ fprintf(f, " -T type specify content-type\n");
+ fprintf(f, " -m internet message mode\n");
+ fprintf(f, " -v visual startup mode\n");
+#ifdef USE_COLOR
+ fprintf(f, " -M monochrome display\n");
+#endif /* USE_COLOR */
+ fprintf(f,
+ " -N open URL of command line on each new tab\n");
+ fprintf(f, " -F automatically render frame\n");
+ fprintf(f,
+ " -cols width specify column width (used with -dump)\n");
+ fprintf(f,
+ " -ppc count specify the number of pixels per character (4.0...32.0)\n");
+#ifdef USE_IMAGE
+ fprintf(f,
+ " -ppl count specify the number of pixels per line (4.0...64.0)\n");
+#endif
+ fprintf(f, " -dump dump formatted page into stdout\n");
+ fprintf(f,
+ " -dump_head dump response of HEAD request into stdout\n");
+ fprintf(f, " -dump_source dump page source into stdout\n");
+ fprintf(f, " -dump_both dump HEAD and source into stdout\n");
+ fprintf(f,
+ " -dump_extra dump HEAD, source, and extra information into stdout\n");
+ fprintf(f, " -post file use POST method with file content\n");
+ fprintf(f, " -header string insert string as a header\n");
+ fprintf(f, " +<num> goto <num> line\n");
+ fprintf(f, " -num show line number\n");
+ fprintf(f, " -no-proxy don't use proxy\n");
+#ifdef INET6
+ fprintf(f, " -4 IPv4 only (-o dns_order=4)\n");
+ fprintf(f, " -6 IPv6 only (-o dns_order=6)\n");
+#endif
+#ifdef USE_MOUSE
+ fprintf(f, " -no-mouse don't use mouse\n");
+#endif /* USE_MOUSE */
+#ifdef USE_COOKIE
+ fprintf(f,
+ " -cookie use cookie (-no-cookie: don't use cookie)\n");
+#endif /* USE_COOKIE */
+ fprintf(f, " -pauth user:pass proxy authentication\n");
+ fprintf(f, " -graph use graphic character\n");
+ fprintf(f, " -no-graph don't use graphic character\n");
+#ifdef DEBIAN /* replaced by ukai: pager requires -s */
+ fprintf(f, " -s squeeze multiple blank lines\n");
+#else
+ fprintf(f, " -S squeeze multiple blank lines\n");
+#endif
+ fprintf(f, " -W toggle wrap search mode\n");
+ fprintf(f, " -X don't use termcap init/deinit\n");
+ fprintf(f,
+ " -title[=TERM] set buffer name to terminal title string\n");
+ fprintf(f, " -o opt=value assign value to config option\n");
+ fprintf(f, " -show-option print all config options\n");
+ fprintf(f, " -config file specify config file\n");
+ fprintf(f, " -help print this usage message\n");
+ fprintf(f, " -version print w3m version\n");
+ fprintf(f, " -debug DO NOT USE\n");
+ if (show_params_p)
+ show_params(f);
+ exit(err);
+}
+
+#ifdef USE_M17N
+#ifdef __EMX__
+static char *getCodePage(void);
+#endif
+#endif
+
+static GC_warn_proc orig_GC_warn_proc = NULL;
+#define GC_WARN_KEEP_MAX (20)
+
+static void
+wrap_GC_warn_proc(char *msg, GC_word arg)
+{
+ if (fmInitialized) {
+ /* *INDENT-OFF* */
+ static struct {
+ char *msg;
+ GC_word arg;
+ } msg_ring[GC_WARN_KEEP_MAX];
+ /* *INDENT-ON* */
+ static int i = 0;
+ static int n = 0;
+ static int lock = 0;
+ int j;
+
+ j = (i + n) % (sizeof(msg_ring) / sizeof(msg_ring[0]));
+ msg_ring[j].msg = msg;
+ msg_ring[j].arg = arg;
+
+ if (n < sizeof(msg_ring) / sizeof(msg_ring[0]))
+ ++n;
+ else
+ ++i;
+
+ if (!lock) {
+ lock = 1;
+
+ for (; n > 0; --n, ++i) {
+ i %= sizeof(msg_ring) / sizeof(msg_ring[0]);
+ disp_message_nsec(Sprintf
+ (msg_ring[i].msg,
+ (unsigned long)msg_ring[i].arg)->ptr, FALSE,
+ 1, TRUE, FALSE);
+ }
+
+ lock = 0;
+ }
+ }
+ else if (orig_GC_warn_proc)
+ orig_GC_warn_proc(msg, arg);
+ else
+ fprintf(stderr, msg, (unsigned long)arg);
+}
+
+#ifdef SIGCHLD
+static void
+sig_chld(int signo)
+{
+ int p_stat;
+#ifdef HAVE_WAITPID
+ pid_t pid;
+
+ while ((pid = waitpid(-1, &p_stat, WNOHANG)) > 0) {
+ ;
+ }
+#elif HAVE_WAIT3
+ int pid;
+
+ while ((pid = wait3(&p_stat, WNOHANG, NULL)) > 0) {
+ ;
+ }
+#else
+ wait(&p_stat);
+#endif
+ mySignal(SIGCHLD, sig_chld);
+ return;
+}
+#endif
+
+Str
+make_optional_header_string(char *s)
+{
+ char *p;
+ Str hs;
+
+ if (strchr(s, '\n') || strchr(s, '\r'))
+ return NULL;
+ for (p = s; *p && *p != ':'; p++) ;
+ if (*p != ':' || p == s)
+ return NULL;
+ hs = Strnew_size(strlen(s) + 3);
+ Strcopy_charp_n(hs, s, p - s);
+ if (!Strcasecmp_charp(hs, "content-type"))
+ override_content_type = TRUE;
+ Strcat_charp(hs, ": ");
+ if (*(++p)) { /* not null header */
+ SKIP_BLANKS(p); /* skip white spaces */
+ Strcat_charp(hs, p);
+ }
+ Strcat_charp(hs, "\r\n");
+ return hs;
+}
+
+int
+main(int argc, char **argv, char **envp)
+{
+ Buffer *newbuf = NULL;
+ char *p, c;
+ int i;
+ InputStream redin;
+ char *line_str = NULL;
+ char **load_argv;
+ FormList *request;
+ int load_argc = 0;
+ int load_bookmark = FALSE;
+ int visual_start = FALSE;
+ int open_new_tab = FALSE;
+ char search_header = FALSE;
+ char *default_type = NULL;
+ char *post_file = NULL;
+ Str err_msg;
+#ifdef USE_M17N
+ char *Locale = NULL;
+ wc_uint8 auto_detect;
+#ifdef __EMX__
+ wc_ces CodePage;
+#endif
+#endif
+ GC_init();
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+
+#ifndef HAVE_SYS_ERRLIST
+ prepare_sys_errlist();
+#endif /* not HAVE_SYS_ERRLIST */
+
+ NO_proxy_domains = newTextList();
+ fileToDelete = newTextList();
+
+ load_argv = New_N(char *, argc - 1);
+ load_argc = 0;
+
+ CurrentDir = currentdir();
+ CurrentPid = (int)getpid();
+ BookmarkFile = NULL;
+ config_file = NULL;
+
+ /* argument search 1 */
+ for (i = 1; i < argc; i++) {
+ if (*argv[i] == '-') {
+ if (!strcmp("-config", argv[i])) {
+ argv[i] = "-dummy";
+ if (++i >= argc)
+ usage();
+ config_file = argv[i];
+ argv[i] = "-dummy";
+ }
+ else if (!strcmp("-h", argv[i]) || !strcmp("-help", argv[i]))
+ help();
+ else if (!strcmp("-V", argv[i]) || !strcmp("-version", argv[i])) {
+ fversion(stdout);
+ exit(0);
+ }
+ }
+ }
+
+#ifdef USE_M17N
+ if (non_null(Locale = getenv("LC_ALL")) ||
+ non_null(Locale = getenv("LC_CTYPE")) ||
+ non_null(Locale = getenv("LANG"))) {
+ DisplayCharset = wc_guess_locale_charset(Locale, DisplayCharset);
+ DocumentCharset = wc_guess_locale_charset(Locale, DocumentCharset);
+ SystemCharset = wc_guess_locale_charset(Locale, SystemCharset);
+ }
+#ifdef __EMX__
+ CodePage = wc_guess_charset(getCodePage(), 0);
+ if (CodePage)
+ DisplayCharset = DocumentCharset = SystemCharset = CodePage;
+#endif
+#endif
+
+ /* initializations */
+ init_rc();
+
+ LoadHist = newHist();
+ SaveHist = newHist();
+ ShellHist = newHist();
+ TextHist = newHist();
+ URLHist = newHist();
+
+#ifdef USE_M17N
+ if (FollowLocale && Locale) {
+ DisplayCharset = wc_guess_locale_charset(Locale, DisplayCharset);
+ SystemCharset = wc_guess_locale_charset(Locale, SystemCharset);
+ }
+ auto_detect = WcOption.auto_detect;
+#endif
+
+ if (!non_null(HTTP_proxy) &&
+ ((p = getenv("HTTP_PROXY")) ||
+ (p = getenv("http_proxy")) || (p = getenv("HTTP_proxy"))))
+ HTTP_proxy = p;
+#ifdef USE_SSL
+ if (!non_null(HTTPS_proxy) &&
+ ((p = getenv("HTTPS_PROXY")) ||
+ (p = getenv("https_proxy")) || (p = getenv("HTTPS_proxy"))))
+ HTTPS_proxy = p;
+ if (HTTPS_proxy == NULL && non_null(HTTP_proxy))
+ HTTPS_proxy = HTTP_proxy;
+#endif /* USE_SSL */
+#ifdef USE_GOPHER
+ if (!non_null(GOPHER_proxy) &&
+ ((p = getenv("GOPHER_PROXY")) ||
+ (p = getenv("gopher_proxy")) || (p = getenv("GOPHER_proxy"))))
+ GOPHER_proxy = p;
+#endif /* USE_GOPHER */
+ if (!non_null(FTP_proxy) &&
+ ((p = getenv("FTP_PROXY")) ||
+ (p = getenv("ftp_proxy")) || (p = getenv("FTP_proxy"))))
+ FTP_proxy = p;
+ if (!non_null(NO_proxy) &&
+ ((p = getenv("NO_PROXY")) ||
+ (p = getenv("no_proxy")) || (p = getenv("NO_proxy"))))
+ NO_proxy = p;
+#ifdef USE_NNTP
+ if (!non_null(NNTP_server) && (p = getenv("NNTPSERVER")) != NULL)
+ NNTP_server = p;
+ if (!non_null(NNTP_mode) && (p = getenv("NNTPMODE")) != NULL)
+ NNTP_mode = p;
+#endif
+
+ if (!non_null(Editor) && (p = getenv("EDITOR")) != NULL)
+ Editor = p;
+ if (!non_null(Mailer) && (p = getenv("MAILER")) != NULL)
+ Mailer = p;
+
+ /* argument search 2 */
+ i = 1;
+ while (i < argc) {
+ if (*argv[i] == '-') {
+ if (!strcmp("-t", argv[i])) {
+ if (++i >= argc)
+ usage();
+ if (atoi(argv[i]) > 0)
+ Tabstop = atoi(argv[i]);
+ }
+ else if (!strcmp("-r", argv[i]))
+ ShowEffect = FALSE;
+ else if (!strcmp("-l", argv[i])) {
+ if (++i >= argc)
+ usage();
+ if (atoi(argv[i]) > 0)
+ PagerMax = atoi(argv[i]);
+ }
+#ifdef USE_M17N
+#ifndef DEBIAN /* XXX: use -o kanjicode={S|J|E} */
+ else if (!strcmp("-s", argv[i]))
+ DisplayCharset = WC_CES_SHIFT_JIS;
+ else if (!strcmp("-j", argv[i]))
+ DisplayCharset = WC_CES_ISO_2022_JP;
+ else if (!strcmp("-e", argv[i]))
+ DisplayCharset = WC_CES_EUC_JP;
+#endif
+ else if (!strncmp("-I", argv[i], 2)) {
+ if (argv[i][2] != '\0')
+ p = argv[i] + 2;
+ else {
+ if (++i >= argc)
+ usage();
+ p = argv[i];
+ }
+ DocumentCharset = wc_guess_charset_short(p, DocumentCharset);
+ WcOption.auto_detect = WC_OPT_DETECT_OFF;
+ UseContentCharset = FALSE;
+ }
+ else if (!strncmp("-O", argv[i], 2)) {
+ if (argv[i][2] != '\0')
+ p = argv[i] + 2;
+ else {
+ if (++i >= argc)
+ usage();
+ p = argv[i];
+ }
+ DisplayCharset = wc_guess_charset_short(p, DisplayCharset);
+ }
+#endif
+ else if (!strcmp("-graph", argv[i]))
+ UseGraphicChar = TRUE;
+ else if (!strcmp("-no-graph", argv[i]))
+ UseGraphicChar = FALSE;
+ else if (!strcmp("-T", argv[i])) {
+ if (++i >= argc)
+ usage();
+ DefaultType = default_type = argv[i];
+ }
+ else if (!strcmp("-m", argv[i]))
+ SearchHeader = search_header = TRUE;
+ else if (!strcmp("-v", argv[i]))
+ visual_start = TRUE;
+ else if (!strcmp("-N", argv[i]))
+ open_new_tab = TRUE;
+#ifdef USE_COLOR
+ else if (!strcmp("-M", argv[i]))
+ useColor = FALSE;
+#endif /* USE_COLOR */
+ else if (!strcmp("-B", argv[i]))
+ load_bookmark = TRUE;
+ else if (!strcmp("-bookmark", argv[i])) {
+ if (++i >= argc)
+ usage();
+ BookmarkFile = argv[i];
+ if (BookmarkFile[0] != '~' && BookmarkFile[0] != '/') {
+ Str tmp = Strnew_charp(CurrentDir);
+ if (Strlastchar(tmp) != '/')
+ Strcat_char(tmp, '/');
+ Strcat_charp(tmp, BookmarkFile);
+ BookmarkFile = cleanupName(tmp->ptr);
+ }
+ }
+ else if (!strcmp("-F", argv[i]))
+ RenderFrame = TRUE;
+ else if (!strcmp("-W", argv[i])) {
+ if (WrapDefault)
+ WrapDefault = FALSE;
+ else
+ WrapDefault = TRUE;
+ }
+ else if (!strcmp("-dump", argv[i]))
+ w3m_dump = DUMP_BUFFER;
+ else if (!strcmp("-dump_source", argv[i]))
+ w3m_dump = DUMP_SOURCE;
+ else if (!strcmp("-dump_head", argv[i]))
+ w3m_dump = DUMP_HEAD;
+ else if (!strcmp("-dump_both", argv[i]))
+ w3m_dump = (DUMP_HEAD | DUMP_SOURCE);
+ else if (!strcmp("-dump_extra", argv[i]))
+ w3m_dump = (DUMP_HEAD | DUMP_SOURCE | DUMP_EXTRA);
+ else if (!strcmp("-halfdump", argv[i]))
+ w3m_dump = DUMP_HALFDUMP;
+ else if (!strcmp("-halfload", argv[i])) {
+ w3m_dump = 0;
+ w3m_halfload = TRUE;
+ DefaultType = default_type = "text/html";
+ }
+ else if (!strcmp("-backend", argv[i])) {
+ w3m_backend = TRUE;
+ }
+ else if (!strcmp("-backend_batch", argv[i])) {
+ w3m_backend = TRUE;
+ if (++i >= argc)
+ usage();
+ if (!backend_batch_commands)
+ backend_batch_commands = newTextList();
+ pushText(backend_batch_commands, argv[i]);
+ }
+ else if (!strcmp("-cols", argv[i])) {
+ if (++i >= argc)
+ usage();
+ COLS = atoi(argv[i]);
+ }
+ else if (!strcmp("-ppc", argv[i])) {
+ double ppc;
+ if (++i >= argc)
+ usage();
+ ppc = atof(argv[i]);
+ if (ppc >= MINIMUM_PIXEL_PER_CHAR &&
+ ppc <= MAXIMUM_PIXEL_PER_CHAR) {
+ pixel_per_char = ppc;
+ set_pixel_per_char = TRUE;
+ }
+ }
+#ifdef USE_IMAGE
+ else if (!strcmp("-ppl", argv[i])) {
+ double ppc;
+ if (++i >= argc)
+ usage();
+ ppc = atof(argv[i]);
+ if (ppc >= MINIMUM_PIXEL_PER_CHAR &&
+ ppc <= MAXIMUM_PIXEL_PER_CHAR * 2) {
+ pixel_per_line = ppc;
+ set_pixel_per_line = TRUE;
+ }
+ }
+#endif
+ else if (!strcmp("-num", argv[i]))
+ showLineNum = TRUE;
+ else if (!strcmp("-no-proxy", argv[i]))
+ use_proxy = FALSE;
+#ifdef INET6
+ else if (!strcmp("-4", argv[i]) || !strcmp("-6", argv[i]))
+ set_param_option(Sprintf("dns_order=%c", argv[i][1])->ptr);
+#endif
+ else if (!strcmp("-post", argv[i])) {
+ if (++i >= argc)
+ usage();
+ post_file = argv[i];
+ }
+ else if (!strcmp("-header", argv[i])) {
+ Str hs;
+ if (++i >= argc)
+ usage();
+ if ((hs = make_optional_header_string(argv[i])) != NULL) {
+ if (header_string == NULL)
+ header_string = hs;
+ else
+ Strcat(header_string, hs);
+ }
+ while (argv[i][0]) {
+ argv[i][0] = '\0';
+ argv[i]++;
+ }
+ }
+#ifdef USE_MOUSE
+ else if (!strcmp("-no-mouse", argv[i])) {
+ use_mouse = FALSE;
+ }
+#endif /* USE_MOUSE */
+#ifdef USE_COOKIE
+ else if (!strcmp("-no-cookie", argv[i])) {
+ use_cookie = FALSE;
+ accept_cookie = FALSE;
+ }
+ else if (!strcmp("-cookie", argv[i])) {
+ use_cookie = TRUE;
+ accept_cookie = TRUE;
+ }
+#endif /* USE_COOKIE */
+ else if (!strcmp("-pauth", argv[i])) {
+ if (++i >= argc)
+ usage();
+ proxy_auth_cookie = Strnew_m_charp("Basic ",
+ encodeB(argv[i])->ptr,
+ NULL);
+ while (argv[i][0]) {
+ argv[i][0] = '\0';
+ argv[i]++;
+ }
+ }
+#ifdef DEBIAN
+ else if (!strcmp("-s", argv[i]))
+#else
+ else if (!strcmp("-S", argv[i]))
+#endif
+ squeezeBlankLine = TRUE;
+ else if (!strcmp("-X", argv[i]))
+ Do_not_use_ti_te = TRUE;
+ else if (!strcmp("-title", argv[i]))
+ displayTitleTerm = getenv("TERM");
+ else if (!strncmp("-title=", argv[i], 7))
+ displayTitleTerm = argv[i] + 7;
+ else if (!strcmp("-o", argv[i]) ||
+ !strcmp("-show-option", argv[i])) {
+ if (!strcmp("-show-option", argv[i]) || ++i >= argc ||
+ !strcmp(argv[i], "?")) {
+ show_params(stdout);
+ exit(0);
+ }
+ if (!set_param_option(argv[i])) {
+ /* option set failed */
+ /* FIXME: gettextize? */
+ fprintf(stderr, "%s: bad option\n", argv[i]);
+ show_params_p = 1;
+ usage();
+ }
+ }
+ else if (!strcmp("-dummy", argv[i])) {
+ /* do nothing */
+ }
+ else if (!strcmp("-debug", argv[i]))
+ w3m_debug = TRUE;
+ else {
+ usage();
+ }
+ }
+ else if (*argv[i] == '+') {
+ line_str = argv[i] + 1;
+ }
+ else {
+ load_argv[load_argc++] = argv[i];
+ }
+ i++;
+ }
+
+#ifdef __WATT32__
+ if (w3m_debug)
+ dbug_init();
+ sock_init();
+#endif
+
+ FirstTab = NULL;
+ LastTab = NULL;
+ nTab = 0;
+ CurrentTab = NULL;
+ CurrentKey = -1;
+ if (BookmarkFile == NULL)
+ BookmarkFile = rcFile(BOOKMARK);
+
+ if (!isatty(1) && !w3m_dump) {
+ /* redirected output */
+ w3m_dump = DUMP_BUFFER;
+ }
+ if (w3m_dump) {
+ if (COLS == 0)
+ COLS = 80;
+ }
+
+#ifdef USE_BINMODE_STREAM
+ setmode(fileno(stdout), O_BINARY);
+#endif
+ if (!w3m_dump && !w3m_backend) {
+ fmInit();
+#ifdef SIGWINCH
+ mySignal(SIGWINCH, resize_hook);
+#else /* not SIGWINCH */
+ setlinescols();
+ setupscreen();
+#endif /* not SIGWINCH */
+ }
+#ifdef USE_IMAGE
+ else if (w3m_halfdump && displayImage)
+ activeImage = TRUE;
+#endif
+
+ sync_with_option();
+#ifdef USE_COOKIE
+ initCookie();
+#endif /* USE_COOKIE */
+#ifdef USE_HISTORY
+ if (UseHistory)
+ loadHistory(URLHist);
+#endif /* not USE_HISTORY */
+
+#ifdef USE_M17N
+ wtf_init(DocumentCharset, DisplayCharset);
+ /* if (w3m_dump)
+ * WcOption.pre_conv = WC_TRUE;
+ */
+#endif
+
+ if (w3m_backend)
+ backend();
+
+ if (w3m_dump)
+ mySignal(SIGINT, SIG_IGN);
+#ifdef SIGCHLD
+ mySignal(SIGCHLD, sig_chld);
+#endif
+#ifdef SIGPIPE
+ mySignal(SIGPIPE, SigPipe);
+#endif
+
+ orig_GC_warn_proc = GC_set_warn_proc(wrap_GC_warn_proc);
+ err_msg = Strnew();
+ if (load_argc == 0) {
+ /* no URL specified */
+ if (!isatty(0)) {
+ redin = newFileStream(fdopen(dup(0), "rb"), (void (*)())pclose);
+ newbuf = openGeneralPagerBuffer(redin);
+ dup2(1, 0);
+ }
+ else if (load_bookmark) {
+ newbuf = loadGeneralFile(BookmarkFile, NULL, NO_REFERER, 0, NULL);
+ if (newbuf == NULL)
+ Strcat_charp(err_msg, "w3m: Can't load bookmark.\n");
+ }
+ else if (visual_start) {
+ /* FIXME: gettextize? */
+ Str s_page;
+ s_page =
+ Strnew_charp
+ ("<title>W3M startup page</title><center><b>Welcome to ");
+ Strcat_charp(s_page, "<a href='http://w3m.sourceforge.net/'>");
+ Strcat_m_charp(s_page,
+ "w3m</a>!<p><p>This is w3m version ",
+ w3m_version,
+ "<br>Written by <a href='mailto:aito@fw.ipsj.or.jp'>Akinori Ito</a>",
+ NULL);
+#ifdef DEBIAN
+ Strcat_m_charp(s_page,
+ "<p>Debian package is maintained by <a href='mailto:ukai@debian.or.jp'>Fumitoshi UKAI</a>.",
+ "You can read <a href='file:///usr/share/doc/w3m/'>w3m documents on your local system</a>.",
+ NULL);
+#endif /* DEBIAN */
+ newbuf = loadHTMLString(s_page);
+ if (newbuf == NULL)
+ Strcat_charp(err_msg, "w3m: Can't load string.\n");
+ else if (newbuf != NO_BUFFER)
+ newbuf->bufferprop |= (BP_INTERNAL | BP_NO_URL);
+ }
+ else if ((p = getenv("HTTP_HOME")) != NULL ||
+ (p = getenv("WWW_HOME")) != NULL) {
+ newbuf = loadGeneralFile(p, NULL, NO_REFERER, 0, NULL);
+ if (newbuf == NULL)
+ Strcat(err_msg, Sprintf("w3m: Can't load %s.\n", p));
+ else if (newbuf != NO_BUFFER)
+ pushHashHist(URLHist, parsedURL2Str(&newbuf->currentURL)->ptr);
+ }
+ else {
+ if (fmInitialized)
+ fmTerm();
+ usage();
+ }
+ if (newbuf == NULL) {
+ if (fmInitialized)
+ fmTerm();
+ if (err_msg->length)
+ fprintf(stderr, "%s", err_msg->ptr);
+ w3m_exit(2);
+ }
+ i = -1;
+ }
+ else {
+ i = 0;
+ }
+ for (; i < load_argc; i++) {
+ if (i >= 0) {
+ SearchHeader = search_header;
+ DefaultType = default_type;
+ if (w3m_dump == DUMP_HEAD) {
+ request = New(FormList);
+ request->method = FORM_METHOD_HEAD;
+ newbuf =
+ loadGeneralFile(load_argv[i], NULL, NO_REFERER, 0,
+ request);
+ }
+ else {
+ if (post_file && i == 0) {
+ FILE *fp;
+ Str body;
+ if (!strcmp(post_file, "-"))
+ fp = stdin;
+ else
+ fp = fopen(post_file, "r");
+ if (fp == NULL) {
+ /* FIXME: gettextize? */
+ Strcat(err_msg,
+ Sprintf("w3m: Can't open %s.\n", post_file));
+ continue;
+ }
+ body = Strfgetall(fp);
+ if (fp != stdin)
+ fclose(fp);
+ request =
+ newFormList(NULL, "post", NULL, NULL, NULL, NULL,
+ NULL);
+ request->body = body->ptr;
+ request->boundary = NULL;
+ request->length = body->length;
+ }
+ else {
+ request = NULL;
+ }
+ newbuf =
+ loadGeneralFile(load_argv[i], NULL, NO_REFERER, 0,
+ request);
+ }
+ if (newbuf == NULL) {
+ /* FIXME: gettextize? */
+ Strcat(err_msg,
+ Sprintf("w3m: Can't load %s.\n", load_argv[i]));
+ continue;
+ }
+ else if (newbuf == NO_BUFFER)
+ continue;
+ switch (newbuf->real_scheme) {
+ case SCM_MAILTO:
+ break;
+ case SCM_LOCAL:
+ case SCM_LOCAL_CGI:
+ unshiftHist(LoadHist, conv_from_system(load_argv[i]));
+ default:
+ pushHashHist(URLHist, parsedURL2Str(&newbuf->currentURL)->ptr);
+ break;
+ }
+ }
+ else if (newbuf == NO_BUFFER)
+ continue;
+ if (newbuf->pagerSource ||
+ (newbuf->real_scheme == SCM_LOCAL && newbuf->header_source &&
+ newbuf->currentURL.file && strcmp(newbuf->currentURL.file, "-")))
+ newbuf->search_header = search_header;
+ if (CurrentTab == NULL) {
+ FirstTab = LastTab = CurrentTab = newTab();
+ nTab = 1;
+ Firstbuf = Currentbuf = newbuf;
+ }
+ else if (open_new_tab) {
+ _newT();
+ Currentbuf->nextBuffer = newbuf;
+ delBuffer(Currentbuf);
+ }
+ else {
+ Currentbuf->nextBuffer = newbuf;
+ Currentbuf = newbuf;
+ }
+ if (!w3m_dump || w3m_dump == DUMP_BUFFER) {
+ if (Currentbuf->frameset != NULL && RenderFrame)
+ rFrame();
+ }
+ if (w3m_dump)
+ do_dump(Currentbuf);
+ else {
+ Currentbuf = newbuf;
+#ifdef USE_BUFINFO
+ saveBufferInfo();
+#endif
+ }
+ }
+ if (w3m_dump) {
+ if (err_msg->length)
+ fprintf(stderr, "%s", err_msg->ptr);
+#ifdef USE_COOKIE
+ save_cookies();
+#endif /* USE_COOKIE */
+ w3m_exit(0);
+ }
+
+ if (add_download_list) {
+ add_download_list = FALSE;
+ CurrentTab = LastTab;
+ if (!FirstTab) {
+ FirstTab = LastTab = CurrentTab = newTab();
+ nTab = 1;
+ }
+ if (!Firstbuf || Firstbuf == NO_BUFFER) {
+ Firstbuf = Currentbuf = newBuffer(INIT_BUFFER_WIDTH);
+ Currentbuf->bufferprop = BP_INTERNAL | BP_NO_URL;
+ Currentbuf->buffername = DOWNLOAD_LIST_TITLE;
+ }
+ else
+ Currentbuf = Firstbuf;
+ ldDL();
+ }
+ else
+ CurrentTab = FirstTab;
+ if (!FirstTab || !Firstbuf || Firstbuf == NO_BUFFER) {
+ if (newbuf == NO_BUFFER) {
+ if (fmInitialized)
+ /* FIXME: gettextize? */
+ inputChar("Hit any key to quit w3m:");
+ }
+ if (fmInitialized)
+ fmTerm();
+ if (err_msg->length)
+ fprintf(stderr, "%s", err_msg->ptr);
+ if (newbuf == NO_BUFFER) {
+#ifdef USE_COOKIE
+ save_cookies();
+#endif /* USE_COOKIE */
+ if (!err_msg->length)
+ w3m_exit(0);
+ }
+ w3m_exit(2);
+ }
+ if (err_msg->length)
+ disp_message_nsec(err_msg->ptr, FALSE, 1, TRUE, FALSE);
+
+ SearchHeader = FALSE;
+ DefaultType = NULL;
+#ifdef USE_M17N
+ UseContentCharset = TRUE;
+ WcOption.auto_detect = auto_detect;
+#endif
+
+ Currentbuf = Firstbuf;
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+ if (line_str) {
+ _goLine(line_str);
+ }
+ for (;;) {
+ if (add_download_list) {
+ add_download_list = FALSE;
+ ldDL();
+ }
+ if (Currentbuf->submit) {
+ Anchor *a = Currentbuf->submit;
+ Currentbuf->submit = NULL;
+ gotoLine(Currentbuf, a->start.line);
+ Currentbuf->pos = a->start.pos;
+ _followForm(TRUE);
+ continue;
+ }
+ /* event processing */
+ if (CurrentEvent) {
+ CurrentKey = -1;
+ CurrentKeyData = NULL;
+ CurrentCmdData = (char *)CurrentEvent->data;
+ w3mFuncList[CurrentEvent->cmd].func();
+ CurrentCmdData = NULL;
+ CurrentEvent = CurrentEvent->next;
+ continue;
+ }
+ /* get keypress event */
+#ifdef USE_ALARM
+ if (Currentbuf->event) {
+ if (Currentbuf->event->status != AL_UNSET) {
+ CurrentAlarm = Currentbuf->event;
+ if (CurrentAlarm->sec == 0) { /* refresh (0sec) */
+ Currentbuf->event = NULL;
+ CurrentKey = -1;
+ CurrentKeyData = NULL;
+ CurrentCmdData = (char *)CurrentAlarm->data;
+ w3mFuncList[CurrentAlarm->cmd].func();
+ CurrentCmdData = NULL;
+ continue;
+ }
+ }
+ else
+ Currentbuf->event = NULL;
+ }
+ if (!Currentbuf->event)
+ CurrentAlarm = &DefaultAlarm;
+#endif
+#ifdef USE_MOUSE
+ mouse_action.in_action = FALSE;
+ if (use_mouse)
+ mouse_active();
+#endif /* USE_MOUSE */
+#ifdef USE_ALARM
+ if (CurrentAlarm->sec > 0) {
+ mySignal(SIGALRM, SigAlarm);
+ alarm(CurrentAlarm->sec);
+ }
+#endif
+#ifdef SIGWINCH
+ if (need_resize_screen) {
+ need_resize_screen = FALSE;
+ resize_screen();
+ }
+ mySignal(SIGWINCH, resize_handler);
+#endif
+#ifdef USE_IMAGE
+ if (activeImage && displayImage && Currentbuf->img &&
+ !Currentbuf->image_loaded) {
+ do {
+ loadImage(Currentbuf, IMG_FLAG_NEXT);
+ } while (sleep_till_anykey(1, 0) <= 0);
+ }
+#endif
+ c = getch();
+#ifdef SIGWINCH
+ mySignal(SIGWINCH, resize_hook);
+#endif
+#ifdef USE_ALARM
+ if (CurrentAlarm->sec > 0) {
+ alarm(0);
+ }
+#endif
+#ifdef USE_MOUSE
+ if (use_mouse)
+ mouse_inactive();
+#endif /* USE_MOUSE */
+ if (IS_ASCII(c)) { /* Ascii */
+ if (((prec_num && c == '0') || '1' <= c) && (c <= '9')) {
+ prec_num = prec_num * 10 + (int)(c - '0');
+ if (prec_num > PREC_LIMIT)
+ prec_num = PREC_LIMIT;
+ }
+ else {
+ set_buffer_environ(Currentbuf);
+ save_buffer_position(Currentbuf);
+ keyPressEventProc((int)c);
+ prec_num = 0;
+ }
+ }
+ prev_key = CurrentKey;
+ CurrentKey = -1;
+ CurrentKeyData = NULL;
+ }
+}
+
+static void
+keyPressEventProc(int c)
+{
+ CurrentKey = c;
+ w3mFuncList[(int)GlobalKeymap[c]].func();
+}
+
+void
+pushEvent(int cmd, void *data)
+{
+ Event *event;
+
+ event = New(Event);
+ event->cmd = cmd;
+ event->data = data;
+ event->next = NULL;
+ if (CurrentEvent)
+ LastEvent->next = event;
+ else
+ CurrentEvent = event;
+ LastEvent = event;
+}
+
+static void
+dump_source(Buffer *buf)
+{
+ FILE *f;
+ char c;
+ if (buf->sourcefile == NULL)
+ return;
+ f = fopen(buf->sourcefile, "r");
+ if (f == NULL)
+ return;
+ while (c = fgetc(f), !feof(f)) {
+ putchar(c);
+ }
+ fclose(f);
+}
+
+static void
+dump_head(Buffer *buf)
+{
+ TextListItem *ti;
+
+ if (buf->document_header == NULL) {
+ if (w3m_dump & DUMP_EXTRA)
+ printf("\n");
+ return;
+ }
+ for (ti = buf->document_header->first; ti; ti = ti->next) {
+#ifdef USE_M17N
+ printf("%s",
+ wc_conv_strict(ti->ptr, InnerCharset,
+ buf->document_charset)->ptr);
+#else
+ printf("%s", ti->ptr);
+#endif
+ }
+ puts("");
+}
+
+static void
+dump_extra(Buffer *buf)
+{
+ printf("W3m-current-url: %s\n", parsedURL2Str(&buf->currentURL)->ptr);
+ if (buf->baseURL)
+ printf("W3m-base-url: %s\n", parsedURL2Str(buf->baseURL)->ptr);
+#ifdef USE_M17N
+ printf("W3m-document-charset: %s\n",
+ wc_ces_to_charset(buf->document_charset));
+#endif
+#ifdef USE_SSL
+ if (buf->ssl_certificate) {
+ Str tmp = Strnew();
+ char *p;
+ for (p = buf->ssl_certificate; *p; p++) {
+ Strcat_char(tmp, *p);
+ if (*p == '\n') {
+ for (; *(p + 1) == '\n'; p++) ;
+ if (*(p + 1))
+ Strcat_char(tmp, '\t');
+ }
+ }
+ if (Strlastchar(tmp) != '\n')
+ Strcat_char(tmp, '\n');
+ printf("W3m-ssl-certificate: %s", tmp->ptr);
+ }
+#endif
+}
+
+static void
+do_dump(Buffer *buf)
+{
+ MySignalHandler(*volatile prevtrap) (SIGNAL_ARG) = NULL;
+
+ prevtrap = mySignal(SIGINT, intTrap);
+ if (SETJMP(IntReturn) != 0) {
+ mySignal(SIGINT, prevtrap);
+ return;
+ }
+ if (w3m_dump & DUMP_EXTRA)
+ dump_extra(buf);
+ if (w3m_dump & DUMP_HEAD)
+ dump_head(buf);
+ if (w3m_dump & DUMP_SOURCE)
+ dump_source(buf);
+ if (w3m_dump == DUMP_BUFFER)
+ saveBuffer(buf, stdout, FALSE);
+ mySignal(SIGINT, prevtrap);
+}
+
+DEFUN(nulcmd, NOTHING NULL @@@, "Do nothing")
+{ /* do nothing */
+}
+
+#ifdef __EMX__
+DEFUN(pcmap, PCMAP, "pcmap")
+{
+ w3mFuncList[(int)PcKeymap[(int)getch()]].func();
+}
+#else /* not __EMX__ */
+void
+pcmap(void)
+{
+}
+#endif
+
+static void
+escKeyProc(int c, int esc, unsigned char *map)
+{
+ if (CurrentKey >= 0 && CurrentKey & K_MULTI) {
+ unsigned char **mmap;
+ mmap = (unsigned char **)getKeyData(MULTI_KEY(CurrentKey));
+ if (!mmap)
+ return;
+ switch (esc) {
+ case K_ESCD:
+ map = mmap[3];
+ break;
+ case K_ESCB:
+ map = mmap[2];
+ break;
+ case K_ESC:
+ map = mmap[1];
+ break;
+ default:
+ map = mmap[0];
+ break;
+ }
+ esc |= (CurrentKey & ~0xFFFF);
+ }
+ CurrentKey = esc | c;
+ w3mFuncList[(int)map[c]].func();
+}
+
+DEFUN(escmap, ESCMAP, "ESC map")
+{
+ char c;
+ c = getch();
+ if (IS_ASCII(c))
+ escKeyProc((int)c, K_ESC, EscKeymap);
+}
+
+DEFUN(escbmap, ESCBMAP, "ESC [ map")
+{
+ char c;
+ c = getch();
+ if (IS_DIGIT(c)) {
+ escdmap(c);
+ return;
+ }
+ if (IS_ASCII(c))
+ escKeyProc((int)c, K_ESCB, EscBKeymap);
+}
+
+void
+escdmap(char c)
+{
+ int d;
+ d = (int)c - (int)'0';
+ c = getch();
+ if (IS_DIGIT(c)) {
+ d = d * 10 + (int)c - (int)'0';
+ c = getch();
+ }
+ if (c == '~')
+ escKeyProc((int)d, K_ESCD, EscDKeymap);
+}
+
+DEFUN(multimap, MULTIMAP, "multimap")
+{
+ char c;
+ c = getch();
+ if (IS_ASCII(c)) {
+ CurrentKey = K_MULTI | (CurrentKey << 16) | c;
+ escKeyProc((int)c, 0, NULL);
+ }
+}
+
+void
+tmpClearBuffer(Buffer *buf)
+{
+ if (buf->pagerSource == NULL && writeBufferCache(buf) == 0) {
+ buf->firstLine = NULL;
+ buf->topLine = NULL;
+ buf->currentLine = NULL;
+ buf->lastLine = NULL;
+ }
+}
+
+static Str currentURL(void);
+
+#ifdef USE_BUFINFO
+void
+saveBufferInfo()
+{
+ FILE *fp;
+
+ if (w3m_dump)
+ return;
+ if ((fp = fopen(rcFile("bufinfo"), "w")) == NULL) {
+ return;
+ }
+ fprintf(fp, "%s\n", currentURL()->ptr);
+ fclose(fp);
+}
+#endif
+
+static void
+pushBuffer(Buffer *buf)
+{
+ Buffer *b;
+
+#ifdef USE_IMAGE
+ deleteImage(Currentbuf);
+#endif
+ if (clear_buffer)
+ tmpClearBuffer(Currentbuf);
+ if (Firstbuf == Currentbuf) {
+ buf->nextBuffer = Firstbuf;
+ Firstbuf = Currentbuf = buf;
+ }
+ else if ((b = prevBuffer(Firstbuf, Currentbuf)) != NULL) {
+ b->nextBuffer = buf;
+ buf->nextBuffer = Currentbuf;
+ Currentbuf = buf;
+ }
+#ifdef USE_BUFINFO
+ saveBufferInfo();
+#endif
+
+}
+
+static void
+delBuffer(Buffer *buf)
+{
+ if (buf == NULL)
+ return;
+ if (Currentbuf == buf)
+ Currentbuf = buf->nextBuffer;
+ Firstbuf = deleteBuffer(Firstbuf, buf);
+ if (!Currentbuf)
+ Currentbuf = Firstbuf;
+}
+
+static void
+repBuffer(Buffer *oldbuf, Buffer *buf)
+{
+ Firstbuf = replaceBuffer(Firstbuf, oldbuf, buf);
+ Currentbuf = buf;
+}
+
+
+MySignalHandler
+intTrap(SIGNAL_ARG)
+{ /* Interrupt catcher */
+ LONGJMP(IntReturn, 0);
+ SIGNAL_RETURN;
+}
+
+#ifdef SIGWINCH
+static MySignalHandler
+resize_hook(SIGNAL_ARG)
+{
+ need_resize_screen = TRUE;
+ mySignal(SIGWINCH, resize_hook);
+ SIGNAL_RETURN;
+}
+
+static MySignalHandler
+resize_handler(SIGNAL_ARG)
+{
+ resize_screen();
+ mySignal(SIGWINCH, resize_handler);
+ SIGNAL_RETURN;
+}
+
+static void
+resize_screen(void)
+{
+ setlinescols();
+ setupscreen();
+ if (CurrentTab)
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+}
+#endif /* SIGWINCH */
+
+#ifdef SIGPIPE
+static MySignalHandler
+SigPipe(SIGNAL_ARG)
+{
+#ifdef USE_MIGEMO
+ init_migemo();
+#endif
+ mySignal(SIGPIPE, SigPipe);
+ SIGNAL_RETURN;
+}
+#endif
+
+/*
+ * Command functions: These functions are called with a keystroke.
+ */
+
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+static void
+nscroll(int n, int mode)
+{
+ Buffer *buf = Currentbuf;
+ Line *top = buf->topLine, *cur = buf->currentLine;
+ int lnum, tlnum, llnum, diff_n;
+
+ if (buf->firstLine == NULL)
+ return;
+ lnum = cur->linenumber;
+ buf->topLine = lineSkip(buf, top, n, FALSE);
+ if (buf->topLine == top) {
+ lnum += n;
+ if (lnum < buf->topLine->linenumber)
+ lnum = buf->topLine->linenumber;
+ else if (lnum > buf->lastLine->linenumber)
+ lnum = buf->lastLine->linenumber;
+ }
+ else {
+ tlnum = buf->topLine->linenumber;
+ llnum = buf->topLine->linenumber + buf->LINES - 1;
+ if (nextpage_topline)
+ diff_n = 0;
+ else
+ diff_n = n - (tlnum - top->linenumber);
+ if (lnum < tlnum)
+ lnum = tlnum + diff_n;
+ if (lnum > llnum)
+ lnum = llnum + diff_n;
+ }
+ gotoLine(buf, lnum);
+ arrangeLine(buf);
+ if (n > 0) {
+ if (buf->currentLine->bpos &&
+ buf->currentLine->bwidth >= buf->currentColumn + buf->visualpos)
+ cursorDown(buf, 1);
+ else {
+ while (buf->currentLine->next && buf->currentLine->next->bpos &&
+ buf->currentLine->bwidth + buf->currentLine->width <
+ buf->currentColumn + buf->visualpos)
+ cursorDown0(buf, 1);
+ }
+ }
+ else {
+ if (buf->currentLine->bwidth + buf->currentLine->width <
+ buf->currentColumn + buf->visualpos)
+ cursorUp(buf, 1);
+ else {
+ while (buf->currentLine->prev && buf->currentLine->bpos &&
+ buf->currentLine->bwidth >=
+ buf->currentColumn + buf->visualpos)
+ cursorUp0(buf, 1);
+ }
+ }
+ displayBuffer(buf, mode);
+}
+
+/* Move page forward */
+DEFUN(pgFore, NEXT_PAGE, "Move to next page")
+{
+ if (vi_prec_num)
+ nscroll(searchKeyNum() * (Currentbuf->LINES - 1), B_NORMAL);
+ else
+ nscroll(prec_num ? searchKeyNum() : searchKeyNum()
+ * (Currentbuf->LINES - 1), prec_num ? B_SCROLL : B_NORMAL);
+}
+
+/* Move page backward */
+DEFUN(pgBack, PREV_PAGE, "Move to previous page")
+{
+ if (vi_prec_num)
+ nscroll(-searchKeyNum() * (Currentbuf->LINES - 1), B_NORMAL);
+ else
+ nscroll(-(prec_num ? searchKeyNum() : searchKeyNum()
+ * (Currentbuf->LINES - 1)), prec_num ? B_SCROLL : B_NORMAL);
+}
+
+/* 1 line up */
+DEFUN(lup1, UP, "Scroll up one line")
+{
+ nscroll(searchKeyNum(), B_SCROLL);
+}
+
+/* 1 line down */
+DEFUN(ldown1, DOWN, "Scroll down one line")
+{
+ nscroll(-searchKeyNum(), B_SCROLL);
+}
+
+/* move cursor position to the center of screen */
+DEFUN(ctrCsrV, CENTER_V, "Move to the center column")
+{
+ int offsety;
+ if (Currentbuf->firstLine == NULL)
+ return;
+ offsety = Currentbuf->LINES / 2 - Currentbuf->cursorY;
+ if (offsety != 0) {
+#if 0
+ Currentbuf->currentLine = lineSkip(Currentbuf,
+ Currentbuf->currentLine, offsety,
+ FALSE);
+#endif
+ Currentbuf->topLine =
+ lineSkip(Currentbuf, Currentbuf->topLine, -offsety, FALSE);
+ arrangeLine(Currentbuf);
+ displayBuffer(Currentbuf, B_NORMAL);
+ }
+}
+
+DEFUN(ctrCsrH, CENTER_H, "Move to the center line")
+{
+ int offsetx;
+ if (Currentbuf->firstLine == NULL)
+ return;
+ offsetx = Currentbuf->cursorX - Currentbuf->COLS / 2;
+ if (offsetx != 0) {
+ columnSkip(Currentbuf, offsetx);
+ arrangeCursor(Currentbuf);
+ displayBuffer(Currentbuf, B_NORMAL);
+ }
+}
+
+/* Redraw screen */
+DEFUN(rdrwSc, REDRAW, "Redraw screen")
+{
+ clear();
+ arrangeCursor(Currentbuf);
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+}
+
+static void
+clear_mark(Line *l)
+{
+ int pos;
+ if (!l)
+ return;
+ for (pos = 0; pos < l->size; pos++)
+ l->propBuf[pos] &= ~PE_MARK;
+}
+
+/* search by regular expression */
+static int
+srchcore(char *volatile str, int (*func) (Buffer *, char *))
+{
+ MySignalHandler(*prevtrap) ();
+ volatile int i, result = SR_NOTFOUND;
+
+ if (str != NULL && str != SearchString)
+ SearchString = str;
+ if (SearchString == NULL || *SearchString == '\0')
+ return SR_NOTFOUND;
+
+ str = conv_search_string(SearchString, DisplayCharset);
+ prevtrap = mySignal(SIGINT, intTrap);
+ crmode();
+ if (SETJMP(IntReturn) == 0) {
+ for (i = 0; i < PREC_NUM; i++) {
+ result = func(Currentbuf, str);
+ if (i < PREC_NUM - 1 && result & SR_FOUND)
+ clear_mark(Currentbuf->currentLine);
+ }
+ }
+ mySignal(SIGINT, prevtrap);
+ term_raw();
+ return result;
+}
+
+static void
+disp_srchresult(int result, char *prompt, char *str)
+{
+ if (str == NULL)
+ str = "";
+ if (result & SR_NOTFOUND)
+ disp_message(Sprintf("Not found: %s", str)->ptr, TRUE);
+ else if (result & SR_WRAPPED)
+ disp_message(Sprintf("Search wrapped: %s", str)->ptr, TRUE);
+ else if (show_srch_str)
+ disp_message(Sprintf("%s%s", prompt, str)->ptr, TRUE);
+}
+
+static int
+dispincsrch(int ch, Str buf, Lineprop *prop)
+{
+ static Buffer sbuf;
+ static Line *currentLine;
+ static int pos;
+ char *str;
+ int do_next_search = FALSE;
+
+ if (ch == 0 && buf == NULL) {
+ SAVE_BUFPOSITION(&sbuf); /* search starting point */
+ currentLine = sbuf.currentLine;
+ pos = sbuf.pos;
+ return -1;
+ }
+
+ str = buf->ptr;
+ switch (ch) {
+ case 022: /* C-r */
+ searchRoutine = backwardSearch;
+ do_next_search = TRUE;
+ break;
+ case 023: /* C-s */
+ searchRoutine = forwardSearch;
+ do_next_search = TRUE;
+ break;
+
+#ifdef USE_MIGEMO
+ case 034:
+ migemo_active = -migemo_active;
+ goto done;
+#endif
+
+ default:
+ if (ch >= 0)
+ return ch; /* use InputKeymap */
+ }
+
+ if (do_next_search) {
+ if (*str) {
+ if (searchRoutine == forwardSearch)
+ Currentbuf->pos += 1;
+ SAVE_BUFPOSITION(&sbuf);
+ if (srchcore(str, searchRoutine) == SR_NOTFOUND
+ && searchRoutine == forwardSearch) {
+ Currentbuf->pos -= 1;
+ SAVE_BUFPOSITION(&sbuf);
+ }
+ arrangeCursor(Currentbuf);
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+ clear_mark(Currentbuf->currentLine);
+ return -1;
+ }
+ else
+ return 020; /* _prev completion for C-s C-s */
+ }
+ else if (*str) {
+ RESTORE_BUFPOSITION(&sbuf);
+ arrangeCursor(Currentbuf);
+ srchcore(str, searchRoutine);
+ arrangeCursor(Currentbuf);
+ currentLine = Currentbuf->currentLine;
+ pos = Currentbuf->pos;
+ }
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+ clear_mark(Currentbuf->currentLine);
+#ifdef USE_MIGEMO
+ done:
+ while (*str++ != '\0') {
+ if (migemo_active > 0)
+ *prop++ |= PE_UNDER;
+ else
+ *prop++ &= ~PE_UNDER;
+ }
+#endif
+ return -1;
+}
+
+void
+isrch(int (*func) (Buffer *, char *), char *prompt)
+{
+ char *str;
+ Buffer sbuf;
+ SAVE_BUFPOSITION(&sbuf);
+ dispincsrch(0, NULL, NULL); /* initialize incremental search state */
+
+ searchRoutine = func;
+ str = inputLineHistSearch(prompt, NULL, IN_STRING, TextHist, dispincsrch);
+ if (str == NULL) {
+ RESTORE_BUFPOSITION(&sbuf);
+ }
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+}
+
+void
+srch(int (*func) (Buffer *, char *), char *prompt)
+{
+ char *str;
+ int result;
+ int disp = FALSE;
+ int pos;
+
+ str = searchKeyData();
+ if (str == NULL || *str == '\0') {
+ str = inputStrHist(prompt, NULL, TextHist);
+ if (str != NULL && *str == '\0')
+ str = SearchString;
+ if (str == NULL) {
+ displayBuffer(Currentbuf, B_NORMAL);
+ return;
+ }
+ disp = TRUE;
+ }
+ pos = Currentbuf->pos;
+ if (func == forwardSearch)
+ Currentbuf->pos += 1;
+ result = srchcore(str, func);
+ if (result & SR_FOUND)
+ clear_mark(Currentbuf->currentLine);
+ else
+ Currentbuf->pos = pos;
+ displayBuffer(Currentbuf, B_NORMAL);
+ if (disp)
+ disp_srchresult(result, prompt, str);
+ searchRoutine = func;
+}
+
+/* Search regular expression forward */
+
+DEFUN(srchfor, SEARCH SEARCH_FORE WHEREIS, "Search forward")
+{
+ srch(forwardSearch, "Forward: ");
+}
+
+DEFUN(isrchfor, ISEARCH, "Incremental search forward")
+{
+ isrch(forwardSearch, "I-search: ");
+}
+
+/* Search regular expression backward */
+
+DEFUN(srchbak, SEARCH_BACK, "Search backward")
+{
+ srch(backwardSearch, "Backward: ");
+}
+
+DEFUN(isrchbak, ISEARCH_BACK, "Incremental search backward")
+{
+ isrch(backwardSearch, "I-search backward: ");
+}
+
+static void
+srch_nxtprv(int reverse)
+{
+ int result;
+ /* *INDENT-OFF* */
+ static int (*routine[2]) (Buffer *, char *) = {
+ forwardSearch, backwardSearch
+ };
+ /* *INDENT-ON* */
+
+ if (searchRoutine == NULL) {
+ /* FIXME: gettextize? */
+ disp_message("No previous regular expression", TRUE);
+ return;
+ }
+ if (reverse != 0)
+ reverse = 1;
+ if (searchRoutine == backwardSearch)
+ reverse ^= 1;
+ if (reverse == 0)
+ Currentbuf->pos += 1;
+ result = srchcore(SearchString, routine[reverse]);
+ if (result & SR_FOUND)
+ clear_mark(Currentbuf->currentLine);
+ displayBuffer(Currentbuf, B_NORMAL);
+ disp_srchresult(result, (reverse ? "Backward: " : "Forward: "),
+ SearchString);
+}
+
+/* Search next matching */
+DEFUN(srchnxt, SEARCH_NEXT, "Search next regexp")
+{
+ srch_nxtprv(0);
+}
+
+/* Search previous matching */
+DEFUN(srchprv, SEARCH_PREV, "Search previous regexp")
+{
+ srch_nxtprv(1);
+}
+
+static void
+shiftvisualpos(Buffer *buf, int shift)
+{
+ Line *l = buf->currentLine;
+ buf->visualpos -= shift;
+ if (buf->visualpos - l->bwidth >= buf->COLS)
+ buf->visualpos = l->bwidth + buf->COLS - 1;
+ else if (buf->visualpos - l->bwidth < 0)
+ buf->visualpos = l->bwidth;
+ arrangeLine(buf);
+ if (buf->visualpos - l->bwidth == -shift && buf->cursorX == 0)
+ buf->visualpos = l->bwidth;
+}
+
+/* Shift screen left */
+DEFUN(shiftl, SHIFT_LEFT, "Shift screen left")
+{
+ int column;
+
+ if (Currentbuf->firstLine == NULL)
+ return;
+ column = Currentbuf->currentColumn;
+ columnSkip(Currentbuf, searchKeyNum() * (-Currentbuf->COLS + 1) + 1);
+ shiftvisualpos(Currentbuf, Currentbuf->currentColumn - column);
+ displayBuffer(Currentbuf, B_NORMAL);
+}
+
+/* Shift screen right */
+DEFUN(shiftr, SHIFT_RIGHT, "Shift screen right")
+{
+ int column;
+
+ if (Currentbuf->firstLine == NULL)
+ return;
+ column = Currentbuf->currentColumn;
+ columnSkip(Currentbuf, searchKeyNum() * (Currentbuf->COLS - 1) - 1);
+ shiftvisualpos(Currentbuf, Currentbuf->currentColumn - column);
+ displayBuffer(Currentbuf, B_NORMAL);
+}
+
+DEFUN(col1R, RIGHT, "Shift screen one column right")
+{
+ Buffer *buf = Currentbuf;
+ Line *l = buf->currentLine;
+ int j, column, n = searchKeyNum();
+
+ if (l == NULL)
+ return;
+ for (j = 0; j < n; j++) {
+ column = buf->currentColumn;
+ columnSkip(Currentbuf, 1);
+ if (column == buf->currentColumn)
+ break;
+ shiftvisualpos(Currentbuf, 1);
+ }
+ displayBuffer(Currentbuf, B_NORMAL);
+}
+
+DEFUN(col1L, LEFT, "Shift screen one column")
+{
+ Buffer *buf = Currentbuf;
+ Line *l = buf->currentLine;
+ int j, n = searchKeyNum();
+
+ if (l == NULL)
+ return;
+ for (j = 0; j < n; j++) {
+ if (buf->currentColumn == 0)
+ break;
+ columnSkip(Currentbuf, -1);
+ shiftvisualpos(Currentbuf, -1);
+ }
+ displayBuffer(Currentbuf, B_NORMAL);
+}
+
+DEFUN(setEnv, SETENV, "Set environment variable")
+{
+ char *env;
+ char *var, *value;
+
+ CurrentKeyData = NULL; /* not allowed in w3m-control: */
+ env = searchKeyData();
+ if (env == NULL || *env == '\0' || strchr(env, '=') == NULL) {
+ if (env != NULL && *env != '\0')
+ env = Sprintf("%s=", env)->ptr;
+ env = inputStrHist("Set environ: ", env, TextHist);
+ if (env == NULL || *env == '\0') {
+ displayBuffer(Currentbuf, B_NORMAL);
+ return;
+ }
+ }
+ if ((value = strchr(env, '=')) != NULL && value > env) {
+ var = allocStr(env, value - env);
+ value++;
+ set_environ(var, value);
+ }
+ displayBuffer(Currentbuf, B_NORMAL);
+}
+
+DEFUN(pipeBuf, PIPE_BUF, "Send rendered document to pipe")
+{
+ Buffer *buf;
+ char *cmd, *tmpf;
+ FILE *f;
+
+ CurrentKeyData = NULL; /* not allowed in w3m-control: */
+ cmd = searchKeyData();
+ if (cmd == NULL || *cmd == '\0') {
+ /* FIXME: gettextize? */
+ cmd = inputLineHist("Pipe buffer to: ", "", IN_COMMAND, ShellHist);
+ }
+ if (cmd != NULL)
+ cmd = conv_to_system(cmd);
+ if (cmd == NULL || *cmd == '\0') {
+ displayBuffer(Currentbuf, B_NORMAL);
+ return;
+ }
+ tmpf = tmpfname(TMPF_DFL, NULL)->ptr;
+ f = fopen(tmpf, "w");
+ if (f == NULL) {
+ /* FIXME: gettextize? */
+ disp_message(Sprintf("Can't save buffer to %s", cmd)->ptr, TRUE);
+ return;
+ }
+ saveBuffer(Currentbuf, f, TRUE);
+ fclose(f);
+ buf = getpipe(myExtCommand(cmd, shell_quote(tmpf), TRUE)->ptr);
+ if (buf == NULL) {
+ disp_message("Execution failed", TRUE);
+ return;
+ }
+ else {
+ buf->filename = cmd;
+ buf->buffername = Sprintf("%s %s", PIPEBUFFERNAME,
+ conv_from_system(cmd))->ptr;
+ buf->bufferprop |= (BP_INTERNAL | BP_NO_URL);
+ if (buf->type == NULL)
+ buf->type = "text/plain";
+ pushBuffer(buf);
+ }
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+}
+
+/* Execute shell command and read output ac pipe. */
+DEFUN(pipesh, PIPE_SHELL, "Execute shell command and browse")
+{
+ Buffer *buf;
+ char *cmd;
+
+ CurrentKeyData = NULL; /* not allowed in w3m-control: */
+ cmd = searchKeyData();
+ if (cmd == NULL || *cmd == '\0') {
+ cmd = inputLineHist("(read shell[pipe])!", "", IN_COMMAND, ShellHist);
+ }
+ if (cmd != NULL)
+ cmd = conv_to_system(cmd);
+ if (cmd == NULL || *cmd == '\0') {
+ displayBuffer(Currentbuf, B_NORMAL);
+ return;
+ }
+ buf = getpipe(cmd);
+ if (buf == NULL) {
+ disp_message("Execution failed", TRUE);
+ return;
+ }
+ else {
+ buf->bufferprop |= (BP_INTERNAL | BP_NO_URL);
+ if (buf->type == NULL)
+ buf->type = "text/plain";
+ pushBuffer(buf);
+ }
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+}
+
+/* Execute shell command and load entire output to buffer */
+DEFUN(readsh, READ_SHELL, "Execute shell command and load")
+{
+ Buffer *buf;
+ MySignalHandler(*prevtrap) ();
+ char *cmd;
+
+ CurrentKeyData = NULL; /* not allowed in w3m-control: */
+ cmd = searchKeyData();
+ if (cmd == NULL || *cmd == '\0') {
+ cmd = inputLineHist("(read shell)!", "", IN_COMMAND, ShellHist);
+ }
+ if (cmd != NULL)
+ cmd = conv_to_system(cmd);
+ if (cmd == NULL || *cmd == '\0') {
+ displayBuffer(Currentbuf, B_NORMAL);
+ return;
+ }
+ prevtrap = mySignal(SIGINT, intTrap);
+ crmode();
+ buf = getshell(cmd);
+ mySignal(SIGINT, prevtrap);
+ term_raw();
+ if (buf == NULL) {
+ /* FIXME: gettextize? */
+ disp_message("Execution failed", TRUE);
+ return;
+ }
+ else {
+ buf->bufferprop |= (BP_INTERNAL | BP_NO_URL);
+ if (buf->type == NULL)
+ buf->type = "text/plain";
+ pushBuffer(buf);
+ }
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+}
+
+/* Execute shell command */
+DEFUN(execsh, EXEC_SHELL SHELL, "Execute shell command")
+{
+ char *cmd;
+
+ CurrentKeyData = NULL; /* not allowed in w3m-control: */
+ cmd = searchKeyData();
+ if (cmd == NULL || *cmd == '\0') {
+ cmd = inputLineHist("(exec shell)!", "", IN_COMMAND, ShellHist);
+ }
+ if (cmd != NULL)
+ cmd = conv_to_system(cmd);
+ if (cmd != NULL && *cmd != '\0') {
+ fmTerm();
+ printf("\n");
+ system(cmd);
+ /* FIXME: gettextize? */
+ printf("\n[Hit any key]");
+ fflush(stdout);
+ fmInit();
+ getch();
+ }
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+}
+
+/* Load file */
+DEFUN(ldfile, LOAD, "Load local file")
+{
+ char *fn;
+
+ fn = searchKeyData();
+ if (fn == NULL || *fn == '\0') {
+ /* FIXME: gettextize? */
+ fn = inputFilenameHist("(Load)Filename? ", NULL, LoadHist);
+ }
+ if (fn != NULL)
+ fn = conv_to_system(fn);
+ if (fn == NULL || *fn == '\0') {
+ displayBuffer(Currentbuf, B_NORMAL);
+ return;
+ }
+ cmd_loadfile(fn);
+}
+
+/* Load help file */
+DEFUN(ldhelp, HELP, "View help")
+{
+#ifdef USE_HELP_CGI
+ char *lang;
+ int n;
+ Str tmp;
+
+ lang = AcceptLang;
+ n = strcspn(lang, ";, \t");
+ tmp = Sprintf("file:///$LIB/" HELP_CGI CGI_EXTENSION "?version=%s&lang=%s",
+ Str_form_quote(Strnew_charp(w3m_version))->ptr,
+ Str_form_quote(Strnew_charp_n(lang, n))->ptr);
+ cmd_loadURL(tmp->ptr, NULL, NO_REFERER, NULL);
+#else
+ cmd_loadURL(helpFile(HELP_FILE), NULL, NO_REFERER, NULL);
+#endif
+}
+
+static void
+cmd_loadfile(char *fn)
+{
+ Buffer *buf;
+
+ buf = loadGeneralFile(file_to_url(fn), NULL, NO_REFERER, 0, NULL);
+ if (buf == NULL) {
+ /* FIXME: gettextize? */
+ char *emsg = Sprintf("%s not found", conv_from_system(fn))->ptr;
+ disp_err_message(emsg, FALSE);
+ }
+ else if (buf != NO_BUFFER) {
+ pushBuffer(buf);
+ if (RenderFrame && Currentbuf->frameset != NULL)
+ rFrame();
+ }
+ displayBuffer(Currentbuf, B_NORMAL);
+}
+
+/* Move cursor left */
+static void
+_movL(int n)
+{
+ int i, m = searchKeyNum();
+ if (Currentbuf->firstLine == NULL)
+ return;
+ for (i = 0; i < m; i++)
+ cursorLeft(Currentbuf, n);
+ displayBuffer(Currentbuf, B_NORMAL);
+}
+
+DEFUN(movL, MOVE_LEFT,
+ "Move cursor left (a half screen shift at the left edge)")
+{
+ _movL(Currentbuf->COLS / 2);
+}
+
+DEFUN(movL1, MOVE_LEFT1, "Move cursor left (1 columns shift at the left edge)")
+{
+ _movL(1);
+}
+
+/* Move cursor downward */
+static void
+_movD(int n)
+{
+ int i, m = searchKeyNum();
+ if (Currentbuf->firstLine == NULL)
+ return;
+ for (i = 0; i < m; i++)
+ cursorDown(Currentbuf, n);
+ displayBuffer(Currentbuf, B_NORMAL);
+}
+
+DEFUN(movD, MOVE_DOWN,
+ "Move cursor down (a half screen scroll at the end of screen)")
+{
+ _movD((Currentbuf->LINES + 1) / 2);
+}
+
+DEFUN(movD1, MOVE_DOWN1,
+ "Move cursor down (1 line scroll at the end of screen)")
+{
+ _movD(1);
+}
+
+/* move cursor upward */
+static void
+_movU(int n)
+{
+ int i, m = searchKeyNum();
+ if (Currentbuf->firstLine == NULL)
+ return;
+ for (i = 0; i < m; i++)
+ cursorUp(Currentbuf, n);
+ displayBuffer(Currentbuf, B_NORMAL);
+}
+
+DEFUN(movU, MOVE_UP,
+ "Move cursor up (a half screen scroll at the top of screen)")
+{
+ _movU((Currentbuf->LINES + 1) / 2);
+}
+
+DEFUN(movU1, MOVE_UP1, "Move cursor up (1 line scrol at the top of screen)")
+{
+ _movU(1);
+}
+
+/* Move cursor right */
+static void
+_movR(int n)
+{
+ int i, m = searchKeyNum();
+ if (Currentbuf->firstLine == NULL)
+ return;
+ for (i = 0; i < m; i++)
+ cursorRight(Currentbuf, n);
+ displayBuffer(Currentbuf, B_NORMAL);
+}
+
+DEFUN(movR, MOVE_RIGHT,
+ "Move cursor right (a half screen shift at the right edge)")
+{
+ _movR(Currentbuf->COLS / 2);
+}
+
+DEFUN(movR1, MOVE_RIGHT1,
+ "Move cursor right (1 columns shift at the right edge)")
+{
+ _movR(1);
+}
+
+/* movLW, movRW */
+/*
+ * From: Takashi Nishimoto <g96p0935@mse.waseda.ac.jp> Date: Mon, 14 Jun
+ * 1999 09:29:56 +0900
+ */
+#define IS_WORD_CHAR(c,p) (IS_ALNUM(c) && CharType(p) == PC_ASCII)
+
+static int
+prev_nonnull_line(Line *line)
+{
+ Line *l;
+
+ for (l = line; l != NULL && l->len == 0; l = l->prev) ;
+ if (l == NULL || l->len == 0)
+ return -1;
+
+ Currentbuf->currentLine = l;
+ if (l != line)
+ Currentbuf->pos = Currentbuf->currentLine->len;
+ return 0;
+}
+
+DEFUN(movLW, PREV_WORD, "Move to previous word")
+{
+ char *lb;
+ Lineprop *pb;
+ Line *pline;
+ int ppos;
+ int i, n = searchKeyNum();
+
+ if (Currentbuf->firstLine == NULL)
+ return;
+
+ for (i = 0; i < n; i++) {
+ pline = Currentbuf->currentLine;
+ ppos = Currentbuf->pos;
+
+ if (prev_nonnull_line(Currentbuf->currentLine) < 0)
+ goto end;
+
+ while (1) {
+ lb = Currentbuf->currentLine->lineBuf;
+ pb = Currentbuf->currentLine->propBuf;
+ while (Currentbuf->pos > 0 &&
+ !IS_WORD_CHAR(lb[Currentbuf->pos - 1],
+ pb[Currentbuf->pos - 1])) {
+ Currentbuf->pos--;
+ }
+ if (Currentbuf->pos > 0)
+ break;
+ if (prev_nonnull_line(Currentbuf->currentLine->prev) < 0) {
+ Currentbuf->currentLine = pline;
+ Currentbuf->pos = ppos;
+ goto end;
+ }
+ Currentbuf->pos = Currentbuf->currentLine->len;
+ }
+
+ lb = Currentbuf->currentLine->lineBuf;
+ pb = Currentbuf->currentLine->propBuf;
+ while (Currentbuf->pos > 0 &&
+ IS_WORD_CHAR(lb[Currentbuf->pos - 1],
+ pb[Currentbuf->pos - 1])) {
+ Currentbuf->pos--;
+ }
+ }
+ end:
+ arrangeCursor(Currentbuf);
+ displayBuffer(Currentbuf, B_NORMAL);
+}
+
+static int
+next_nonnull_line(Line *line)
+{
+ Line *l;
+
+ for (l = line; l != NULL && l->len == 0; l = l->next) ;
+
+ if (l == NULL || l->len == 0)
+ return -1;
+
+ Currentbuf->currentLine = l;
+ if (l != line)
+ Currentbuf->pos = 0;
+ return 0;
+}
+
+DEFUN(movRW, NEXT_WORD, "Move to next word")
+{
+ char *lb;
+ Lineprop *pb;
+ Line *pline;
+ int ppos;
+ int i, n = searchKeyNum();
+
+ if (Currentbuf->firstLine == NULL)
+ return;
+
+ for (i = 0; i < n; i++) {
+ pline = Currentbuf->currentLine;
+ ppos = Currentbuf->pos;
+
+ if (next_nonnull_line(Currentbuf->currentLine) < 0)
+ goto end;
+
+ lb = Currentbuf->currentLine->lineBuf;
+ pb = Currentbuf->currentLine->propBuf;
+
+ while (lb[Currentbuf->pos] &&
+ IS_WORD_CHAR(lb[Currentbuf->pos], pb[Currentbuf->pos]))
+ Currentbuf->pos++;
+
+ while (1) {
+ while (lb[Currentbuf->pos] &&
+ !IS_WORD_CHAR(lb[Currentbuf->pos], pb[Currentbuf->pos]))
+ Currentbuf->pos++;
+ if (lb[Currentbuf->pos])
+ break;
+ if (next_nonnull_line(Currentbuf->currentLine->next) < 0) {
+ Currentbuf->currentLine = pline;
+ Currentbuf->pos = ppos;
+ goto end;
+ }
+ Currentbuf->pos = 0;
+ lb = Currentbuf->currentLine->lineBuf;
+ pb = Currentbuf->currentLine->propBuf;
+ }
+ }
+ end:
+ arrangeCursor(Currentbuf);
+ displayBuffer(Currentbuf, B_NORMAL);
+}
+
+static void
+_quitfm(int confirm)
+{
+ char *ans = "y";
+
+ if (checkDownloadList())
+ /* FIXME: gettextize? */
+ ans = inputChar("Download process retains. "
+ "Do you want to exit w3m? (y/n)");
+ else if (confirm)
+ /* FIXME: gettextize? */
+ ans = inputChar("Do you want to exit w3m? (y/n)");
+ if (!(ans && TOLOWER(*ans) == 'y')) {
+ displayBuffer(Currentbuf, B_NORMAL);
+ return;
+ }
+
+ term_title(""); /* XXX */
+#ifdef USE_IMAGE
+ if (activeImage)
+ termImage();
+#endif
+ fmTerm();
+#ifdef USE_COOKIE
+ save_cookies();
+#endif /* USE_COOKIE */
+#ifdef USE_HISTORY
+ if (UseHistory && SaveURLHist)
+ saveHistory(URLHist, URLHistSize);
+#endif /* USE_HISTORY */
+ w3m_exit(0);
+}
+
+/* Quit */
+DEFUN(quitfm, ABORT EXIT, "Quit w3m without confirmation")
+{
+ _quitfm(FALSE);
+}
+
+/* Question and Quit */
+DEFUN(qquitfm, QUIT, "Quit w3m")
+{
+ _quitfm(confirm_on_quit);
+}
+
+/* Select buffer */
+DEFUN(selBuf, SELECT, "Go to buffer selection panel")
+{
+ Buffer *buf;
+ int ok;
+ char cmd;
+
+ ok = FALSE;
+ do {
+ buf = selectBuffer(Firstbuf, Currentbuf, &cmd);
+ switch (cmd) {
+ case 'B':
+ ok = TRUE;
+ break;
+ case '\n':
+ case ' ':
+ Currentbuf = buf;
+ ok = TRUE;
+ break;
+ case 'D':
+ delBuffer(buf);
+ if (Firstbuf == NULL) {
+ /* No more buffer */
+ Firstbuf = nullBuffer();
+ Currentbuf = Firstbuf;
+ }
+ break;
+ case 'q':
+ qquitfm();
+ break;
+ case 'Q':
+ quitfm();
+ break;
+ }
+ } while (!ok);
+
+ for (buf = Firstbuf; buf != NULL; buf = buf->nextBuffer) {
+ if (buf == Currentbuf)
+ continue;
+#ifdef USE_IMAGE
+ deleteImage(buf);
+#endif
+ if (clear_buffer)
+ tmpClearBuffer(buf);
+ }
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+}
+
+/* Suspend (on BSD), or run interactive shell (on SysV) */
+DEFUN(susp, INTERRUPT SUSPEND, "Stop loading document")
+{
+#ifndef SIGSTOP
+ char *shell;
+#endif /* not SIGSTOP */
+ move(LASTLINE, 0);
+ clrtoeolx();
+ refresh();
+ fmTerm();
+#ifndef SIGSTOP
+ shell = getenv("SHELL");
+ if (shell == NULL)
+ shell = "/bin/sh";
+ system(shell);
+#else /* SIGSTOP */
+ kill((pid_t) 0, SIGSTOP);
+#endif /* SIGSTOP */
+ fmInit();
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+}
+
+/* Go to specified line */
+static void
+_goLine(char *l)
+{
+ if (l == NULL || *l == '\0' || Currentbuf->currentLine == NULL) {
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+ return;
+ }
+ Currentbuf->pos = 0;
+ if (((*l == '^') || (*l == '$')) && prec_num) {
+ gotoRealLine(Currentbuf, prec_num);
+ }
+ else if (*l == '^') {
+ Currentbuf->topLine = Currentbuf->currentLine = Currentbuf->firstLine;
+ }
+ else if (*l == '$') {
+ Currentbuf->topLine =
+ lineSkip(Currentbuf, Currentbuf->lastLine,
+ -(Currentbuf->LINES + 1) / 2, TRUE);
+ Currentbuf->currentLine = Currentbuf->lastLine;
+ }
+ else
+ gotoRealLine(Currentbuf, atoi(l));
+ arrangeCursor(Currentbuf);
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+}
+
+DEFUN(goLine, GOTO_LINE, "Go to specified line")
+{
+
+ char *str = searchKeyData();
+ if (prec_num)
+ _goLine("^");
+ else if (str)
+ _goLine(str);
+ else
+ /* FIXME: gettextize? */
+ _goLine(inputStr("Goto line: ", ""));
+}
+
+
+DEFUN(goLineF, BEGIN, "Go to the first line")
+{
+ _goLine("^");
+}
+
+DEFUN(goLineL, END, "Go to the last line")
+{
+ _goLine("$");
+}
+
+/* Go to the beginning of the line */
+DEFUN(linbeg, LINE_BEGIN, "Go to the beginning of line")
+{
+ if (Currentbuf->firstLine == NULL)
+ return;
+ while (Currentbuf->currentLine->prev && Currentbuf->currentLine->bpos)
+ cursorUp0(Currentbuf, 1);
+ Currentbuf->pos = 0;
+ arrangeCursor(Currentbuf);
+ displayBuffer(Currentbuf, B_NORMAL);
+}
+
+/* Go to the bottom of the line */
+DEFUN(linend, LINE_END, "Go to the end of line")
+{
+ if (Currentbuf->firstLine == NULL)
+ return;
+ while (Currentbuf->currentLine->next
+ && Currentbuf->currentLine->next->bpos)
+ cursorDown0(Currentbuf, 1);
+ Currentbuf->pos = Currentbuf->currentLine->len - 1;
+ arrangeCursor(Currentbuf);
+ displayBuffer(Currentbuf, B_NORMAL);
+}
+
+static int
+cur_real_linenumber(Buffer *buf)
+{
+ Line *l, *cur = buf->currentLine;
+ int n;
+
+ if (!cur)
+ return 1;
+ n = cur->real_linenumber ? cur->real_linenumber : 1;
+ for (l = buf->firstLine; l && l != cur && l->real_linenumber == 0; l = l->next) { /* header */
+ if (l->bpos == 0)
+ n++;
+ }
+ return n;
+}
+
+/* Run editor on the current buffer */
+DEFUN(editBf, EDIT, "Edit current document")
+{
+ char *fn = Currentbuf->filename;
+ Str cmd;
+
+ if (fn == NULL || Currentbuf->pagerSource != NULL || /* Behaving as a pager */
+ (Currentbuf->type == NULL && Currentbuf->edit == NULL) || /* Reading shell */
+ Currentbuf->real_scheme != SCM_LOCAL || !strcmp(Currentbuf->currentURL.file, "-") || /* file is std input */
+ Currentbuf->bufferprop & BP_FRAME) { /* Frame */
+ disp_err_message("Can't edit other than local file", TRUE);
+ return;
+ }
+ if (Currentbuf->edit)
+ cmd = unquote_mailcap(Currentbuf->edit, Currentbuf->real_type, fn,
+ checkHeader(Currentbuf, "Content-Type:"), NULL);
+ else
+ cmd = myEditor(Editor, shell_quote(fn),
+ cur_real_linenumber(Currentbuf));
+ fmTerm();
+ system(cmd->ptr);
+ fmInit();
+
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+ reload();
+}
+
+/* Run editor on the current screen */
+DEFUN(editScr, EDIT_SCREEN, "Edit currently rendered document")
+{
+ char *tmpf;
+ FILE *f;
+
+ tmpf = tmpfname(TMPF_DFL, NULL)->ptr;
+ f = fopen(tmpf, "w");
+ if (f == NULL) {
+ /* FIXME: gettextize? */
+ disp_err_message(Sprintf("Can't open %s", tmpf)->ptr, TRUE);
+ return;
+ }
+ saveBuffer(Currentbuf, f, TRUE);
+ fclose(f);
+ fmTerm();
+ system(myEditor(Editor, shell_quote(tmpf),
+ cur_real_linenumber(Currentbuf))->ptr);
+ fmInit();
+ unlink(tmpf);
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+}
+
+#ifdef USE_MARK
+
+/* Set / unset mark */
+DEFUN(_mark, MARK, "Set/unset mark")
+{
+ Line *l;
+ if (!use_mark)
+ return;
+ if (Currentbuf->firstLine == NULL)
+ return;
+ l = Currentbuf->currentLine;
+ l->propBuf[Currentbuf->pos] ^= PE_MARK;
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+}
+
+/* Go to next mark */
+DEFUN(nextMk, NEXT_MARK, "Move to next word")
+{
+ Line *l;
+ int i;
+
+ if (!use_mark)
+ return;
+ if (Currentbuf->firstLine == NULL)
+ return;
+ i = Currentbuf->pos + 1;
+ l = Currentbuf->currentLine;
+ if (i >= l->len) {
+ i = 0;
+ l = l->next;
+ }
+ while (l != NULL) {
+ for (; i < l->len; i++) {
+ if (l->propBuf[i] & PE_MARK) {
+ Currentbuf->currentLine = l;
+ Currentbuf->pos = i;
+ arrangeCursor(Currentbuf);
+ displayBuffer(Currentbuf, B_NORMAL);
+ return;
+ }
+ }
+ l = l->next;
+ i = 0;
+ }
+ /* FIXME: gettextize? */
+ disp_message("No mark exist after here", TRUE);
+}
+
+/* Go to previous mark */
+DEFUN(prevMk, PREV_MARK, "Move to previous mark")
+{
+ Line *l;
+ int i;
+
+ if (!use_mark)
+ return;
+ if (Currentbuf->firstLine == NULL)
+ return;
+ i = Currentbuf->pos - 1;
+ l = Currentbuf->currentLine;
+ if (i < 0) {
+ l = l->prev;
+ if (l != NULL)
+ i = l->len - 1;
+ }
+ while (l != NULL) {
+ for (; i >= 0; i--) {
+ if (l->propBuf[i] & PE_MARK) {
+ Currentbuf->currentLine = l;
+ Currentbuf->pos = i;
+ arrangeCursor(Currentbuf);
+ displayBuffer(Currentbuf, B_NORMAL);
+ return;
+ }
+ }
+ l = l->prev;
+ if (l != NULL)
+ i = l->len - 1;
+ }
+ /* FIXME: gettextize? */
+ disp_message("No mark exist before here", TRUE);
+}
+
+/* Mark place to which the regular expression matches */
+DEFUN(reMark, REG_MARK, "Set mark using regexp")
+{
+ Line *l;
+ char *str;
+ char *p, *p1, *p2;
+
+ if (!use_mark)
+ return;
+ str = searchKeyData();
+ if (str == NULL || *str == '\0') {
+ str = inputStrHist("(Mark)Regexp: ", MarkString, TextHist);
+ if (str == NULL || *str == '\0') {
+ displayBuffer(Currentbuf, B_NORMAL);
+ return;
+ }
+ }
+ str = conv_search_string(str, DisplayCharset);
+ if ((str = regexCompile(str, 1)) != NULL) {
+ disp_message(str, TRUE);
+ return;
+ }
+ MarkString = str;
+ for (l = Currentbuf->firstLine; l != NULL; l = l->next) {
+ p = l->lineBuf;
+ for (;;) {
+ if (regexMatch(p, &l->lineBuf[l->len] - p, p == l->lineBuf) == 1) {
+ matchedPosition(&p1, &p2);
+ l->propBuf[p1 - l->lineBuf] |= PE_MARK;
+ p = p2;
+ }
+ else
+ break;
+ }
+ }
+
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+}
+#endif /* USE_MARK */
+
+static Buffer *
+loadNormalBuf(Buffer *buf, int renderframe)
+{
+ pushBuffer(buf);
+ if (renderframe && RenderFrame && Currentbuf->frameset != NULL)
+ rFrame();
+ return buf;
+}
+
+static Buffer *
+loadLink(char *url, char *target, char *referer, FormList *request)
+{
+ Buffer *buf, *nfbuf;
+ union frameset_element *f_element = NULL;
+ int flag = 0;
+ ParsedURL *base, pu;
+
+ message(Sprintf("loading %s", url)->ptr, 0, 0);
+ refresh();
+
+ base = baseURL(Currentbuf);
+ if (base == NULL ||
+ base->scheme == SCM_LOCAL || base->scheme == SCM_LOCAL_CGI)
+ referer = NO_REFERER;
+ if (referer == NULL)
+ referer = parsedURL2Str(&Currentbuf->currentURL)->ptr;
+ buf = loadGeneralFile(url, baseURL(Currentbuf), referer, flag, request);
+ if (buf == NULL) {
+ char *emsg = Sprintf("Can't load %s", url)->ptr;
+ disp_err_message(emsg, FALSE);
+ return NULL;
+ }
+
+ parseURL2(url, &pu, base);
+ pushHashHist(URLHist, parsedURL2Str(&pu)->ptr);
+
+ if (buf == NO_BUFFER) {
+ return NULL;
+ }
+ if (!on_target) /* open link as an indivisual page */
+ return loadNormalBuf(buf, TRUE);
+
+ if (do_download) /* download (thus no need to render frame) */
+ return loadNormalBuf(buf, FALSE);
+
+ if (target == NULL || /* no target specified (that means this page is not a frame page) */
+ !strcmp(target, "_top") || /* this link is specified to be opened as an indivisual * page */
+ !(Currentbuf->bufferprop & BP_FRAME) /* This page is not a frame page */
+ ) {
+ return loadNormalBuf(buf, TRUE);
+ }
+ nfbuf = Currentbuf->linkBuffer[LB_N_FRAME];
+ if (nfbuf == NULL) {
+ /* original page (that contains <frameset> tag) doesn't exist */
+ return loadNormalBuf(buf, TRUE);
+ }
+
+ f_element = search_frame(nfbuf->frameset, target);
+ if (f_element == NULL) {
+ /* specified target doesn't exist in this frameset */
+ return loadNormalBuf(buf, TRUE);
+ }
+
+ /* frame page */
+
+ /* stack current frameset */
+ pushFrameTree(&(nfbuf->frameQ), copyFrameSet(nfbuf->frameset), Currentbuf);
+ /* delete frame view buffer */
+ delBuffer(Currentbuf);
+ Currentbuf = nfbuf;
+ /* nfbuf->frameset = copyFrameSet(nfbuf->frameset); */
+ resetFrameElement(f_element, buf, referer, request);
+ discardBuffer(buf);
+ rFrame();
+ {
+ Anchor *al = NULL;
+ char *label = pu.label;
+
+ if (label && f_element->element->attr == F_BODY) {
+ al = searchAnchor(f_element->body->nameList, label);
+ }
+ if (!al) {
+ label = Strnew_m_charp("_", target, NULL)->ptr;
+ al = searchURLLabel(Currentbuf, label);
+ }
+ if (al) {
+ gotoLine(Currentbuf, al->start.line);
+ if (label_topline)
+ Currentbuf->topLine = lineSkip(Currentbuf, Currentbuf->topLine,
+ Currentbuf->currentLine->
+ linenumber -
+ Currentbuf->topLine->linenumber,
+ FALSE);
+ Currentbuf->pos = al->start.pos;
+ arrangeCursor(Currentbuf);
+ }
+ }
+ displayBuffer(Currentbuf, B_NORMAL);
+ return buf;
+}
+
+static void
+gotoLabel(char *label)
+{
+ Buffer *buf;
+ Anchor *al;
+ int i;
+
+ al = searchURLLabel(Currentbuf, label);
+ if (al == NULL) {
+ /* FIXME: gettextize? */
+ disp_message(Sprintf("%s is not found", label)->ptr, TRUE);
+ return;
+ }
+ buf = newBuffer(Currentbuf->width);
+ copyBuffer(buf, Currentbuf);
+ for (i = 0; i < MAX_LB; i++)
+ buf->linkBuffer[i] = NULL;
+ buf->currentURL.label = allocStr(label, -1);
+ pushHashHist(URLHist, parsedURL2Str(&buf->currentURL)->ptr);
+ (*buf->clone)++;
+ pushBuffer(buf);
+ gotoLine(Currentbuf, al->start.line);
+ if (label_topline)
+ Currentbuf->topLine = lineSkip(Currentbuf, Currentbuf->topLine,
+ Currentbuf->currentLine->linenumber
+ - Currentbuf->topLine->linenumber,
+ FALSE);
+ Currentbuf->pos = al->start.pos;
+ arrangeCursor(Currentbuf);
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+ return;
+}
+
+/* follow HREF link */
+DEFUN(followA, GOTO_LINK, "Go to current link")
+{
+ Line *l;
+ Anchor *a;
+ ParsedURL u;
+#ifdef USE_IMAGE
+ int x = 0, y = 0, map = 0;
+#endif
+ char *url;
+
+ if (Currentbuf->firstLine == NULL)
+ return;
+ l = Currentbuf->currentLine;
+
+#ifdef USE_IMAGE
+ a = retrieveCurrentImg(Currentbuf);
+ if (a && a->image && a->image->map) {
+ _followForm(FALSE);
+ return;
+ }
+ if (a && a->image && a->image->ismap) {
+ getMapXY(Currentbuf, a, &x, &y);
+ map = 1;
+ }
+#else
+ a = retrieveCurrentMap(Currentbuf);
+ if (a) {
+ _followForm(FALSE);
+ return;
+ }
+#endif
+ a = retrieveCurrentAnchor(Currentbuf);
+ if (a == NULL) {
+ _followForm(FALSE);
+ return;
+ }
+ if (*a->url == '#') { /* index within this buffer */
+ gotoLabel(a->url + 1);
+ return;
+ }
+ parseURL2(a->url, &u, baseURL(Currentbuf));
+ if (Strcmp(parsedURL2Str(&u), parsedURL2Str(&Currentbuf->currentURL)) == 0) {
+ /* index within this buffer */
+ if (u.label) {
+ gotoLabel(u.label);
+ return;
+ }
+ }
+ if (!strncasecmp(a->url, "mailto:", 7)
+#ifdef USE_W3MMAILER
+ && non_null(Mailer) && strchr(a->url, '?') == NULL
+#endif
+ ) {
+ /* invoke external mailer */
+ Str to = Strnew_charp(a->url + 7);
+#ifndef USE_W3MMAILER
+ char *pos;
+ if (!non_null(Mailer)) {
+ /* FIXME: gettextize? */
+ disp_err_message("no mailer is specified", TRUE);
+ return;
+ }
+ if ((pos = strchr(to->ptr, '?')) != NULL)
+ Strtruncate(to, pos - to->ptr);
+#endif
+ fmTerm();
+ system(myExtCommand(Mailer, shell_quote(file_unquote(to->ptr)),
+ FALSE)->ptr);
+ fmInit();
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+ pushHashHist(URLHist, a->url);
+ return;
+ }
+#if 0
+ else if (!strncasecmp(a->url, "news:", 5) && strchr(a->url, '@') == NULL) {
+ /* news:newsgroup is not supported */
+ /* FIXME: gettextize? */
+ disp_err_message("news:newsgroup_name is not supported", TRUE);
+ return;
+ }
+#endif /* USE_NNTP */
+ url = a->url;
+#ifdef USE_IMAGE
+ if (map)
+ url = Sprintf("%s?%d,%d", a->url, x, y)->ptr;
+#endif
+
+ if (check_target && open_tab_blank && a->target &&
+ (!strcasecmp(a->target, "_new") || !strcasecmp(a->target, "_blank"))) {
+ Buffer *buf;
+
+ _newT();
+ buf = Currentbuf;
+ loadLink(url, a->target, a->referer, NULL);
+ if (buf != Currentbuf)
+ delBuffer(buf);
+ else
+ deleteTab(CurrentTab);
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+ return;
+ }
+ loadLink(url, a->target, a->referer, NULL);
+ displayBuffer(Currentbuf, B_NORMAL);
+}
+
+/* follow HREF link in the buffer */
+void
+bufferA(void)
+{
+ on_target = FALSE;
+ followA();
+ on_target = TRUE;
+}
+
+/* view inline image */
+DEFUN(followI, VIEW_IMAGE, "View image")
+{
+ Line *l;
+ Anchor *a;
+ Buffer *buf;
+
+ if (Currentbuf->firstLine == NULL)
+ return;
+ l = Currentbuf->currentLine;
+
+ a = retrieveCurrentImg(Currentbuf);
+ if (a == NULL)
+ return;
+ /* FIXME: gettextize? */
+ message(Sprintf("loading %s", a->url)->ptr, 0, 0);
+ refresh();
+ buf = loadGeneralFile(a->url, baseURL(Currentbuf), NULL, 0, NULL);
+ if (buf == NULL) {
+ /* FIXME: gettextize? */
+ char *emsg = Sprintf("Can't load %s", a->url)->ptr;
+ disp_err_message(emsg, FALSE);
+ }
+ else if (buf != NO_BUFFER) {
+ pushBuffer(buf);
+ }
+ displayBuffer(Currentbuf, B_NORMAL);
+}
+
+static FormItemList *
+save_submit_formlist(FormItemList *src)
+{
+ FormList *list;
+ FormList *srclist;
+ FormItemList *srcitem;
+ FormItemList *item;
+ FormItemList *ret = NULL;
+#ifdef MENU_SELECT
+ FormSelectOptionItem *opt;
+ FormSelectOptionItem *curopt;
+ FormSelectOptionItem *srcopt;
+#endif /* MENU_SELECT */
+
+ if (src == NULL)
+ return NULL;
+ srclist = src->parent;
+ list = New(FormList);
+ list->method = srclist->method;
+ list->action = Strdup(srclist->action);
+#ifdef USE_M17N
+ list->charset = srclist->charset;
+#endif
+ list->enctype = srclist->enctype;
+ list->nitems = srclist->nitems;
+ list->body = srclist->body;
+ list->boundary = srclist->boundary;
+ list->length = srclist->length;
+
+ for (srcitem = srclist->item; srcitem; srcitem = srcitem->next) {
+ item = New(FormItemList);
+ item->type = srcitem->type;
+ item->name = Strdup(srcitem->name);
+ item->value = Strdup(srcitem->value);
+ item->checked = srcitem->checked;
+ item->accept = srcitem->accept;
+ item->size = srcitem->size;
+ item->rows = srcitem->rows;
+ item->maxlength = srcitem->maxlength;
+ item->readonly = srcitem->readonly;
+#ifdef MENU_SELECT
+ opt = curopt = NULL;
+ for (srcopt = srcitem->select_option; srcopt; srcopt = srcopt->next) {
+ if (!srcopt->checked)
+ continue;
+ opt = New(FormSelectOptionItem);
+ opt->value = Strdup(srcopt->value);
+ opt->label = Strdup(srcopt->label);
+ opt->checked = srcopt->checked;
+ if (item->select_option == NULL) {
+ item->select_option = curopt = opt;
+ }
+ else {
+ curopt->next = opt;
+ curopt = curopt->next;
+ }
+ }
+ item->select_option = opt;
+ if (srcitem->label)
+ item->label = Strdup(srcitem->label);
+#endif /* MENU_SELECT */
+ item->parent = list;
+ item->next = NULL;
+
+ if (list->lastitem == NULL) {
+ list->item = list->lastitem = item;
+ }
+ else {
+ list->lastitem->next = item;
+ list->lastitem = item;
+ }
+
+ if (srcitem == src)
+ ret = item;
+ }
+
+ return ret;
+}
+
+#ifdef USE_M17N
+static Str
+conv_form_encoding(Str val, FormItemList *fi, Buffer *buf)
+{
+ wc_ces charset = SystemCharset;
+
+ if (fi->parent->charset)
+ charset = fi->parent->charset;
+ else if (buf->document_charset && buf->document_charset != WC_CES_US_ASCII)
+ charset = buf->document_charset;
+ return wc_Str_conv_strict(val, InnerCharset, charset);
+}
+#else
+#define conv_form_encoding(val, fi, buf) (val)
+#endif
+
+static void
+query_from_followform(Str *query, FormItemList *fi, int multipart)
+{
+ FormItemList *f2;
+ FILE *body = NULL;
+
+ if (multipart) {
+ *query = tmpfname(TMPF_DFL, NULL);
+ body = fopen((*query)->ptr, "w");
+ if (body == NULL) {
+ return;
+ }
+ fi->parent->body = (*query)->ptr;
+ fi->parent->boundary =
+ Sprintf("------------------------------%d%ld%ld%ld", CurrentPid,
+ fi->parent, fi->parent->body, fi->parent->boundary)->ptr;
+ }
+ *query = Strnew();
+ for (f2 = fi->parent->item; f2; f2 = f2->next) {
+ if (f2->name == NULL)
+ continue;
+ /* <ISINDEX> is translated into single text form */
+ if (f2->name->length == 0 &&
+ (multipart || f2->type != FORM_INPUT_TEXT))
+ continue;
+ switch (f2->type) {
+ case FORM_INPUT_RESET:
+ /* do nothing */
+ continue;
+ case FORM_INPUT_SUBMIT:
+ case FORM_INPUT_IMAGE:
+ if (f2 != fi || f2->value == NULL)
+ continue;
+ break;
+ case FORM_INPUT_RADIO:
+ case FORM_INPUT_CHECKBOX:
+ if (!f2->checked)
+ continue;
+ }
+ if (multipart) {
+ if (f2->type == FORM_INPUT_IMAGE) {
+ int x = 0, y = 0;
+#ifdef USE_IMAGE
+ getMapXY(Currentbuf, retrieveCurrentImg(Currentbuf), &x, &y);
+#endif
+ *query = Strdup(conv_form_encoding(f2->name, fi, Currentbuf));
+ Strcat_charp(*query, ".x");
+ form_write_data(body, fi->parent->boundary, (*query)->ptr,
+ Sprintf("%d", x)->ptr);
+ *query = Strdup(conv_form_encoding(f2->name, fi, Currentbuf));
+ Strcat_charp(*query, ".y");
+ form_write_data(body, fi->parent->boundary, (*query)->ptr,
+ Sprintf("%d", y)->ptr);
+ }
+ else if (f2->name && f2->name->length > 0 && f2->value != NULL) {
+ /* not IMAGE */
+ *query = conv_form_encoding(f2->value, fi, Currentbuf);
+ if (f2->type == FORM_INPUT_FILE)
+ form_write_from_file(body, fi->parent->boundary,
+ conv_form_encoding(f2->name, fi,
+ Currentbuf)->ptr,
+ (*query)->ptr,
+ Str_conv_to_system(f2->value)->ptr);
+ else
+ form_write_data(body, fi->parent->boundary,
+ conv_form_encoding(f2->name, fi,
+ Currentbuf)->ptr,
+ (*query)->ptr);
+ }
+ }
+ else {
+ /* not multipart */
+ if (f2->type == FORM_INPUT_IMAGE) {
+ int x = 0, y = 0;
+#ifdef USE_IMAGE
+ getMapXY(Currentbuf, retrieveCurrentImg(Currentbuf), &x, &y);
+#endif
+ Strcat(*query,
+ Str_form_quote(conv_form_encoding
+ (f2->name, fi, Currentbuf)));
+ Strcat(*query, Sprintf(".x=%d&", x));
+ Strcat(*query,
+ Str_form_quote(conv_form_encoding
+ (f2->name, fi, Currentbuf)));
+ Strcat(*query, Sprintf(".y=%d", y));
+ }
+ else {
+ /* not IMAGE */
+ if (f2->name && f2->name->length > 0) {
+ Strcat(*query,
+ Str_form_quote(conv_form_encoding
+ (f2->name, fi, Currentbuf)));
+ Strcat_char(*query, '=');
+ }
+ if (f2->value != NULL) {
+ if (fi->parent->method == FORM_METHOD_INTERNAL)
+ Strcat(*query, Str_form_quote(f2->value));
+ else {
+ Strcat(*query,
+ Str_form_quote(conv_form_encoding
+ (f2->value, fi, Currentbuf)));
+ }
+ }
+ }
+ if (f2->next)
+ Strcat_char(*query, '&');
+ }
+ }
+ if (multipart) {
+ fprintf(body, "--%s--\r\n", fi->parent->boundary);
+ fclose(body);
+ }
+ else {
+ /* remove trailing & */
+ while (Strlastchar(*query) == '&')
+ Strshrink(*query, 1);
+ }
+}
+
+/* submit form */
+DEFUN(submitForm, SUBMIT, "Submit form")
+{
+ _followForm(TRUE);
+}
+
+/* process form */
+void
+followForm(void)
+{
+ _followForm(FALSE);
+}
+
+static void
+_followForm(int submit)
+{
+ Line *l;
+ Anchor *a, *a2;
+ char *p;
+ FormItemList *fi, *f2;
+ Str tmp, tmp2;
+ int multipart = 0, i;
+
+ if (Currentbuf->firstLine == NULL)
+ return;
+ l = Currentbuf->currentLine;
+
+ a = retrieveCurrentForm(Currentbuf);
+ if (a == NULL)
+ return;
+ fi = (FormItemList *)a->url;
+ switch (fi->type) {
+ case FORM_INPUT_TEXT:
+ if (submit)
+ goto do_submit;
+ if (fi->readonly)
+ /* FIXME: gettextize? */
+ disp_message_nsec("Read only field!", FALSE, 1, TRUE, FALSE);
+ /* FIXME: gettextize? */
+ p = inputStrHist("TEXT:", fi->value ? fi->value->ptr : NULL, TextHist);
+ if (p == NULL || fi->readonly)
+ break;
+ fi->value = Strnew_charp(p);
+ formUpdateBuffer(a, Currentbuf, fi);
+ if (fi->accept || fi->parent->nitems == 1)
+ goto do_submit;
+ break;
+ case FORM_INPUT_FILE:
+ if (submit)
+ goto do_submit;
+ if (fi->readonly)
+ /* FIXME: gettextize? */
+ disp_message_nsec("Read only field!", FALSE, 1, TRUE, FALSE);
+ /* FIXME: gettextize? */
+ p = inputFilenameHist("Filename:", fi->value ? fi->value->ptr : NULL,
+ NULL);
+ if (p == NULL || fi->readonly)
+ break;
+ fi->value = Strnew_charp(p);
+ formUpdateBuffer(a, Currentbuf, fi);
+ if (fi->accept || fi->parent->nitems == 1)
+ goto do_submit;
+ break;
+ case FORM_INPUT_PASSWORD:
+ if (submit)
+ goto do_submit;
+ if (fi->readonly) {
+ /* FIXME: gettextize? */
+ disp_message_nsec("Read only field!", FALSE, 1, TRUE, FALSE);
+ break;
+ }
+ /* FIXME: gettextize? */
+ p = inputLine("Password:", fi->value ? fi->value->ptr : NULL,
+ IN_PASSWORD);
+ if (p == NULL)
+ break;
+ fi->value = Strnew_charp(p);
+ formUpdateBuffer(a, Currentbuf, fi);
+ if (fi->accept)
+ goto do_submit;
+ break;
+ case FORM_TEXTAREA:
+ if (submit)
+ goto do_submit;
+ if (fi->readonly)
+ /* FIXME: gettextize? */
+ disp_message_nsec("Read only field!", FALSE, 1, TRUE, FALSE);
+ input_textarea(fi);
+ formUpdateBuffer(a, Currentbuf, fi);
+ break;
+ case FORM_INPUT_RADIO:
+ if (submit)
+ goto do_submit;
+ if (fi->readonly) {
+ /* FIXME: gettextize? */
+ disp_message_nsec("Read only field!", FALSE, 1, TRUE, FALSE);
+ break;
+ }
+ formRecheckRadio(a, Currentbuf, fi);
+ break;
+ case FORM_INPUT_CHECKBOX:
+ if (submit)
+ goto do_submit;
+ if (fi->readonly) {
+ /* FIXME: gettextize? */
+ disp_message_nsec("Read only field!", FALSE, 1, TRUE, FALSE);
+ break;
+ }
+ fi->checked = !fi->checked;
+ formUpdateBuffer(a, Currentbuf, fi);
+ break;
+#ifdef MENU_SELECT
+ case FORM_SELECT:
+ if (submit)
+ goto do_submit;
+ if (!formChooseOptionByMenu(fi,
+ Currentbuf->cursorX - Currentbuf->pos +
+ a->start.pos + Currentbuf->rootX,
+ Currentbuf->cursorY + Currentbuf->rootY))
+ break;
+ formUpdateBuffer(a, Currentbuf, fi);
+ if (fi->parent->nitems == 1)
+ goto do_submit;
+ break;
+#endif /* MENU_SELECT */
+ case FORM_INPUT_IMAGE:
+ case FORM_INPUT_SUBMIT:
+ case FORM_INPUT_BUTTON:
+ do_submit:
+ tmp = Strnew();
+ tmp2 = Strnew();
+ multipart = (fi->parent->method == FORM_METHOD_POST &&
+ fi->parent->enctype == FORM_ENCTYPE_MULTIPART);
+ query_from_followform(&tmp, fi, multipart);
+
+ tmp2 = Strdup(fi->parent->action);
+ if (!Strcmp_charp(tmp2, "!CURRENT_URL!")) {
+ /* It means "current URL" */
+ tmp2 = parsedURL2Str(&Currentbuf->currentURL);
+ if ((p = strchr(tmp2->ptr, '?')) != NULL)
+ Strshrink(tmp2, (tmp2->ptr + tmp2->length) - p);
+ }
+
+ if (fi->parent->method == FORM_METHOD_GET) {
+ if ((p = strchr(tmp2->ptr, '?')) != NULL)
+ Strshrink(tmp2, (tmp2->ptr + tmp2->length) - p);
+ Strcat_charp(tmp2, "?");
+ Strcat(tmp2, tmp);
+ loadLink(tmp2->ptr, a->target, NULL, NULL);
+ }
+ else if (fi->parent->method == FORM_METHOD_POST) {
+ Buffer *buf;
+ if (multipart) {
+ struct stat st;
+ stat(fi->parent->body, &st);
+ fi->parent->length = st.st_size;
+ }
+ else {
+ fi->parent->body = tmp->ptr;
+ fi->parent->length = tmp->length;
+ }
+ buf = loadLink(tmp2->ptr, a->target, NULL, fi->parent);
+ if (multipart) {
+ unlink(fi->parent->body);
+ }
+ if (buf && !(buf->bufferprop & BP_REDIRECTED)) { /* buf must be Currentbuf */
+ /* BP_REDIRECTED means that the buffer is obtained through
+ * Location: header. In this case, buf->form_submit must not be set
+ * because the page is not loaded by POST method but GET method.
+ */
+ buf->form_submit = save_submit_formlist(fi);
+ }
+ }
+ else if ((fi->parent->method == FORM_METHOD_INTERNAL && (!Strcmp_charp(fi->parent->action, "map") || !Strcmp_charp(fi->parent->action, "none"))) || Currentbuf->bufferprop & BP_INTERNAL) { /* internal */
+ do_internal(tmp2->ptr, tmp->ptr);
+ }
+ else {
+ disp_err_message("Can't send form because of illegal method.",
+ FALSE);
+ }
+ break;
+ case FORM_INPUT_RESET:
+ for (i = 0; i < Currentbuf->formitem->nanchor; i++) {
+ a2 = &Currentbuf->formitem->anchors[i];
+ f2 = (FormItemList *)a2->url;
+ if (f2->parent == fi->parent &&
+ f2->name && f2->value &&
+ f2->type != FORM_INPUT_SUBMIT &&
+ f2->type != FORM_INPUT_HIDDEN &&
+ f2->type != FORM_INPUT_RESET) {
+ f2->value = f2->init_value;
+ f2->checked = f2->init_checked;
+#ifdef MENU_SELECT
+ f2->label = f2->init_label;
+ f2->selected = f2->init_selected;
+#endif /* MENU_SELECT */
+ formUpdateBuffer(a2, Currentbuf, f2);
+ }
+ }
+ break;
+ case FORM_INPUT_HIDDEN:
+ default:
+ break;
+ }
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+}
+
+/* go to the top anchor */
+DEFUN(topA, LINK_BEGIN, "Go to the first link")
+{
+ HmarkerList *hl = Currentbuf->hmarklist;
+ BufferPoint *po;
+ Anchor *an;
+ int hseq = 0;
+
+ if (Currentbuf->firstLine == NULL)
+ return;
+ if (!hl || hl->nmark == 0)
+ return;
+
+ if (prec_num > hl->nmark)
+ hseq = hl->nmark - 1;
+ else if (prec_num > 0)
+ hseq = prec_num - 1;
+ do {
+ if (hseq >= hl->nmark)
+ return;
+ po = hl->marks + hseq;
+ an = retrieveAnchor(Currentbuf->href, po->line, po->pos);
+ if (an == NULL)
+ an = retrieveAnchor(Currentbuf->formitem, po->line, po->pos);
+ hseq++;
+ } while (an == NULL);
+
+ gotoLine(Currentbuf, po->line);
+ Currentbuf->pos = po->pos;
+ arrangeCursor(Currentbuf);
+ displayBuffer(Currentbuf, B_NORMAL);
+}
+
+/* go to the last anchor */
+DEFUN(lastA, LINK_END, "Go to the last link")
+{
+ HmarkerList *hl = Currentbuf->hmarklist;
+ BufferPoint *po;
+ Anchor *an;
+ int hseq;
+
+ if (Currentbuf->firstLine == NULL)
+ return;
+ if (!hl || hl->nmark == 0)
+ return;
+
+ if (prec_num >= hl->nmark)
+ hseq = 0;
+ else if (prec_num > 0)
+ hseq = hl->nmark - prec_num;
+ else
+ hseq = hl->nmark - 1;
+ do {
+ if (hseq < 0)
+ return;
+ po = hl->marks + hseq;
+ an = retrieveAnchor(Currentbuf->href, po->line, po->pos);
+ if (an == NULL)
+ an = retrieveAnchor(Currentbuf->formitem, po->line, po->pos);
+ hseq--;
+ } while (an == NULL);
+
+ gotoLine(Currentbuf, po->line);
+ Currentbuf->pos = po->pos;
+ arrangeCursor(Currentbuf);
+ displayBuffer(Currentbuf, B_NORMAL);
+}
+
+/* go to the next anchor */
+DEFUN(nextA, NEXT_LINK, "Move to next link")
+{
+ _nextA(FALSE);
+}
+
+/* go to the previous anchor */
+DEFUN(prevA, PREV_LINK, "Move to previous link")
+{
+ _prevA(FALSE);
+}
+
+/* go to the next visited anchor */
+DEFUN(nextVA, NEXT_VISITED, "Move to next visited link")
+{
+ _nextA(TRUE);
+}
+
+/* go to the previous visited anchor */
+DEFUN(prevVA, PREV_VISITED, "Move to previous visited link")
+{
+ _prevA(TRUE);
+}
+
+/* go to the next [visited] anchor */
+static void
+_nextA(int visited)
+{
+ HmarkerList *hl = Currentbuf->hmarklist;
+ BufferPoint *po;
+ Anchor *an, *pan;
+ int i, x, y, n = searchKeyNum();
+ ParsedURL url;
+
+ if (Currentbuf->firstLine == NULL)
+ return;
+ if (!hl || hl->nmark == 0)
+ return;
+
+ an = retrieveCurrentAnchor(Currentbuf);
+ if (visited != TRUE && an == NULL)
+ an = retrieveCurrentForm(Currentbuf);
+
+ y = Currentbuf->currentLine->linenumber;
+ x = Currentbuf->pos;
+
+ if (visited == TRUE) {
+ n = hl->nmark;
+ }
+
+ for (i = 0; i < n; i++) {
+ pan = an;
+ if (an && an->hseq >= 0) {
+ int hseq = an->hseq + 1;
+ do {
+ if (hseq >= hl->nmark) {
+ if (visited == TRUE)
+ return;
+ an = pan;
+ goto _end;
+ }
+ po = &hl->marks[hseq];
+ an = retrieveAnchor(Currentbuf->href, po->line, po->pos);
+ if (visited != TRUE && an == NULL)
+ an = retrieveAnchor(Currentbuf->formitem, po->line,
+ po->pos);
+ hseq++;
+ if (visited == TRUE && an) {
+ parseURL2(an->url, &url, baseURL(Currentbuf));
+ if (getHashHist(URLHist, parsedURL2Str(&url)->ptr)) {
+ goto _end;
+ }
+ }
+ } while (an == NULL || an == pan);
+ }
+ else {
+ an = closest_next_anchor(Currentbuf->href, NULL, x, y);
+ if (visited != TRUE)
+ an = closest_next_anchor(Currentbuf->formitem, an, x, y);
+ if (an == NULL) {
+ if (visited == TRUE)
+ return;
+ an = pan;
+ break;
+ }
+ x = an->start.pos;
+ y = an->start.line;
+ if (visited == TRUE) {
+ parseURL2(an->url, &url, baseURL(Currentbuf));
+ if (getHashHist(URLHist, parsedURL2Str(&url)->ptr)) {
+ goto _end;
+ }
+ }
+ }
+ }
+ if (visited == TRUE)
+ return;
+
+ _end:
+ if (an == NULL || an->hseq < 0)
+ return;
+ po = &hl->marks[an->hseq];
+ gotoLine(Currentbuf, po->line);
+ Currentbuf->pos = po->pos;
+ arrangeCursor(Currentbuf);
+ displayBuffer(Currentbuf, B_NORMAL);
+}
+
+/* go to the previous anchor */
+static void
+_prevA(int visited)
+{
+ HmarkerList *hl = Currentbuf->hmarklist;
+ BufferPoint *po;
+ Anchor *an, *pan;
+ int i, x, y, n = searchKeyNum();
+ ParsedURL url;
+
+ if (Currentbuf->firstLine == NULL)
+ return;
+ if (!hl || hl->nmark == 0)
+ return;
+
+ an = retrieveCurrentAnchor(Currentbuf);
+ if (visited != TRUE && an == NULL)
+ an = retrieveCurrentForm(Currentbuf);
+
+ y = Currentbuf->currentLine->linenumber;
+ x = Currentbuf->pos;
+
+ if (visited == TRUE) {
+ n = hl->nmark;
+ }
+
+ for (i = 0; i < n; i++) {
+ pan = an;
+ if (an && an->hseq >= 0) {
+ int hseq = an->hseq - 1;
+ do {
+ if (hseq < 0) {
+ if (visited == TRUE)
+ return;
+ an = pan;
+ goto _end;
+ }
+ po = hl->marks + hseq;
+ an = retrieveAnchor(Currentbuf->href, po->line, po->pos);
+ if (visited != TRUE && an == NULL)
+ an = retrieveAnchor(Currentbuf->formitem, po->line,
+ po->pos);
+ hseq--;
+ if (visited == TRUE && an) {
+ parseURL2(an->url, &url, baseURL(Currentbuf));
+ if (getHashHist(URLHist, parsedURL2Str(&url)->ptr)) {
+ goto _end;
+ }
+ }
+ } while (an == NULL || an == pan);
+ }
+ else {
+ an = closest_prev_anchor(Currentbuf->href, NULL, x, y);
+ if (visited != TRUE)
+ an = closest_prev_anchor(Currentbuf->formitem, an, x, y);
+ if (an == NULL) {
+ if (visited == TRUE)
+ return;
+ an = pan;
+ break;
+ }
+ x = an->start.pos;
+ y = an->start.line;
+ if (visited == TRUE && an) {
+ parseURL2(an->url, &url, baseURL(Currentbuf));
+ if (getHashHist(URLHist, parsedURL2Str(&url)->ptr)) {
+ goto _end;
+ }
+ }
+ }
+ }
+ if (visited == TRUE)
+ return;
+
+ _end:
+ if (an == NULL || an->hseq < 0)
+ return;
+ po = hl->marks + an->hseq;
+ gotoLine(Currentbuf, po->line);
+ Currentbuf->pos = po->pos;
+ arrangeCursor(Currentbuf);
+ displayBuffer(Currentbuf, B_NORMAL);
+}
+
+/* go to the next left/right anchor */
+static void
+nextX(int d, int dy)
+{
+ HmarkerList *hl = Currentbuf->hmarklist;
+ Anchor *an, *pan;
+ Line *l;
+ int i, x, y, n = searchKeyNum();
+
+ if (Currentbuf->firstLine == NULL)
+ return;
+ if (!hl || hl->nmark == 0)
+ return;
+
+ an = retrieveCurrentAnchor(Currentbuf);
+ if (an == NULL)
+ an = retrieveCurrentForm(Currentbuf);
+
+ l = Currentbuf->currentLine;
+ x = Currentbuf->pos;
+ y = l->linenumber;
+ pan = NULL;
+ for (i = 0; i < n; i++) {
+ if (an)
+ x = (d > 0) ? an->end.pos : an->start.pos - 1;
+ an = NULL;
+ while (1) {
+ for (; x >= 0 && x < l->len; x += d) {
+ an = retrieveAnchor(Currentbuf->href, y, x);
+ if (!an)
+ an = retrieveAnchor(Currentbuf->formitem, y, x);
+ if (an) {
+ pan = an;
+ break;
+ }
+ }
+ if (!dy || an)
+ break;
+ l = (dy > 0) ? l->next : l->prev;
+ if (!l)
+ break;
+ x = (d > 0) ? 0 : l->len - 1;
+ y = l->linenumber;
+ }
+ if (!an)
+ break;
+ }
+
+ if (pan == NULL)
+ return;
+ gotoLine(Currentbuf, y);
+ Currentbuf->pos = pan->start.pos;
+ arrangeCursor(Currentbuf);
+ displayBuffer(Currentbuf, B_NORMAL);
+}
+
+/* go to the next downward/upward anchor */
+static void
+nextY(int d)
+{
+ HmarkerList *hl = Currentbuf->hmarklist;
+ Anchor *an, *pan;
+ int i, x, y, n = searchKeyNum();
+ int hseq;
+
+ if (Currentbuf->firstLine == NULL)
+ return;
+ if (!hl || hl->nmark == 0)
+ return;
+
+ an = retrieveCurrentAnchor(Currentbuf);
+ if (an == NULL)
+ an = retrieveCurrentForm(Currentbuf);
+
+ x = Currentbuf->pos;
+ y = Currentbuf->currentLine->linenumber + d;
+ pan = NULL;
+ hseq = -1;
+ for (i = 0; i < n; i++) {
+ if (an)
+ hseq = abs(an->hseq);
+ an = NULL;
+ for (; y >= 0 && y <= Currentbuf->lastLine->linenumber; y += d) {
+ an = retrieveAnchor(Currentbuf->href, y, x);
+ if (!an)
+ an = retrieveAnchor(Currentbuf->formitem, y, x);
+ if (an && hseq != abs(an->hseq)) {
+ pan = an;
+ break;
+ }
+ }
+ if (!an)
+ break;
+ }
+
+ if (pan == NULL)
+ return;
+ gotoLine(Currentbuf, pan->start.line);
+ arrangeLine(Currentbuf);
+ displayBuffer(Currentbuf, B_NORMAL);
+}
+
+/* go to the next left anchor */
+DEFUN(nextL, NEXT_LEFT, "Move to next left link")
+{
+ nextX(-1, 0);
+}
+
+/* go to the next left-up anchor */
+DEFUN(nextLU, NEXT_LEFT_UP, "Move to next left (or upward) link")
+{
+ nextX(-1, -1);
+}
+
+/* go to the next right anchor */
+DEFUN(nextR, NEXT_RIGHT, "Move to next right link")
+{
+ nextX(1, 0);
+}
+
+/* go to the next right-down anchor */
+DEFUN(nextRD, NEXT_RIGHT_DOWN, "Move to next right (or downward) link")
+{
+ nextX(1, 1);
+}
+
+/* go to the next downward anchor */
+DEFUN(nextD, NEXT_DOWN, "Move to next downward link")
+{
+ nextY(1);
+}
+
+/* go to the next upward anchor */
+DEFUN(nextU, NEXT_UP, "Move to next upward link")
+{
+ nextY(-1);
+}
+
+/* go to the next bufferr */
+DEFUN(nextBf, NEXT, "Move to next buffer")
+{
+ Buffer *buf;
+ int i;
+
+ for (i = 0; i < PREC_NUM; i++) {
+ buf = prevBuffer(Firstbuf, Currentbuf);
+ if (!buf) {
+ if (i == 0)
+ return;
+ break;
+ }
+ Currentbuf = buf;
+ }
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+}
+
+/* go to the previous bufferr */
+DEFUN(prevBf, PREV, "Move to previous buffer")
+{
+ Buffer *buf;
+ int i;
+
+ for (i = 0; i < PREC_NUM; i++) {
+ buf = Currentbuf->nextBuffer;
+ if (!buf) {
+ if (i == 0)
+ return;
+ break;
+ }
+ Currentbuf = buf;
+ }
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+}
+
+static int
+checkBackBuffer(Buffer *buf)
+{
+ Buffer *fbuf = buf->linkBuffer[LB_N_FRAME];
+
+ if (fbuf) {
+ if (fbuf->frameQ)
+ return TRUE; /* Currentbuf has stacked frames */
+ /* when no frames stacked and next is frame source, try next's
+ * nextBuffer */
+ if (RenderFrame && fbuf == buf->nextBuffer) {
+ if (fbuf->nextBuffer != NULL)
+ return TRUE;
+ else
+ return FALSE;
+ }
+ }
+
+ if (buf->nextBuffer)
+ return TRUE;
+
+ return FALSE;
+}
+
+/* delete current buffer and back to the previous buffer */
+DEFUN(backBf, BACK, "Back to previous buffer")
+{
+ Buffer *buf = Currentbuf->linkBuffer[LB_N_FRAME];
+
+ if (!checkBackBuffer(Currentbuf)) {
+ if (close_tab_back && nTab >= 1) {
+ deleteTab(CurrentTab);
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+ }
+ else
+ /* FIXME: gettextize? */
+ disp_message("Can't back...", TRUE);
+ return;
+ }
+
+ delBuffer(Currentbuf);
+
+ if (buf) {
+ if (buf->frameQ) {
+ struct frameset *fs;
+ long linenumber = buf->frameQ->linenumber;
+ long top = buf->frameQ->top_linenumber;
+ int pos = buf->frameQ->pos;
+ int currentColumn = buf->frameQ->currentColumn;
+ AnchorList *formitem = buf->frameQ->formitem;
+
+ fs = popFrameTree(&(buf->frameQ));
+ deleteFrameSet(buf->frameset);
+ buf->frameset = fs;
+
+ if (buf == Currentbuf) {
+ rFrame();
+ Currentbuf->topLine = lineSkip(Currentbuf,
+ Currentbuf->firstLine, top - 1,
+ FALSE);
+ gotoLine(Currentbuf, linenumber);
+ Currentbuf->pos = pos;
+ Currentbuf->currentColumn = currentColumn;
+ arrangeCursor(Currentbuf);
+ formResetBuffer(Currentbuf, formitem);
+ }
+ }
+ else if (RenderFrame && buf == Currentbuf) {
+ delBuffer(Currentbuf);
+ }
+ }
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+}
+
+DEFUN(deletePrevBuf, DELETE_PREVBUF,
+ "Delete previous buffer (mainly for local-CGI)")
+{
+ Buffer *buf = Currentbuf->nextBuffer;
+ if (buf)
+ delBuffer(buf);
+}
+
+static void
+cmd_loadURL(char *url, ParsedURL *current, char *referer, FormList *request)
+{
+ Buffer *buf;
+
+ if (!strncasecmp(url, "mailto:", 7)
+#ifdef USE_W3MMAILER
+ && non_null(Mailer) && strchr(url, '?') == NULL
+#endif
+ ) {
+ /* invoke external mailer */
+ Str to = Strnew_charp(url + 7);
+#ifndef USE_W3MMAILER
+ char *pos;
+ if (!non_null(Mailer)) {
+ /* FIXME: gettextize? */
+ disp_err_message("no mailer is specified", TRUE);
+ return;
+ }
+ if ((pos = strchr(to->ptr, '?')) != NULL)
+ Strtruncate(to, pos - to->ptr);
+#endif
+ fmTerm();
+ system(myExtCommand(Mailer, shell_quote(file_unquote(to->ptr)),
+ FALSE)->ptr);
+ fmInit();
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+ pushHashHist(URLHist, url);
+ return;
+ }
+#if 0
+ if (!strncasecmp(url, "news:", 5) && strchr(url, '@') == NULL) {
+ /* news:newsgroup is not supported */
+ /* FIXME: gettextize? */
+ disp_err_message("news:newsgroup_name is not supported", TRUE);
+ return;
+ }
+#endif /* USE_NNTP */
+
+ refresh();
+ buf = loadGeneralFile(url, current, referer, 0, request);
+ if (buf == NULL) {
+ /* FIXME: gettextize? */
+ char *emsg = Sprintf("Can't load %s", conv_from_system(url))->ptr;
+ disp_err_message(emsg, FALSE);
+ }
+ else if (buf != NO_BUFFER) {
+ pushBuffer(buf);
+ if (RenderFrame && Currentbuf->frameset != NULL)
+ rFrame();
+ }
+ displayBuffer(Currentbuf, B_NORMAL);
+}
+
+
+/* go to specified URL */
+static void
+goURL0(char *prompt, int relative)
+{
+ char *url, *referer;
+ ParsedURL p_url, *current;
+ Buffer *cur_buf = Currentbuf;
+
+ url = searchKeyData();
+ if (url == NULL) {
+ Hist *hist = copyHist(URLHist);
+ Anchor *a;
+
+ current = baseURL(Currentbuf);
+ if (current) {
+ char *c_url = parsedURL2Str(current)->ptr;
+ if (DefaultURLString == DEFAULT_URL_CURRENT) {
+ url = c_url;
+ if (DecodeURL)
+ url = url_unquote_conv(url, 0);
+ }
+ else
+ pushHist(hist, c_url);
+ }
+ a = retrieveCurrentAnchor(Currentbuf);
+ if (a) {
+ char *a_url;
+ parseURL2(a->url, &p_url, current);
+ a_url = parsedURL2Str(&p_url)->ptr;
+ if (DefaultURLString == DEFAULT_URL_LINK) {
+ url = a_url;
+ if (DecodeURL)
+ url = url_unquote_conv(url, Currentbuf->document_charset);
+ }
+ else
+ pushHist(hist, a_url);
+ }
+ url = inputLineHist(prompt, url, IN_URL, hist);
+ if (url != NULL)
+ SKIP_BLANKS(url);
+ }
+#ifdef USE_M17N
+ if (url != NULL) {
+ if ((relative || *url == '#') && Currentbuf->document_charset)
+ url = wc_conv_strict(url, InnerCharset,
+ Currentbuf->document_charset)->ptr;
+ else
+ url = conv_to_system(url);
+ }
+#endif
+ if (url == NULL || *url == '\0') {
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+ return;
+ }
+ if (*url == '#') {
+ gotoLabel(url + 1);
+ return;
+ }
+ if (relative) {
+ current = baseURL(Currentbuf);
+ referer = parsedURL2Str(&Currentbuf->currentURL)->ptr;
+ }
+ else {
+ current = NULL;
+ referer = NULL;
+ }
+ parseURL2(url, &p_url, current);
+ pushHashHist(URLHist, parsedURL2Str(&p_url)->ptr);
+ cmd_loadURL(url, current, referer, NULL);
+ if (Currentbuf != cur_buf) /* success */
+ pushHashHist(URLHist, parsedURL2Str(&Currentbuf->currentURL)->ptr);
+}
+
+DEFUN(goURL, GOTO, "Go to URL")
+{
+ goURL0("Goto URL: ", FALSE);
+}
+
+DEFUN(gorURL, GOTO_RELATIVE, "Go to relative URL")
+{
+ goURL0("Goto relative URL: ", TRUE);
+}
+
+static void
+cmd_loadBuffer(Buffer *buf, int prop, int linkid)
+{
+ if (buf == NULL) {
+ disp_err_message("Can't load string", FALSE);
+ }
+ else if (buf != NO_BUFFER) {
+ buf->bufferprop |= (BP_INTERNAL | prop);
+ if (!(buf->bufferprop & BP_NO_URL))
+ copyParsedURL(&buf->currentURL, &Currentbuf->currentURL);
+ if (linkid != LB_NOLINK) {
+ buf->linkBuffer[REV_LB[linkid]] = Currentbuf;
+ Currentbuf->linkBuffer[linkid] = buf;
+ }
+ pushBuffer(buf);
+ }
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+}
+
+/* load bookmark */
+DEFUN(ldBmark, BOOKMARK VIEW_BOOKMARK, "Read bookmark")
+{
+ cmd_loadURL(BookmarkFile, NULL, NO_REFERER, NULL);
+}
+
+
+/* Add current to bookmark */
+DEFUN(adBmark, ADD_BOOKMARK, "Add current page to bookmark")
+{
+ Str tmp;
+ FormList *request;
+
+ tmp = Sprintf("mode=panel&cookie=%s&bmark=%s&url=%s&title=%s",
+ (Str_form_quote(localCookie()))->ptr,
+ (Str_form_quote(Strnew_charp(BookmarkFile)))->ptr,
+ (Str_form_quote(parsedURL2Str(&Currentbuf->currentURL)))->
+ ptr,
+#ifdef USE_M17N
+#if LANG == JA
+ /* FIXME: why WC_CES_EUC_JP hardcoded?
+ * backward compatibility.
+ * w3mbookmark takes arguments as EUC-JP only?
+ */
+ (Str_form_quote(wc_conv_strict(Currentbuf->buffername,
+ InnerCharset,
+ WC_CES_EUC_JP)))->ptr);
+#else
+ (Str_form_quote(wc_conv_strict(Currentbuf->buffername,
+ InnerCharset,
+ SystemCharset)))->ptr);
+#endif
+#else
+ (Str_form_quote(Strnew_charp(Currentbuf->buffername)))->ptr);
+#endif
+ request = newFormList(NULL, "post", NULL, NULL, NULL, NULL, NULL);
+ request->body = tmp->ptr;
+ request->length = tmp->length;
+ cmd_loadURL("file:///$LIB/" W3MBOOKMARK_CMDNAME, NULL, NO_REFERER,
+ request);
+}
+
+/* option setting */
+DEFUN(ldOpt, OPTIONS, "Option setting panel")
+{
+ cmd_loadBuffer(load_option_panel(), BP_NO_URL, LB_NOLINK);
+}
+
+/* set an option */
+DEFUN(setOpt, SET_OPTION, "Set option")
+{
+ char *opt;
+
+ CurrentKeyData = NULL; /* not allowed in w3m-control: */
+ opt = searchKeyData();
+ if (opt == NULL || *opt == '\0' || strchr(opt, '=') == NULL) {
+ if (opt != NULL && *opt != '\0') {
+ char *v = get_param_option(opt);
+ opt = Sprintf("%s=%s", opt, v ? v : "")->ptr;
+ }
+ opt = inputStrHist("Set option: ", opt, TextHist);
+ if (opt == NULL || *opt == '\0') {
+ displayBuffer(Currentbuf, B_NORMAL);
+ return;
+ }
+ }
+ if (set_param_option(opt))
+ sync_with_option();
+ displayBuffer(Currentbuf, B_REDRAW_IMAGE);
+}
+
+/* error message list */
+DEFUN(msgs, MSGS, "Display error messages")
+{
+ cmd_loadBuffer(message_list_panel(), BP_NO_URL, LB_NOLINK);
+}
+
+/* page info */
+DEFUN(pginfo, INFO, "View info of current document")
+{
+ Buffer *buf;
+
+ if ((buf = Currentbuf->linkBuffer[LB_N_INFO]) != NULL) {
+ Currentbuf = buf;
+ displayBuffer(Currentbuf, B_NORMAL);
+ return;
+ }
+ if ((buf = Currentbuf->linkBuffer[LB_INFO]) != NULL)
+ delBuffer(buf);
+ buf = page_info_panel(Currentbuf);
+ cmd_loadBuffer(buf, BP_NORMAL, LB_INFO);
+}
+
+void
+follow_map(struct parsed_tagarg *arg)
+{
+ char *name = tag_get_value(arg, "link");
+#if defined(MENU_MAP) || defined(USE_IMAGE)
+ Anchor *an;
+ MapArea *a;
+ int x, y;
+ ParsedURL p_url;
+
+ an = retrieveCurrentImg(Currentbuf);
+ x = Currentbuf->cursorX + Currentbuf->rootX;
+ y = Currentbuf->cursorY + Currentbuf->rootY;
+ a = follow_map_menu(Currentbuf, name, an, x, y);
+ if (a == NULL || a->url == NULL || *(a->url) == '\0') {
+#endif
+#ifndef MENU_MAP
+ Buffer *buf = follow_map_panel(Currentbuf, name);
+
+ if (buf != NULL)
+ cmd_loadBuffer(buf, BP_NORMAL, LB_NOLINK);
+#endif
+#if defined(MENU_MAP) || defined(USE_IMAGE)
+ return;
+ }
+ if (*(a->url) == '#') {
+ gotoLabel(a->url + 1);
+ return;
+ }
+ parseURL2(a->url, &p_url, baseURL(Currentbuf));
+ pushHashHist(URLHist, parsedURL2Str(&p_url)->ptr);
+ if (check_target && open_tab_blank && a->target &&
+ (!strcasecmp(a->target, "_new") || !strcasecmp(a->target, "_blank"))) {
+ Buffer *buf;
+
+ _newT();
+ buf = Currentbuf;
+ cmd_loadURL(a->url, baseURL(Currentbuf),
+ parsedURL2Str(&Currentbuf->currentURL)->ptr, NULL);
+ if (buf != Currentbuf)
+ delBuffer(buf);
+ else
+ deleteTab(CurrentTab);
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+ return;
+ }
+ cmd_loadURL(a->url, baseURL(Currentbuf),
+ parsedURL2Str(&Currentbuf->currentURL)->ptr, NULL);
+#endif
+}
+
+#ifdef USE_MENU
+/* link menu */
+DEFUN(linkMn, LINK_MENU, "Popup link element menu")
+{
+ LinkList *l = link_menu(Currentbuf);
+ ParsedURL p_url;
+
+ if (!l || !l->url)
+ return;
+ if (*(l->url) == '#') {
+ gotoLabel(l->url + 1);
+ return;
+ }
+ parseURL2(l->url, &p_url, baseURL(Currentbuf));
+ pushHashHist(URLHist, parsedURL2Str(&p_url)->ptr);
+ cmd_loadURL(l->url, baseURL(Currentbuf),
+ parsedURL2Str(&Currentbuf->currentURL)->ptr, NULL);
+}
+
+static void
+anchorMn(Anchor *(*menu_func) (Buffer *), int go)
+{
+ Anchor *a;
+ BufferPoint *po;
+
+ if (!Currentbuf->href || !Currentbuf->hmarklist)
+ return;
+ a = menu_func(Currentbuf);
+ if (!a || a->hseq < 0)
+ return;
+ po = &Currentbuf->hmarklist->marks[a->hseq];
+ gotoLine(Currentbuf, po->line);
+ Currentbuf->pos = po->pos;
+ arrangeCursor(Currentbuf);
+ displayBuffer(Currentbuf, B_NORMAL);
+ if (go)
+ followA();
+}
+
+/* accesskey */
+DEFUN(accessKey, ACCESSKEY, "Popup acceskey menu")
+{
+ anchorMn(accesskey_menu, TRUE);
+}
+
+/* list menu */
+DEFUN(listMn, LIST_MENU, "Popup link list menu and go to selected link")
+{
+ anchorMn(list_menu, TRUE);
+}
+
+DEFUN(movlistMn, MOVE_LIST_MENU,
+ "Popup link list menu and move cursor to selected link")
+{
+ anchorMn(list_menu, FALSE);
+}
+#endif
+
+/* link,anchor,image list */
+DEFUN(linkLst, LIST, "Show all links and images")
+{
+ Buffer *buf;
+
+ buf = link_list_panel(Currentbuf);
+ if (buf != NULL) {
+#ifdef USE_M17N
+ buf->document_charset = Currentbuf->document_charset;
+#endif
+ cmd_loadBuffer(buf, BP_NORMAL, LB_NOLINK);
+ }
+}
+
+#ifdef USE_COOKIE
+/* cookie list */
+DEFUN(cooLst, COOKIE, "View cookie list")
+{
+ Buffer *buf;
+
+ buf = cookie_list_panel();
+ if (buf != NULL)
+ cmd_loadBuffer(buf, BP_NO_URL, LB_NOLINK);
+}
+#endif /* USE_COOKIE */
+
+#ifdef USE_HISTORY
+/* History page */
+DEFUN(ldHist, HISTORY, "View history of URL")
+{
+ cmd_loadBuffer(historyBuffer(URLHist), BP_NO_URL, LB_NOLINK);
+}
+#endif /* USE_HISTORY */
+
+/* download HREF link */
+DEFUN(svA, SAVE_LINK, "Save link to file")
+{
+ CurrentKeyData = NULL; /* not allowed in w3m-control: */
+ do_download = TRUE;
+ followA();
+ do_download = FALSE;
+}
+
+/* download IMG link */
+DEFUN(svI, SAVE_IMAGE, "Save image to file")
+{
+ CurrentKeyData = NULL; /* not allowed in w3m-control: */
+ do_download = TRUE;
+ followI();
+ do_download = FALSE;
+}
+
+/* save buffer */
+DEFUN(svBuf, PRINT SAVE_SCREEN, "Save rendered document to file")
+{
+ char *qfile = NULL, *file;
+ FILE *f;
+ int is_pipe;
+
+ CurrentKeyData = NULL; /* not allowed in w3m-control: */
+ file = searchKeyData();
+ if (file == NULL || *file == '\0') {
+ /* FIXME: gettextize? */
+ qfile = inputLineHist("Save buffer to: ", NULL, IN_COMMAND, SaveHist);
+ if (qfile == NULL || *qfile == '\0') {
+ displayBuffer(Currentbuf, B_NORMAL);
+ return;
+ }
+ }
+ file = conv_to_system(qfile ? qfile : file);
+ if (*file == '|') {
+ is_pipe = TRUE;
+ f = popen(file + 1, "w");
+ }
+ else {
+ if (qfile) {
+ file = unescape_spaces(Strnew_charp(qfile))->ptr;
+ file = conv_to_system(file);
+ }
+ file = expandPath(file);
+ if (checkOverWrite(file) < 0) {
+ displayBuffer(Currentbuf, B_NORMAL);
+ return;
+ }
+ f = fopen(file, "w");
+ is_pipe = FALSE;
+ }
+ if (f == NULL) {
+ /* FIXME: gettextize? */
+ char *emsg = Sprintf("Can't open %s", conv_from_system(file))->ptr;
+ disp_err_message(emsg, TRUE);
+ return;
+ }
+ saveBuffer(Currentbuf, f, TRUE);
+ if (is_pipe)
+ pclose(f);
+ else
+ fclose(f);
+ displayBuffer(Currentbuf, B_NORMAL);
+}
+
+/* save source */
+DEFUN(svSrc, DOWNLOAD SAVE, "Save document source to file")
+{
+ char *file;
+
+ if (Currentbuf->sourcefile == NULL)
+ return;
+ CurrentKeyData = NULL; /* not allowed in w3m-control: */
+ PermitSaveToPipe = TRUE;
+ if (Currentbuf->real_scheme == SCM_LOCAL)
+ file = conv_from_system(guess_save_name(NULL,
+ Currentbuf->currentURL.
+ real_file));
+ else
+ file = guess_save_name(Currentbuf, Currentbuf->currentURL.file);
+ doFileCopy(Currentbuf->sourcefile, file);
+ PermitSaveToPipe = FALSE;
+ displayBuffer(Currentbuf, B_NORMAL);
+}
+
+static void
+_peekURL(int only_img)
+{
+
+ Anchor *a;
+ ParsedURL pu;
+ static Str s = NULL;
+#ifdef USE_M17N
+ static Lineprop *p = NULL;
+ Lineprop *pp;
+#endif
+ static int offset = 0, n;
+
+ if (Currentbuf->firstLine == NULL)
+ return;
+ if (CurrentKey == prev_key && s != NULL) {
+ if (s->length - offset >= COLS)
+ offset++;
+ else if (s->length <= offset) /* bug ? */
+ offset = 0;
+ goto disp;
+ }
+ else {
+ offset = 0;
+ }
+ s = NULL;
+ a = (only_img ? NULL : retrieveCurrentAnchor(Currentbuf));
+ if (a == NULL) {
+ a = (only_img ? NULL : retrieveCurrentForm(Currentbuf));
+ if (a == NULL) {
+ a = retrieveCurrentImg(Currentbuf);
+ if (a == NULL)
+ return;
+ }
+ else
+ s = Strnew_charp(form2str((FormItemList *)a->url));
+ }
+ if (s == NULL) {
+ parseURL2(a->url, &pu, baseURL(Currentbuf));
+ s = parsedURL2Str(&pu);
+ }
+ if (DecodeURL)
+ s = Strnew_charp(url_unquote_conv
+ (s->ptr, Currentbuf->document_charset));
+#ifdef USE_M17N
+ s = checkType(s, &pp, NULL);
+ p = NewAtom_N(Lineprop, s->length);
+ bcopy((void *)pp, (void *)p, s->length * sizeof(Lineprop));
+#endif
+ disp:
+ n = searchKeyNum();
+ if (n > 1 && s->length > (n - 1) * (COLS - 1))
+ offset = (n - 1) * (COLS - 1);
+#ifdef USE_M17N
+ while (offset < s->length && p[offset] & PC_WCHAR2)
+ offset++;
+#endif
+ disp_message_nomouse(&s->ptr[offset], TRUE);
+}
+
+/* peek URL */
+DEFUN(peekURL, PEEK_LINK, "Peek link URL")
+{
+ _peekURL(0);
+}
+
+/* peek URL of image */
+DEFUN(peekIMG, PEEK_IMG, "Peek image URL")
+{
+ _peekURL(1);
+}
+
+/* show current URL */
+static Str
+currentURL(void)
+{
+ if (Currentbuf->bufferprop & BP_INTERNAL)
+ return Strnew_size(0);
+ return parsedURL2Str(&Currentbuf->currentURL);
+}
+
+DEFUN(curURL, PEEK, "Peek current URL")
+{
+ static Str s = NULL;
+#ifdef USE_M17N
+ static Lineprop *p = NULL;
+ Lineprop *pp;
+#endif
+ static int offset = 0, n;
+
+ if (Currentbuf->bufferprop & BP_INTERNAL)
+ return;
+ if (CurrentKey == prev_key && s != NULL) {
+ if (s->length - offset >= COLS)
+ offset++;
+ else if (s->length <= offset) /* bug ? */
+ offset = 0;
+ }
+ else {
+ offset = 0;
+ s = currentURL();
+ if (DecodeURL)
+ s = Strnew_charp(url_unquote_conv(s->ptr, 0));
+#ifdef USE_M17N
+ s = checkType(s, &pp, NULL);
+ p = NewAtom_N(Lineprop, s->length);
+ bcopy((void *)pp, (void *)p, s->length * sizeof(Lineprop));
+#endif
+ }
+ n = searchKeyNum();
+ if (n > 1 && s->length > (n - 1) * (COLS - 1))
+ offset = (n - 1) * (COLS - 1);
+#ifdef USE_M17N
+ while (offset < s->length && p[offset] & PC_WCHAR2)
+ offset++;
+#endif
+ disp_message_nomouse(&s->ptr[offset], TRUE);
+}
+/* view HTML source */
+
+DEFUN(vwSrc, SOURCE VIEW, "View HTML source")
+{
+ Buffer *buf;
+
+ if (Currentbuf->type == NULL || Currentbuf->bufferprop & BP_FRAME)
+ return;
+ if ((buf = Currentbuf->linkBuffer[LB_SOURCE]) != NULL ||
+ (buf = Currentbuf->linkBuffer[LB_N_SOURCE]) != NULL) {
+ Currentbuf = buf;
+ displayBuffer(Currentbuf, B_NORMAL);
+ return;
+ }
+ if (Currentbuf->sourcefile == NULL) {
+ if (Currentbuf->pagerSource &&
+ !strcasecmp(Currentbuf->type, "text/plain")) {
+#ifdef USE_M17N
+ wc_ces old_charset;
+ wc_bool old_fix_width_conv;
+#endif
+ FILE *f;
+ Str tmpf = tmpfname(TMPF_SRC, NULL);
+ f = fopen(tmpf->ptr, "w");
+ if (f == NULL)
+ return;
+#ifdef USE_M17N
+ old_charset = DisplayCharset;
+ old_fix_width_conv = WcOption.fix_width_conv;
+ DisplayCharset = (Currentbuf->document_charset != WC_CES_US_ASCII)
+ ? Currentbuf->document_charset : 0;
+ WcOption.fix_width_conv = WC_FALSE;
+#endif
+ saveBufferBody(Currentbuf, f, TRUE);
+#ifdef USE_M17N
+ DisplayCharset = old_charset;
+ WcOption.fix_width_conv = old_fix_width_conv;
+#endif
+ fclose(f);
+ Currentbuf->sourcefile = tmpf->ptr;
+ }
+ else {
+ return;
+ }
+ }
+
+ buf = newBuffer(INIT_BUFFER_WIDTH);
+
+ if (!strcasecmp(Currentbuf->type, "text/html")) {
+ buf->type = "text/plain";
+ if (Currentbuf->real_type &&
+ !strcasecmp(Currentbuf->real_type, "text/html"))
+ buf->real_type = "text/plain";
+ else
+ buf->real_type = Currentbuf->real_type;
+ buf->buffername = Sprintf("source of %s", Currentbuf->buffername)->ptr;
+ buf->linkBuffer[LB_N_SOURCE] = Currentbuf;
+ Currentbuf->linkBuffer[LB_SOURCE] = buf;
+ }
+ else if (!strcasecmp(Currentbuf->type, "text/plain")) {
+ buf->type = "text/html";
+ if (Currentbuf->real_type &&
+ !strcasecmp(Currentbuf->real_type, "text/plain"))
+ buf->real_type = "text/html";
+ else
+ buf->real_type = Currentbuf->real_type;
+ buf->buffername = Sprintf("HTML view of %s",
+ Currentbuf->buffername)->ptr;
+ buf->linkBuffer[LB_SOURCE] = Currentbuf;
+ Currentbuf->linkBuffer[LB_N_SOURCE] = buf;
+ }
+ else {
+ return;
+ }
+ buf->currentURL = Currentbuf->currentURL;
+ buf->real_scheme = Currentbuf->real_scheme;
+ buf->filename = Currentbuf->filename;
+ buf->sourcefile = Currentbuf->sourcefile;
+ buf->header_source = Currentbuf->header_source;
+ buf->search_header = Currentbuf->search_header;
+#ifdef USE_M17N
+ buf->document_charset = Currentbuf->document_charset;
+#endif
+ buf->clone = Currentbuf->clone;
+ (*buf->clone)++;
+
+ buf->need_reshape = TRUE;
+ reshapeBuffer(buf);
+ pushBuffer(buf);
+ displayBuffer(Currentbuf, B_NORMAL);
+}
+
+/* reload */
+DEFUN(reload, RELOAD, "Reload buffer")
+{
+ Buffer *buf, *fbuf = NULL, sbuf;
+#ifdef USE_M17N
+ wc_ces old_charset;
+#endif
+ Str url;
+ FormList *request;
+ int multipart;
+
+ if (Currentbuf->bufferprop & BP_INTERNAL) {
+ if (!strcmp(Currentbuf->buffername, DOWNLOAD_LIST_TITLE)) {
+ ldDL();
+ return;
+ }
+ /* FIXME: gettextize? */
+ disp_err_message("Can't reload...", TRUE);
+ return;
+ }
+ if (Currentbuf->currentURL.scheme == SCM_LOCAL &&
+ !strcmp(Currentbuf->currentURL.file, "-")) {
+ /* file is std input */
+ /* FIXME: gettextize? */
+ disp_err_message("Can't reload stdin", TRUE);
+ return;
+ }
+ copyBuffer(&sbuf, Currentbuf);
+ if (Currentbuf->bufferprop & BP_FRAME &&
+ (fbuf = Currentbuf->linkBuffer[LB_N_FRAME])) {
+ if (fmInitialized) {
+ message("Rendering frame", 0, 0);
+ refresh();
+ }
+ if (!(buf = renderFrame(fbuf, 1))) {
+ displayBuffer(Currentbuf, B_NORMAL);
+ return;
+ }
+ if (fbuf->linkBuffer[LB_FRAME]) {
+ if (buf->sourcefile &&
+ fbuf->linkBuffer[LB_FRAME]->sourcefile &&
+ !strcmp(buf->sourcefile,
+ fbuf->linkBuffer[LB_FRAME]->sourcefile))
+ fbuf->linkBuffer[LB_FRAME]->sourcefile = NULL;
+ delBuffer(fbuf->linkBuffer[LB_FRAME]);
+ }
+ fbuf->linkBuffer[LB_FRAME] = buf;
+ buf->linkBuffer[LB_N_FRAME] = fbuf;
+ pushBuffer(buf);
+ Currentbuf = buf;
+ if (Currentbuf->firstLine)
+ restorePosition(Currentbuf, &sbuf);
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+ return;
+ }
+ else if (Currentbuf->frameset != NULL)
+ fbuf = Currentbuf->linkBuffer[LB_FRAME];
+ multipart = 0;
+ if (Currentbuf->form_submit) {
+ request = Currentbuf->form_submit->parent;
+ if (request->method == FORM_METHOD_POST
+ && request->enctype == FORM_ENCTYPE_MULTIPART) {
+ Str query;
+ struct stat st;
+ multipart = 1;
+ query_from_followform(&query, Currentbuf->form_submit, multipart);
+ stat(request->body, &st);
+ request->length = st.st_size;
+ }
+ }
+ else {
+ request = NULL;
+ }
+ url = parsedURL2Str(&Currentbuf->currentURL);
+ /* FIXME: gettextize? */
+ message("Reloading...", 0, 0);
+ refresh();
+#ifdef USE_M17N
+ old_charset = DocumentCharset;
+ if (Currentbuf->document_charset != WC_CES_US_ASCII)
+ DocumentCharset = Currentbuf->document_charset;
+#endif
+ SearchHeader = Currentbuf->search_header;
+ DefaultType = Currentbuf->real_type;
+ buf = loadGeneralFile(url->ptr, NULL, NO_REFERER, RG_NOCACHE, request);
+#ifdef USE_M17N
+ DocumentCharset = old_charset;
+#endif
+ SearchHeader = FALSE;
+ DefaultType = NULL;
+
+ if (multipart)
+ unlink(request->body);
+ if (buf == NULL) {
+ /* FIXME: gettextize? */
+ disp_err_message("Can't reload...", TRUE);
+ return;
+ }
+ else if (buf == NO_BUFFER) {
+ displayBuffer(Currentbuf, B_NORMAL);
+ return;
+ }
+ if (fbuf != NULL)
+ Firstbuf = deleteBuffer(Firstbuf, fbuf);
+ repBuffer(Currentbuf, buf);
+ if ((buf->type != NULL) && (sbuf.type != NULL) &&
+ ((!strcasecmp(buf->type, "text/plain") &&
+ !strcasecmp(sbuf.type, "text/html")) ||
+ (!strcasecmp(buf->type, "text/html") &&
+ !strcasecmp(sbuf.type, "text/plain")))) {
+ vwSrc();
+ if (Currentbuf != buf)
+ Firstbuf = deleteBuffer(Firstbuf, buf);
+ }
+ Currentbuf->search_header = sbuf.search_header;
+ Currentbuf->form_submit = sbuf.form_submit;
+ if (Currentbuf->firstLine)
+ restorePosition(Currentbuf, &sbuf);
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+}
+
+/* reshape */
+DEFUN(reshape, RESHAPE, "Re-render buffer")
+{
+ Currentbuf->need_reshape = TRUE;
+ reshapeBuffer(Currentbuf);
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+}
+
+#ifdef USE_M17N
+static void
+_docCSet(wc_ces charset)
+{
+ if (Currentbuf->bufferprop & BP_INTERNAL)
+ return;
+ if (Currentbuf->sourcefile == NULL) {
+ disp_message("Can't reload...", FALSE);
+ return;
+ }
+ Currentbuf->document_charset = charset;
+ Currentbuf->need_reshape = TRUE;
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+}
+
+void
+change_charset(struct parsed_tagarg *arg)
+{
+ Buffer *buf = Currentbuf->linkBuffer[LB_N_INFO];
+ wc_ces charset;
+
+ if (buf == NULL)
+ return;
+ delBuffer(Currentbuf);
+ Currentbuf = buf;
+ if (Currentbuf->bufferprop & BP_INTERNAL)
+ return;
+ charset = Currentbuf->document_charset;
+ for (; arg; arg = arg->next) {
+ if (!strcmp(arg->arg, "charset"))
+ charset = atoi(arg->value);
+ }
+ _docCSet(charset);
+}
+
+DEFUN(docCSet, CHARSET, "Change the current document charset")
+{
+ char *cs;
+ wc_ces charset;
+
+ cs = searchKeyData();
+ if (cs == NULL || *cs == '\0')
+ /* FIXME: gettextize? */
+ cs = inputStr("Document charset: ",
+ wc_ces_to_charset(Currentbuf->document_charset));
+ charset = wc_guess_charset_short(cs, 0);
+ if (charset == 0) {
+ displayBuffer(Currentbuf, B_NORMAL);
+ return;
+ }
+ _docCSet(charset);
+}
+
+DEFUN(defCSet, DEFAULT_CHARSET, "Change the default document charset")
+{
+ char *cs;
+ wc_ces charset;
+
+ cs = searchKeyData();
+ if (cs == NULL || *cs == '\0')
+ /* FIXME: gettextize? */
+ cs = inputStr("Default document charset: ",
+ wc_ces_to_charset(DocumentCharset));
+ charset = wc_guess_charset_short(cs, 0);
+ if (charset != 0)
+ DocumentCharset = charset;
+ displayBuffer(Currentbuf, B_NORMAL);
+}
+#endif
+
+/* mark URL-like patterns as anchors */
+void
+chkURLBuffer(Buffer *buf)
+{
+ static char *url_like_pat[] = {
+ "https?://[a-zA-Z0-9][a-zA-Z0-9:%\\-\\./?=~_\\&+@#,\\$;]*[a-zA-Z0-9_/=\\-]",
+ "file:/[a-zA-Z0-9:%\\-\\./=_\\+@#,\\$;]*",
+#ifdef USE_GOPHER
+ "gopher://[a-zA-Z0-9][a-zA-Z0-9:%\\-\\./_]*",
+#endif /* USE_GOPHER */
+ "ftp://[a-zA-Z0-9][a-zA-Z0-9:%\\-\\./=_+@#,\\$]*[a-zA-Z0-9_/]",
+#ifdef USE_NNTP
+ "news:[^<> ][^<> ]*",
+ "nntp://[a-zA-Z0-9][a-zA-Z0-9:%\\-\\./_]*",
+#endif /* USE_NNTP */
+#ifndef USE_W3MMAILER /* see also chkExternalURIBuffer() */
+ "mailto:[^<> ][^<> ]*@[a-zA-Z0-9][a-zA-Z0-9\\-\\._]*[a-zA-Z0-9]",
+#endif
+#ifdef INET6
+ "https?://[a-zA-Z0-9:%\\-\\./_@]*\\[[a-fA-F0-9:][a-fA-F0-9:\\.]*\\][a-zA-Z0-9:%\\-\\./?=~_\\&+@#,\\$;]*",
+ "ftp://[a-zA-Z0-9:%\\-\\./_@]*\\[[a-fA-F0-9:][a-fA-F0-9:\\.]*\\][a-zA-Z0-9:%\\-\\./=_+@#,\\$]*",
+#endif /* INET6 */
+ NULL
+ };
+ int i;
+ for (i = 0; url_like_pat[i]; i++) {
+ reAnchor(buf, url_like_pat[i]);
+ }
+#ifdef USE_EXTERNAL_URI_LOADER
+ chkExternalURIBuffer(buf);
+#endif
+ buf->check_url |= CHK_URL;
+}
+
+DEFUN(chkURL, MARK_URL, "Mark URL-like strings as anchors")
+{
+ chkURLBuffer(Currentbuf);
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+}
+
+DEFUN(chkWORD, MARK_WORD, "Mark current word as anchor")
+{
+ char *p;
+ int spos, epos;
+ p = getCurWord(Currentbuf, &spos, &epos, ":\"\'`<>()[]{}&|;*?$");
+ if (p == NULL)
+ return;
+ reAnchorWord(Currentbuf, Currentbuf->currentLine, spos, epos);
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+}
+
+#ifdef USE_NNTP
+/* mark Message-ID-like patterns as NEWS anchors */
+void
+chkNMIDBuffer(Buffer *buf)
+{
+ static char *url_like_pat[] = {
+ "<[!-;=?-~]+@[a-zA-Z0-9\\.\\-_]+>",
+ NULL,
+ };
+ int i;
+ for (i = 0; url_like_pat[i]; i++) {
+ reAnchorNews(buf, url_like_pat[i]);
+ }
+ buf->check_url |= CHK_NMID;
+}
+
+DEFUN(chkNMID, MARK_MID, "Mark Message-ID-like strings as anchors")
+{
+ chkNMIDBuffer(Currentbuf);
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+}
+#endif /* USE_NNTP */
+
+/* render frame */
+DEFUN(rFrame, FRAME, "Render frame")
+{
+ Buffer *buf;
+
+ if ((buf = Currentbuf->linkBuffer[LB_FRAME]) != NULL) {
+ Currentbuf = buf;
+ displayBuffer(Currentbuf, B_NORMAL);
+ return;
+ }
+ if (Currentbuf->frameset == NULL) {
+ if ((buf = Currentbuf->linkBuffer[LB_N_FRAME]) != NULL) {
+ Currentbuf = buf;
+ displayBuffer(Currentbuf, B_NORMAL);
+ }
+ return;
+ }
+ if (fmInitialized) {
+ message("Rendering frame", 0, 0);
+ refresh();
+ }
+ buf = renderFrame(Currentbuf, 0);
+ if (buf == NULL) {
+ displayBuffer(Currentbuf, B_NORMAL);
+ return;
+ }
+ buf->linkBuffer[LB_N_FRAME] = Currentbuf;
+ Currentbuf->linkBuffer[LB_FRAME] = buf;
+ pushBuffer(buf);
+ if (fmInitialized && display_ok)
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+}
+
+/* spawn external browser */
+static void
+invoke_browser(char *url)
+{
+ Str cmd;
+ char *browser = NULL;
+ int bg = 0, len;
+
+ CurrentKeyData = NULL; /* not allowed in w3m-control: */
+ browser = searchKeyData();
+ if (browser == NULL || *browser == '\0') {
+ switch (prec_num) {
+ case 0:
+ case 1:
+ browser = ExtBrowser;
+ break;
+ case 2:
+ browser = ExtBrowser2;
+ break;
+ case 3:
+ browser = ExtBrowser3;
+ break;
+ }
+ if (browser == NULL || *browser == '\0') {
+ browser = inputStr("Browse command: ", NULL);
+ if (browser != NULL)
+ browser = conv_to_system(browser);
+ }
+ }
+ else {
+ browser = conv_to_system(browser);
+ }
+ if (browser == NULL || *browser == '\0') {
+ displayBuffer(Currentbuf, B_NORMAL);
+ return;
+ }
+
+ if ((len = strlen(browser)) >= 2 && browser[len - 1] == '&' &&
+ browser[len - 2] != '\\') {
+ browser = allocStr(browser, len - 2);
+ bg = 1;
+ }
+ cmd = myExtCommand(browser, shell_quote(url), FALSE);
+ Strremovetrailingspaces(cmd);
+ fmTerm();
+ mySystem(cmd->ptr, bg);
+ fmInit();
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+}
+
+DEFUN(extbrz, EXTERN, "Execute external browser")
+{
+ if (Currentbuf->bufferprop & BP_INTERNAL) {
+ /* FIXME: gettextize? */
+ disp_err_message("Can't browse...", TRUE);
+ return;
+ }
+ if (Currentbuf->currentURL.scheme == SCM_LOCAL &&
+ !strcmp(Currentbuf->currentURL.file, "-")) {
+ /* file is std input */
+ /* FIXME: gettextize? */
+ disp_err_message("Can't browse stdin", TRUE);
+ return;
+ }
+ invoke_browser(parsedURL2Str(&Currentbuf->currentURL)->ptr);
+}
+
+DEFUN(linkbrz, EXTERN_LINK, "View current link using external browser")
+{
+ Anchor *a;
+ ParsedURL pu;
+
+ if (Currentbuf->firstLine == NULL)
+ return;
+ a = retrieveCurrentAnchor(Currentbuf);
+ if (a == NULL)
+ return;
+ parseURL2(a->url, &pu, baseURL(Currentbuf));
+ invoke_browser(parsedURL2Str(&pu)->ptr);
+}
+
+/* show current line number and number of lines in the entire document */
+DEFUN(curlno, LINE_INFO, "Show current line number")
+{
+ Line *l = Currentbuf->currentLine;
+ Str tmp;
+ int cur = 0, all = 0, col = 0, len = 0;
+
+ if (l != NULL) {
+ cur = l->real_linenumber;
+ col = l->bwidth + Currentbuf->currentColumn + Currentbuf->cursorX + 1;
+ while (l->next && l->next->bpos)
+ l = l->next;
+ if (l->width < 0)
+ l->width = COLPOS(l, l->len);
+ len = l->bwidth + l->width;
+ }
+ if (Currentbuf->lastLine)
+ all = Currentbuf->lastLine->real_linenumber;
+ if (Currentbuf->pagerSource && !(Currentbuf->bufferprop & BP_CLOSE))
+ tmp = Sprintf("line %d col %d/%d", cur, col, len);
+ else
+ tmp = Sprintf("line %d/%d (%d%%) col %d/%d", cur, all,
+ (int)((double)cur * 100.0 / (double)(all ? all : 1)
+ + 0.5), col, len);
+#ifdef USE_M17N
+ Strcat_charp(tmp, " ");
+ Strcat_charp(tmp, wc_ces_to_charset_desc(Currentbuf->document_charset));
+#endif
+
+ disp_message(tmp->ptr, FALSE);
+}
+
+#ifdef USE_IMAGE
+DEFUN(dispI, DISPLAY_IMAGE, "Restart loading and drawing of images")
+{
+ if (!displayImage)
+ initImage();
+ if (!activeImage)
+ return;
+ displayImage = TRUE;
+ /*
+ * if (!(Currentbuf->type && !strcmp(Currentbuf->type, "text/html")))
+ * return;
+ */
+ Currentbuf->image_flag = IMG_FLAG_AUTO;
+ Currentbuf->need_reshape = TRUE;
+ displayBuffer(Currentbuf, B_REDRAW_IMAGE);
+}
+
+DEFUN(stopI, STOP_IMAGE, "Stop loading and drawing of images")
+{
+ if (!activeImage)
+ return;
+ /*
+ * if (!(Currentbuf->type && !strcmp(Currentbuf->type, "text/html")))
+ * return;
+ */
+ Currentbuf->image_flag = IMG_FLAG_SKIP;
+ displayBuffer(Currentbuf, B_REDRAW_IMAGE);
+}
+#endif
+
+#ifdef USE_MOUSE
+
+static int
+mouse_scroll_line(void)
+{
+ if (relative_wheel_scroll)
+ return (relative_wheel_scroll_ratio * LASTLINE + 99) / 100;
+ else
+ return fixed_wheel_scroll_count;
+}
+
+static TabBuffer *
+posTab(int x, int y)
+{
+ TabBuffer *tab;
+
+ if (mouse_action.menu_str && x < mouse_action.menu_width && y == 0)
+ return NO_TABBUFFER;
+ if (y > LastTab->y)
+ return NULL;
+ for (tab = FirstTab; tab; tab = tab->nextTab) {
+ if (tab->x1 <= x && x <= tab->x2 && tab->y == y)
+ return tab;
+ }
+ return NULL;
+}
+
+static void
+do_mouse_action(int btn, int x, int y)
+{
+ MouseActionMap *map = NULL;
+ int ny = -1;
+
+ if (nTab > 1 || mouse_action.menu_str)
+ ny = LastTab->y + 1;
+
+ switch (btn) {
+ case MOUSE_BTN1_DOWN:
+ btn = 0;
+ break;
+ case MOUSE_BTN2_DOWN:
+ btn = 1;
+ break;
+ case MOUSE_BTN3_DOWN:
+ btn = 2;
+ break;
+ default:
+ return;
+ }
+ if (y < ny) {
+ if (mouse_action.menu_str && x >= 0 && x < mouse_action.menu_width) {
+ if (mouse_action.menu_map[btn])
+ map = &mouse_action.menu_map[btn][x];
+ }
+ else
+ map = &mouse_action.tab_map[btn];
+ }
+ else if (y == LASTLINE) {
+ if (mouse_action.lastline_str && x >= 0 &&
+ x < mouse_action.lastline_width) {
+ if (mouse_action.lastline_map[btn])
+ map = &mouse_action.lastline_map[btn][x];
+ }
+ }
+ else if (y > ny) {
+ if (y == Currentbuf->cursorY + Currentbuf->rootY &&
+ (x == Currentbuf->cursorX + Currentbuf->rootX
+#ifdef USE_M17N
+ || (WcOption.use_wide && Currentbuf->currentLine != NULL &&
+ (CharType(Currentbuf->currentLine->propBuf[Currentbuf->pos])
+ == PC_KANJI1)
+ && x == Currentbuf->cursorX + Currentbuf->rootX + 1)
+#endif
+ )) {
+ if (retrieveCurrentAnchor(Currentbuf) ||
+ retrieveCurrentForm(Currentbuf)) {
+ map = &mouse_action.active_map[btn];
+ if (!(map && map->func))
+ map = &mouse_action.anchor_map[btn];
+ }
+ }
+ else {
+ int cx = Currentbuf->cursorX, cy = Currentbuf->cursorY;
+ cursorXY(Currentbuf, x - Currentbuf->rootX, y - Currentbuf->rootY);
+ if (y == Currentbuf->cursorY + Currentbuf->rootY &&
+ (x == Currentbuf->cursorX + Currentbuf->rootX
+#ifdef USE_M17N
+ || (WcOption.use_wide && Currentbuf->currentLine != NULL &&
+ (CharType(Currentbuf->currentLine->
+ propBuf[Currentbuf->pos]) == PC_KANJI1)
+ && x == Currentbuf->cursorX + Currentbuf->rootX + 1)
+#endif
+ ) &&
+ (retrieveCurrentAnchor(Currentbuf) ||
+ retrieveCurrentForm(Currentbuf)))
+ map = &mouse_action.anchor_map[btn];
+ cursorXY(Currentbuf, cx, cy);
+ }
+ }
+ if (!(map && map->func))
+ map = &mouse_action.default_map[btn];
+ if (map && map->func) {
+ mouse_action.in_action = TRUE;
+ mouse_action.cursorX = x;
+ mouse_action.cursorY = y;
+ CurrentKey = -1;
+ CurrentKeyData = NULL;
+ CurrentCmdData = map->data;
+ (*map->func) ();
+ CurrentCmdData = NULL;
+ }
+}
+
+static void
+process_mouse(int btn, int x, int y)
+{
+ int delta_x, delta_y, i;
+ static int press_btn = MOUSE_BTN_RESET, press_x, press_y;
+ TabBuffer *t;
+ int ny = -1;
+
+ if (nTab > 1 || mouse_action.menu_str)
+ ny = LastTab->y + 1;
+ if (btn == MOUSE_BTN_UP) {
+ switch (press_btn) {
+ case MOUSE_BTN1_DOWN:
+ if (press_y == y && press_x == x)
+ do_mouse_action(press_btn, x, y);
+ else if (ny > 0 && y < ny) {
+ if (press_y < ny) {
+ moveTab(posTab(press_x, press_y), posTab(x, y),
+ (press_y == y) ? (press_x < x) : (press_y < y));
+ return;
+ }
+ else if (press_x >= Currentbuf->rootX) {
+ Buffer *buf = Currentbuf;
+ int cx = Currentbuf->cursorX, cy = Currentbuf->cursorY;
+
+ t = posTab(x, y);
+ if (t == NULL)
+ return;
+ if (t == NO_TABBUFFER)
+ t = NULL; /* open new tab */
+ cursorXY(Currentbuf, press_x - Currentbuf->rootX,
+ press_y - Currentbuf->rootY);
+ if (Currentbuf->cursorY == press_y - Currentbuf->rootY &&
+ (Currentbuf->cursorX == press_x - Currentbuf->rootX
+#ifdef USE_M17N
+ || (WcOption.use_wide &&
+ Currentbuf->currentLine != NULL &&
+ (CharType(Currentbuf->currentLine->
+ propBuf[Currentbuf->pos]) == PC_KANJI1)
+ && Currentbuf->cursorX == press_x
+ - Currentbuf->rootX - 1)
+#endif
+ )) {
+ displayBuffer(Currentbuf, B_NORMAL);
+ followTab(t);
+ }
+ if (buf == Currentbuf)
+ cursorXY(Currentbuf, cx, cy);
+ }
+ return;
+ }
+ else {
+ delta_x = x - press_x;
+ delta_y = y - press_y;
+
+ if (abs(delta_x) < abs(delta_y) / 3)
+ delta_x = 0;
+ if (abs(delta_y) < abs(delta_x) / 3)
+ delta_y = 0;
+ if (reverse_mouse) {
+ delta_y = -delta_y;
+ delta_x = -delta_x;
+ }
+ if (delta_y > 0) {
+ prec_num = delta_y;
+ ldown1();
+ }
+ else if (delta_y < 0) {
+ prec_num = -delta_y;
+ lup1();
+ }
+ if (delta_x > 0) {
+ prec_num = delta_x;
+ col1L();
+ }
+ else if (delta_x < 0) {
+ prec_num = -delta_x;
+ col1R();
+ }
+ }
+ break;
+ case MOUSE_BTN2_DOWN:
+ case MOUSE_BTN3_DOWN:
+ if (press_y == y && press_x == x)
+ do_mouse_action(press_btn, x, y);
+ break;
+ case MOUSE_BTN4_DOWN_RXVT:
+ for (i = 0; i < mouse_scroll_line(); i++)
+ ldown1();
+ break;
+ case MOUSE_BTN5_DOWN_RXVT:
+ for (i = 0; i < mouse_scroll_line(); i++)
+ lup1();
+ break;
+ }
+ }
+ else if (btn == MOUSE_BTN4_DOWN_XTERM) {
+ for (i = 0; i < mouse_scroll_line(); i++)
+ ldown1();
+ }
+ else if (btn == MOUSE_BTN5_DOWN_XTERM) {
+ for (i = 0; i < mouse_scroll_line(); i++)
+ lup1();
+ }
+
+ if (btn != MOUSE_BTN4_DOWN_RXVT || press_btn == MOUSE_BTN_RESET) {
+ press_btn = btn;
+ press_x = x;
+ press_y = y;
+ }
+ else {
+ press_btn = MOUSE_BTN_RESET;
+ }
+}
+
+DEFUN(msToggle, MOUSE_TOGGLE, "Toggle activity of mouse")
+{
+ if (use_mouse) {
+ use_mouse = FALSE;
+ }
+ else {
+ use_mouse = TRUE;
+ }
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+}
+
+DEFUN(mouse, MOUSE, "mouse operation")
+{
+ int btn, x, y;
+
+ btn = (unsigned char)getch() - 32;
+#if defined(__CYGWIN__)
+ if (cygwin_mouse_btn_swapped) {
+ if (btn == MOUSE_BTN2_DOWN)
+ btn = MOUSE_BTN3_DOWN;
+ else if (btn == MOUSE_BTN3_DOWN)
+ btn = MOUSE_BTN2_DOWN;
+ }
+#endif
+ x = (unsigned char)getch() - 33;
+ if (x < 0)
+ x += 0x100;
+ y = (unsigned char)getch() - 33;
+ if (y < 0)
+ y += 0x100;
+
+ if (x < 0 || x >= COLS || y < 0 || y > LASTLINE)
+ return;
+ process_mouse(btn, x, y);
+}
+
+#ifdef USE_GPM
+int
+gpm_process_mouse(Gpm_Event * event, void *data)
+{
+ int btn = MOUSE_BTN_RESET, x, y;
+ if (event->type & GPM_UP)
+ btn = MOUSE_BTN_UP;
+ else if (event->type & GPM_DOWN) {
+ switch (event->buttons) {
+ case GPM_B_LEFT:
+ btn = MOUSE_BTN1_DOWN;
+ break;
+ case GPM_B_MIDDLE:
+ btn = MOUSE_BTN2_DOWN;
+ break;
+ case GPM_B_RIGHT:
+ btn = MOUSE_BTN3_DOWN;
+ break;
+ }
+ }
+ else {
+ GPM_DRAWPOINTER(event);
+ return 0;
+ }
+ x = event->x;
+ y = event->y;
+ process_mouse(btn, x - 1, y - 1);
+ return 0;
+}
+#endif /* USE_GPM */
+
+#ifdef USE_SYSMOUSE
+int
+sysm_process_mouse(int x, int y, int nbs, int obs)
+{
+ int btn;
+ int bits;
+
+ if (obs & ~nbs)
+ btn = MOUSE_BTN_UP;
+ else if (nbs & ~obs) {
+ bits = nbs & ~obs;
+ btn = bits & 0x1 ? MOUSE_BTN1_DOWN :
+ (bits & 0x2 ? MOUSE_BTN2_DOWN :
+ (bits & 0x4 ? MOUSE_BTN3_DOWN : 0));
+ }
+ else /* nbs == obs */
+ return 0;
+ process_mouse(btn, x, y);
+ return 0;
+}
+#endif /* USE_SYSMOUSE */
+
+DEFUN(movMs, MOVE_MOUSE, "Move cursor to mouse cursor (for mouse action)")
+{
+ if (!mouse_action.in_action)
+ return;
+ if ((nTab > 1 || mouse_action.menu_str) &&
+ mouse_action.cursorY < LastTab->y + 1)
+ return;
+ else if (mouse_action.cursorX >= Currentbuf->rootX &&
+ mouse_action.cursorY < LASTLINE) {
+ cursorXY(Currentbuf, mouse_action.cursorX - Currentbuf->rootX,
+ mouse_action.cursorY - Currentbuf->rootY);
+ }
+ displayBuffer(Currentbuf, B_NORMAL);
+}
+
+#ifdef USE_MENU
+#ifdef KANJI_SYMBOLS
+#define FRAME_WIDTH 2
+#else
+#define FRAME_WIDTH 1
+#endif
+
+DEFUN(menuMs, MENU_MOUSE, "Popup menu at mouse cursor (for mouse action)")
+{
+ if (!mouse_action.in_action)
+ return;
+ if ((nTab > 1 || mouse_action.menu_str) &&
+ mouse_action.cursorY < LastTab->y + 1)
+ mouse_action.cursorX -= FRAME_WIDTH + 1;
+ else if (mouse_action.cursorX >= Currentbuf->rootX &&
+ mouse_action.cursorY < LASTLINE) {
+ cursorXY(Currentbuf, mouse_action.cursorX - Currentbuf->rootX,
+ mouse_action.cursorY - Currentbuf->rootY);
+ displayBuffer(Currentbuf, B_NORMAL);
+ }
+ mainMn();
+}
+#endif
+
+DEFUN(tabMs, TAB_MOUSE, "Move to tab on mouse cursor (for mouse action)")
+{
+ TabBuffer *tab;
+
+ if (!mouse_action.in_action)
+ return;
+ tab = posTab(mouse_action.cursorX, mouse_action.cursorY);
+ if (!tab || tab == NO_TABBUFFER)
+ return;
+ CurrentTab = tab;
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+}
+
+DEFUN(closeTMs, CLOSE_TAB_MOUSE,
+ "Close tab on mouse cursor (for mouse action)")
+{
+ TabBuffer *tab;
+
+ if (!mouse_action.in_action)
+ return;
+ tab = posTab(mouse_action.cursorX, mouse_action.cursorY);
+ if (!tab || tab == NO_TABBUFFER)
+ return;
+ deleteTab(tab);
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+}
+#endif /* USE_MOUSE */
+
+DEFUN(dispVer, VERSION, "Display version of w3m")
+{
+ disp_message(Sprintf("w3m version %s", w3m_version)->ptr, TRUE);
+}
+
+DEFUN(wrapToggle, WRAP_TOGGLE, "Toggle wrap search mode")
+{
+ if (WrapSearch) {
+ WrapSearch = FALSE;
+ /* FIXME: gettextize? */
+ disp_message("Wrap search off", TRUE);
+ }
+ else {
+ WrapSearch = TRUE;
+ /* FIXME: gettextize? */
+ disp_message("Wrap search on", TRUE);
+ }
+}
+
+static int
+is_wordchar(int c, const char *badchars)
+{
+ if (badchars)
+ return !(IS_SPACE(c) || strchr(badchars, c));
+ else
+ return IS_ALPHA(c);
+}
+
+static char *
+getCurWord(Buffer *buf, int *spos, int *epos, const char *badchars)
+{
+ char *p;
+ Line *l = buf->currentLine;
+ int b, e;
+
+ *spos = 0;
+ *epos = 0;
+ if (l == NULL)
+ return NULL;
+ p = l->lineBuf;
+ e = buf->pos;
+ while (e > 0 && !is_wordchar(p[e], badchars))
+ e--;
+ if (!is_wordchar(p[e], badchars))
+ return NULL;
+ b = e;
+ while (b > 0 && is_wordchar(p[b - 1], badchars))
+ b--;
+ while (e < l->len && is_wordchar(p[e], badchars))
+ e++;
+ *spos = b;
+ *epos = e;
+ return &p[b];
+}
+
+static char *
+GetWord(Buffer *buf)
+{
+ int b, e;
+ char *p;
+
+ if ((p = getCurWord(buf, &b, &e, 0)) != NULL) {
+ return Strnew_charp_n(p, e - b)->ptr;
+ }
+ return NULL;
+}
+
+#ifdef USE_DICT
+static void
+execdict(char *word)
+{
+ char *w, *dictcmd;
+ Buffer *buf;
+
+ if (!UseDictCommand || word == NULL || *word == '\0') {
+ displayBuffer(Currentbuf, B_NORMAL);
+ return;
+ }
+ w = conv_to_system(word);
+ if (*w == '\0') {
+ displayBuffer(Currentbuf, B_NORMAL);
+ return;
+ }
+ dictcmd = Sprintf("%s?%s", DictCommand,
+ Str_form_quote(Strnew_charp(w))->ptr)->ptr;
+ buf = loadGeneralFile(dictcmd, NULL, NO_REFERER, 0, NULL);
+ if (buf == NULL) {
+ disp_message("Execution failed", TRUE);
+ return;
+ }
+ else {
+ buf->filename = w;
+ buf->buffername = Sprintf("%s %s", DICTBUFFERNAME, word)->ptr;
+ if (buf->type == NULL)
+ buf->type = "text/plain";
+ pushBuffer(buf);
+ }
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+}
+
+DEFUN(dictword, DICT_WORD, "Execute dictionary command (see README.dict)")
+{
+ execdict(inputStr("(dictionary)!", ""));
+}
+
+DEFUN(dictwordat, DICT_WORD_AT,
+ "Execute dictionary command for word at cursor")
+{
+ execdict(GetWord(Currentbuf));
+}
+#endif /* USE_DICT */
+
+void
+set_buffer_environ(Buffer *buf)
+{
+ static Buffer *prev_buf = NULL;
+ static Line *prev_line = NULL;
+ static int prev_pos = -1;
+ Line *l;
+
+ if (buf == NULL)
+ return;
+ if (buf != prev_buf) {
+ set_environ("W3M_SOURCEFILE", buf->sourcefile);
+ set_environ("W3M_FILENAME", buf->filename);
+ set_environ("W3M_TITLE", buf->buffername);
+ set_environ("W3M_URL", parsedURL2Str(&buf->currentURL)->ptr);
+ set_environ("W3M_TYPE", buf->real_type ? buf->real_type : "unknown");
+#ifdef USE_M17N
+ set_environ("W3M_CHARSET", wc_ces_to_charset(buf->document_charset));
+#endif
+ }
+ l = buf->currentLine;
+ if (l && (buf != prev_buf || l != prev_line || buf->pos != prev_pos)) {
+ Anchor *a;
+ ParsedURL pu;
+ char *s = GetWord(buf);
+ set_environ("W3M_CURRENT_WORD", s ? s : "");
+ a = retrieveCurrentAnchor(buf);
+ if (a) {
+ parseURL2(a->url, &pu, baseURL(buf));
+ set_environ("W3M_CURRENT_LINK", parsedURL2Str(&pu)->ptr);
+ }
+ else
+ set_environ("W3M_CURRENT_LINK", "");
+ a = retrieveCurrentImg(buf);
+ if (a) {
+ parseURL2(a->url, &pu, baseURL(buf));
+ set_environ("W3M_CURRENT_IMG", parsedURL2Str(&pu)->ptr);
+ }
+ else
+ set_environ("W3M_CURRENT_IMG", "");
+ a = retrieveCurrentForm(buf);
+ if (a)
+ set_environ("W3M_CURRENT_FORM", form2str((FormItemList *)a->url));
+ else
+ set_environ("W3M_CURRENT_FORM", "");
+ set_environ("W3M_CURRENT_LINE", Sprintf("%d",
+ l->real_linenumber)->ptr);
+ set_environ("W3M_CURRENT_COLUMN", Sprintf("%d",
+ buf->currentColumn +
+ buf->cursorX + 1)->ptr);
+ }
+ else if (!l) {
+ set_environ("W3M_CURRENT_WORD", "");
+ set_environ("W3M_CURRENT_LINK", "");
+ set_environ("W3M_CURRENT_IMG", "");
+ set_environ("W3M_CURRENT_FORM", "");
+ set_environ("W3M_CURRENT_LINE", "0");
+ set_environ("W3M_CURRENT_COLUMN", "0");
+ }
+ prev_buf = buf;
+ prev_line = l;
+ prev_pos = buf->pos;
+}
+
+char *
+searchKeyData(void)
+{
+ char *data = NULL;
+
+ if (CurrentKeyData != NULL && *CurrentKeyData != '\0')
+ data = CurrentKeyData;
+ else if (CurrentCmdData != NULL && *CurrentCmdData != '\0')
+ data = CurrentCmdData;
+ else if (CurrentKey >= 0)
+ data = getKeyData(CurrentKey);
+ CurrentKeyData = NULL;
+ CurrentCmdData = NULL;
+ if (data == NULL || *data == '\0')
+ return NULL;
+ return allocStr(data, -1);
+}
+
+static int
+searchKeyNum(void)
+{
+ char *d;
+ int n = 1;
+
+ d = searchKeyData();
+ if (d != NULL)
+ n = atoi(d);
+ return n * PREC_NUM;
+}
+
+#ifdef __EMX__
+#ifdef USE_M17N
+static char *
+getCodePage(void)
+{
+ ULONG CpList[8], CpSize;
+
+ if (!getenv("WINDOWID") && !DosQueryCp(sizeof(CpList), CpList, &CpSize))
+ return Sprintf("CP%d", *CpList)->ptr;
+ return NULL;
+}
+#endif
+#endif
+
+void
+deleteFiles()
+{
+ Buffer *buf;
+ char *f;
+
+ for (CurrentTab = FirstTab; CurrentTab; CurrentTab = CurrentTab->nextTab) {
+ while (Firstbuf && Firstbuf != NO_BUFFER) {
+ buf = Firstbuf->nextBuffer;
+ discardBuffer(Firstbuf);
+ Firstbuf = buf;
+ }
+ }
+ while ((f = popText(fileToDelete)) != NULL)
+ unlink(f);
+}
+
+void
+w3m_exit(int i)
+{
+#ifdef USE_MIGEMO
+ init_migemo(); /* close pipe to migemo */
+#endif
+ stopDownload();
+ deleteFiles();
+#ifdef USE_SSL
+ free_ssl_ctx();
+#endif
+ disconnectFTP();
+#ifdef USE_NNTP
+ disconnectNews();
+#endif
+ exit(i);
+}
+
+DEFUN(execCmd, COMMAND, "Execute w3m command(s)")
+{
+ char *data, *p;
+ int cmd;
+
+ CurrentKeyData = NULL; /* not allowed in w3m-control: */
+ data = searchKeyData();
+ if (data == NULL || *data == '\0') {
+ data = inputStrHist("command [; ...]: ", "", TextHist);
+ if (data == NULL) {
+ displayBuffer(Currentbuf, B_NORMAL);
+ return;
+ }
+ }
+ /* data: FUNC [DATA] [; FUNC [DATA] ...] */
+ while (*data) {
+ SKIP_BLANKS(data);
+ if (*data == ';') {
+ data++;
+ continue;
+ }
+ p = getWord(&data);
+ cmd = getFuncList(p);
+ if (cmd < 0)
+ break;
+ p = getQWord(&data);
+ CurrentKey = -1;
+ CurrentKeyData = NULL;
+ CurrentCmdData = *p ? p : NULL;
+#ifdef USE_MOUSE
+ if (use_mouse)
+ mouse_inactive();
+#endif
+ w3mFuncList[cmd].func();
+#ifdef USE_MOUSE
+ if (use_mouse)
+ mouse_active();
+#endif
+ CurrentCmdData = NULL;
+ }
+ displayBuffer(Currentbuf, B_NORMAL);
+}
+
+#ifdef USE_ALARM
+static MySignalHandler
+SigAlarm(SIGNAL_ARG)
+{
+ char *data;
+
+ if (CurrentAlarm->sec > 0) {
+ CurrentKey = -1;
+ CurrentKeyData = NULL;
+ CurrentCmdData = data = (char *)CurrentAlarm->data;
+#ifdef USE_MOUSE
+ if (use_mouse)
+ mouse_inactive();
+#endif
+ w3mFuncList[CurrentAlarm->cmd].func();
+#ifdef USE_MOUSE
+ if (use_mouse)
+ mouse_active();
+#endif
+ CurrentCmdData = NULL;
+ if (CurrentAlarm->status == AL_IMPLICIT_ONCE) {
+ CurrentAlarm->sec = 0;
+ CurrentAlarm->status = AL_UNSET;
+ }
+ if (Currentbuf->event) {
+ if (Currentbuf->event->status != AL_UNSET)
+ CurrentAlarm = Currentbuf->event;
+ else
+ Currentbuf->event = NULL;
+ }
+ if (!Currentbuf->event)
+ CurrentAlarm = &DefaultAlarm;
+ if (CurrentAlarm->sec > 0) {
+ mySignal(SIGALRM, SigAlarm);
+ alarm(CurrentAlarm->sec);
+ }
+ }
+ SIGNAL_RETURN;
+}
+
+
+DEFUN(setAlarm, ALARM, "Set alarm")
+{
+ char *data;
+ int sec = 0, cmd = -1;
+
+ CurrentKeyData = NULL; /* not allowed in w3m-control: */
+ data = searchKeyData();
+ if (data == NULL || *data == '\0') {
+ data = inputStrHist("(Alarm)sec command: ", "", TextHist);
+ if (data == NULL) {
+ displayBuffer(Currentbuf, B_NORMAL);
+ return;
+ }
+ }
+ if (*data != '\0') {
+ sec = atoi(getWord(&data));
+ if (sec > 0)
+ cmd = getFuncList(getWord(&data));
+ }
+ if (cmd >= 0) {
+ data = getQWord(&data);
+ setAlarmEvent(&DefaultAlarm, sec, AL_EXPLICIT, cmd, data);
+ disp_message_nsec(Sprintf("%dsec %s %s", sec, w3mFuncList[cmd].id,
+ data)->ptr, FALSE, 1, FALSE, TRUE);
+ }
+ else {
+ setAlarmEvent(&DefaultAlarm, 0, AL_UNSET, FUNCNAME_nulcmd, NULL);
+ }
+ displayBuffer(Currentbuf, B_NORMAL);
+}
+
+AlarmEvent *
+setAlarmEvent(AlarmEvent * event, int sec, short status, int cmd, void *data)
+{
+ if (event == NULL)
+ event = New(AlarmEvent);
+ event->sec = sec;
+ event->status = status;
+ event->cmd = cmd;
+ event->data = data;
+ return event;
+}
+#endif
+
+DEFUN(reinit, REINIT, "Reload configuration files")
+{
+ char *resource = searchKeyData();
+
+ if (resource == NULL) {
+ init_rc();
+ sync_with_option();
+#ifdef USE_COOKIE
+ initCookie();
+#endif
+ displayBuffer(Currentbuf, B_REDRAW_IMAGE);
+ return;
+ }
+
+ if (!strcasecmp(resource, "CONFIG") || !strcasecmp(resource, "RC")) {
+ init_rc();
+ sync_with_option();
+ displayBuffer(Currentbuf, B_REDRAW_IMAGE);
+ return;
+ }
+
+#ifdef USE_COOKIE
+ if (!strcasecmp(resource, "COOKIE")) {
+ initCookie();
+ return;
+ }
+#endif
+
+ if (!strcasecmp(resource, "KEYMAP")) {
+ initKeymap(TRUE);
+ return;
+ }
+
+ if (!strcasecmp(resource, "MAILCAP")) {
+ initMailcap();
+ return;
+ }
+
+#ifdef USE_MOUSE
+ if (!strcasecmp(resource, "MOUSE")) {
+ initMouseAction();
+ displayBuffer(Currentbuf, B_REDRAW_IMAGE);
+ return;
+ }
+#endif
+
+#ifdef USE_MENU
+ if (!strcasecmp(resource, "MENU")) {
+ initMenu();
+ return;
+ }
+#endif
+
+ if (!strcasecmp(resource, "MIMETYPES")) {
+ initMimeTypes();
+ return;
+ }
+
+#ifdef USE_EXTERNAL_URI_LOADER
+ if (!strcasecmp(resource, "URIMETHODS")) {
+ initURIMethods();
+ return;
+ }
+#endif
+
+ disp_err_message(Sprintf("Don't know how to reinitialize '%s'", resource)->
+ ptr, FALSE);
+}
+
+DEFUN(defKey, DEFINE_KEY,
+ "Define a binding between a key stroke and a user command")
+{
+ char *data;
+
+ CurrentKeyData = NULL; /* not allowed in w3m-control: */
+ data = searchKeyData();
+ if (data == NULL || *data == '\0') {
+ data = inputStrHist("Key definition: ", "", TextHist);
+ if (data == NULL || *data == '\0') {
+ displayBuffer(Currentbuf, B_NORMAL);
+ return;
+ }
+ }
+ setKeymap(allocStr(data, -1), -1, TRUE);
+ displayBuffer(Currentbuf, B_NORMAL);
+}
+
+TabBuffer *
+newTab(void)
+{
+ TabBuffer *n;
+
+ n = New(TabBuffer);
+ if (n == NULL)
+ return NULL;
+ n->nextTab = NULL;
+ n->currentBuffer = NULL;
+ n->firstBuffer = NULL;
+ return n;
+}
+
+static void
+_newT(void)
+{
+ TabBuffer *tag;
+ Buffer *buf;
+ int i;
+
+ tag = newTab();
+ if (!tag)
+ return;
+
+ buf = newBuffer(Currentbuf->width);
+ copyBuffer(buf, Currentbuf);
+ buf->nextBuffer = NULL;
+ for (i = 0; i < MAX_LB; i++)
+ buf->linkBuffer[i] = NULL;
+ (*buf->clone)++;
+ tag->firstBuffer = tag->currentBuffer = buf;
+
+ tag->nextTab = CurrentTab->nextTab;
+ tag->prevTab = CurrentTab;
+ if (CurrentTab->nextTab)
+ CurrentTab->nextTab->prevTab = tag;
+ else
+ LastTab = tag;
+ CurrentTab->nextTab = tag;
+ CurrentTab = tag;
+ nTab++;
+}
+
+DEFUN(newT, NEW_TAB, "Open new tab")
+{
+ _newT();
+ displayBuffer(Currentbuf, B_REDRAW_IMAGE);
+}
+
+static TabBuffer *
+numTab(int n)
+{
+ TabBuffer *tab;
+ int i;
+
+ if (n == 0)
+ return CurrentTab;
+ if (n == 1)
+ return FirstTab;
+ if (nTab <= 1)
+ return NULL;
+ for (tab = FirstTab, i = 1; tab && i < n; tab = tab->nextTab, i++) ;
+ return tab;
+}
+
+void
+calcTabPos(void)
+{
+ TabBuffer *tab;
+#if 0
+ int lcol = 0, rcol = 2, col;
+#else
+ int lcol = 0, rcol = 0, col;
+#endif
+ int n1, n2, na, nx, ny, ix, iy;
+
+#ifdef USE_MOUSE
+ lcol = mouse_action.menu_str ? mouse_action.menu_width : 0;
+#endif
+
+ if (nTab <= 0)
+ return;
+ n1 = (COLS - rcol - lcol) / TabCols;
+ if (n1 >= nTab) {
+ n2 = 1;
+ ny = 1;
+ }
+ else {
+ if (n1 < 0)
+ n1 = 0;
+ n2 = COLS / TabCols;
+ if (n2 == 0)
+ n2 = 1;
+ ny = (nTab - n1 - 1) / n2 + 2;
+ }
+ na = n1 + n2 * (ny - 1);
+ n1 -= (na - nTab) / ny;
+ if (n1 < 0)
+ n1 = 0;
+ na = n1 + n2 * (ny - 1);
+ tab = FirstTab;
+ for (iy = 0; iy < ny && tab; iy++) {
+ if (iy == 0) {
+ nx = n1;
+ col = COLS - rcol - lcol;
+ }
+ else {
+ nx = n2 - (na - nTab + (iy - 1)) / (ny - 1);
+ col = COLS;
+ }
+ for (ix = 0; ix < nx && tab; ix++, tab = tab->nextTab) {
+ tab->x1 = col * ix / nx;
+ tab->x2 = col * (ix + 1) / nx - 1;
+ tab->y = iy;
+ if (iy == 0) {
+ tab->x1 += lcol;
+ tab->x2 += lcol;
+ }
+ }
+ }
+}
+
+TabBuffer *
+deleteTab(TabBuffer * tab)
+{
+ Buffer *buf, *next;
+
+ if (nTab <= 1)
+ return FirstTab;
+ if (tab->prevTab) {
+ if (tab->nextTab)
+ tab->nextTab->prevTab = tab->prevTab;
+ else
+ LastTab = tab->prevTab;
+ tab->prevTab->nextTab = tab->nextTab;
+ if (tab == CurrentTab)
+ CurrentTab = tab->prevTab;
+ }
+ else { /* tab == FirstTab */
+ tab->nextTab->prevTab = NULL;
+ FirstTab = tab->nextTab;
+ if (tab == CurrentTab)
+ CurrentTab = tab->nextTab;
+ }
+ nTab--;
+ buf = tab->firstBuffer;
+ while (buf && buf != NO_BUFFER) {
+ next = buf->nextBuffer;
+ discardBuffer(buf);
+ buf = next;
+ }
+ return FirstTab;
+}
+
+DEFUN(closeT, CLOSE_TAB, "Close current tab")
+{
+ TabBuffer *tab;
+
+ if (nTab <= 1)
+ return;
+ if (prec_num)
+ tab = numTab(PREC_NUM);
+ else
+ tab = CurrentTab;
+ if (tab)
+ deleteTab(tab);
+ displayBuffer(Currentbuf, B_REDRAW_IMAGE);
+}
+
+DEFUN(nextT, NEXT_TAB, "Move to next tab")
+{
+ int i;
+
+ if (nTab <= 1)
+ return;
+ for (i = 0; i < PREC_NUM; i++) {
+ if (CurrentTab->nextTab)
+ CurrentTab = CurrentTab->nextTab;
+ else
+ CurrentTab = FirstTab;
+ }
+ displayBuffer(Currentbuf, B_REDRAW_IMAGE);
+}
+
+DEFUN(prevT, PREV_TAB, "Move to previous tab")
+{
+ int i;
+
+ if (nTab <= 1)
+ return;
+ for (i = 0; i < PREC_NUM; i++) {
+ if (CurrentTab->prevTab)
+ CurrentTab = CurrentTab->prevTab;
+ else
+ CurrentTab = LastTab;
+ }
+ displayBuffer(Currentbuf, B_REDRAW_IMAGE);
+}
+
+void
+followTab(TabBuffer * tab)
+{
+ Buffer *buf;
+ Anchor *a;
+
+#ifdef USE_IMAGE
+ a = retrieveCurrentImg(Currentbuf);
+ if (!(a && a->image && a->image->map))
+#endif
+ a = retrieveCurrentAnchor(Currentbuf);
+ if (a == NULL)
+ return;
+
+ if (tab == CurrentTab) {
+ check_target = FALSE;
+ followA();
+ check_target = TRUE;
+ return;
+ }
+ _newT();
+ buf = Currentbuf;
+ check_target = FALSE;
+ followA();
+ check_target = TRUE;
+ if (tab == NULL) {
+ if (buf != Currentbuf)
+ delBuffer(buf);
+ else
+ deleteTab(CurrentTab);
+ }
+ else if (buf != Currentbuf) {
+ /* buf <- p <- ... <- Currentbuf = c */
+ Buffer *c, *p;
+
+ c = Currentbuf;
+ p = prevBuffer(c, buf);
+ p->nextBuffer = NULL;
+ Firstbuf = buf;
+ deleteTab(CurrentTab);
+ CurrentTab = tab;
+ for (buf = p; buf; buf = p) {
+ p = prevBuffer(c, buf);
+ pushBuffer(buf);
+ }
+ }
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+}
+
+DEFUN(tabA, TAB_LINK, "Open current link on new tab")
+{
+ followTab(prec_num ? numTab(PREC_NUM) : NULL);
+}
+
+static void
+tabURL0(TabBuffer * tab, char *prompt, int relative)
+{
+ Buffer *buf;
+
+ if (tab == CurrentTab) {
+ goURL0(prompt, relative);
+ return;
+ }
+ _newT();
+ buf = Currentbuf;
+ goURL0(prompt, relative);
+ if (tab == NULL) {
+ if (buf != Currentbuf)
+ delBuffer(buf);
+ else
+ deleteTab(CurrentTab);
+ }
+ else if (buf != Currentbuf) {
+ /* buf <- p <- ... <- Currentbuf = c */
+ Buffer *c, *p;
+
+ c = Currentbuf;
+ p = prevBuffer(c, buf);
+ p->nextBuffer = NULL;
+ Firstbuf = buf;
+ deleteTab(CurrentTab);
+ CurrentTab = tab;
+ for (buf = p; buf; buf = p) {
+ p = prevBuffer(c, buf);
+ pushBuffer(buf);
+ }
+ }
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+}
+
+DEFUN(tabURL, TAB_GOTO, "Open URL on new tab")
+{
+ tabURL0(prec_num ? numTab(PREC_NUM) : NULL,
+ "Goto URL on new tab: ", FALSE);
+}
+
+DEFUN(tabrURL, TAB_GOTO_RELATIVE, "Open relative URL on new tab")
+{
+ tabURL0(prec_num ? numTab(PREC_NUM) : NULL,
+ "Goto relative URL on new tab: ", TRUE);
+}
+
+void
+moveTab(TabBuffer * t, TabBuffer * t2, int right)
+{
+ if (t2 == NO_TABBUFFER)
+ t2 = FirstTab;
+ if (!t || !t2 || t == t2 || t == NO_TABBUFFER)
+ return;
+ if (t->prevTab) {
+ if (t->nextTab)
+ t->nextTab->prevTab = t->prevTab;
+ else
+ LastTab = t->prevTab;
+ t->prevTab->nextTab = t->nextTab;
+ }
+ else {
+ t->nextTab->prevTab = NULL;
+ FirstTab = t->nextTab;
+ }
+ if (right) {
+ t->nextTab = t2->nextTab;
+ t->prevTab = t2;
+ if (t2->nextTab)
+ t2->nextTab->prevTab = t;
+ else
+ LastTab = t;
+ t2->nextTab = t;
+ }
+ else {
+ t->prevTab = t2->prevTab;
+ t->nextTab = t2;
+ if (t2->prevTab)
+ t2->prevTab->nextTab = t;
+ else
+ FirstTab = t;
+ t2->prevTab = t;
+ }
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+}
+
+DEFUN(tabR, TAB_RIGHT, "Move current tab right")
+{
+ TabBuffer *tab;
+ int i;
+
+ for (tab = CurrentTab, i = 0; tab && i < PREC_NUM;
+ tab = tab->nextTab, i++) ;
+ moveTab(CurrentTab, tab ? tab : LastTab, TRUE);
+}
+
+DEFUN(tabL, TAB_LEFT, "Move current tab left")
+{
+ TabBuffer *tab;
+ int i;
+
+ for (tab = CurrentTab, i = 0; tab && i < PREC_NUM;
+ tab = tab->prevTab, i++) ;
+ moveTab(CurrentTab, tab ? tab : FirstTab, FALSE);
+}
+
+void
+addDownloadList(pid_t pid, char *url, char *save, char *lock, clen_t size)
+{
+ DownloadList *d;
+
+ d = New(DownloadList);
+ d->pid = pid;
+ d->url = url;
+ if (save[0] != '/' && save[0] != '~')
+ save = Strnew_m_charp(CurrentDir, "/", save, NULL)->ptr;
+ d->save = expandPath(save);
+ d->lock = lock;
+ d->size = size;
+ d->time = time(0);
+ d->ok = FALSE;
+ d->next = NULL;
+ d->prev = LastDL;
+ if (LastDL)
+ LastDL->next = d;
+ else
+ FirstDL = d;
+ LastDL = d;
+ add_download_list = TRUE;
+}
+
+int
+checkDownloadList(void)
+{
+ DownloadList *d;
+ struct stat st;
+
+ if (!FirstDL)
+ return FALSE;
+ for (d = FirstDL; d != NULL; d = d->next) {
+ if (!d->ok && !lstat(d->lock, &st))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static char *
+convert_size3(clen_t size)
+{
+ Str tmp = Strnew();
+ int n;
+
+ do {
+ n = size % 1000;
+ size /= 1000;
+ tmp = Sprintf(size ? ",%.3d%s" : "%d%s", n, tmp->ptr);
+ } while (size);
+ return tmp->ptr;
+}
+
+static Buffer *
+DownloadListBuffer(void)
+{
+ DownloadList *d;
+ Str src = NULL;
+ struct stat st;
+ time_t cur_time;
+ int duration, rate, eta;
+ size_t size;
+
+ if (!FirstDL)
+ return NULL;
+ cur_time = time(0);
+ /* FIXME: gettextize? */
+ src = Strnew_charp("<html><head><title>" DOWNLOAD_LIST_TITLE
+ "</title></head>\n<body><h1 align=center>"
+ DOWNLOAD_LIST_TITLE "</h1>\n"
+ "<form method=internal action=download><hr>\n");
+ for (d = LastDL; d != NULL; d = d->prev) {
+ if (lstat(d->lock, &st))
+ d->ok = TRUE;
+ Strcat_charp(src, "<pre>\n");
+ Strcat(src, Sprintf("%s\n --&gt; %s\n ", html_quote(d->url),
+ html_quote(conv_from_system(d->save))));
+ duration = cur_time - d->time;
+ if (!stat(d->save, &st)) {
+ size = st.st_size;
+ if (d->ok) {
+ d->size = size;
+ duration = st.st_mtime - d->time;
+ }
+ }
+ else
+ size = 0;
+ if (d->size) {
+ int i, l = COLS - 6;
+ if (size < d->size)
+ i = 1.0 * l * size / d->size;
+ else
+ i = l;
+ l -= i;
+ while (i-- > 0)
+ Strcat_char(src, '#');
+ while (l-- > 0)
+ Strcat_char(src, '_');
+ Strcat_char(src, '\n');
+ }
+ if (!d->ok && size < d->size)
+ Strcat(src, Sprintf(" %s / %s bytes (%d%%)",
+ convert_size3(size), convert_size3(d->size),
+ (int)(100.0 * size / d->size)));
+ else
+ Strcat(src, Sprintf(" %s bytes loaded", convert_size3(size)));
+ if (duration > 0) {
+ rate = size / duration;
+ Strcat(src, Sprintf(" %02d:%02d:%02d rate %s/sec",
+ duration / (60 * 60), (duration / 60) % 60,
+ duration % 60, convert_size(rate, 1)));
+ if (!d->ok && size < d->size && rate) {
+ eta = (d->size - size) / rate;
+ Strcat(src, Sprintf(" eta %02d:%02d:%02d", eta / (60 * 60),
+ (eta / 60) % 60, eta % 60));
+ }
+ }
+ Strcat_char(src, '\n');
+ if (d->ok) {
+ Strcat(src, Sprintf("<input type=submit name=ok%d value=OK>",
+ d->pid));
+ if (size < d->size)
+ Strcat_charp(src, " Download incompleted");
+ else
+ Strcat_charp(src, " Download completed");
+ }
+ else
+ Strcat(src, Sprintf("<input type=submit name=stop%d value=STOP>",
+ d->pid));
+ Strcat_charp(src, "\n</pre><hr>\n");
+ }
+ Strcat_charp(src, "</form></body></html>");
+ return loadHTMLString(src);
+}
+
+void
+download_action(struct parsed_tagarg *arg)
+{
+ DownloadList *d;
+ pid_t pid;
+
+ for (; arg; arg = arg->next) {
+ if (!strncmp(arg->arg, "stop", 4)) {
+ pid = (pid_t) atoi(&arg->arg[4]);
+ kill(pid, SIGKILL);
+ }
+ else if (!strncmp(arg->arg, "ok", 2))
+ pid = (pid_t) atoi(&arg->arg[2]);
+ else
+ continue;
+ for (d = FirstDL; d; d = d->next) {
+ if (d->pid == pid) {
+ unlink(d->lock);
+ if (d->prev)
+ d->prev->next = d->next;
+ else
+ FirstDL = d->next;
+ if (d->next)
+ d->next->prev = d->prev;
+ else
+ LastDL = d->prev;
+ break;
+ }
+ }
+ }
+ ldDL();
+}
+
+void
+stopDownload(void)
+{
+ DownloadList *d;
+
+ if (!FirstDL)
+ return;
+ for (d = FirstDL; d != NULL; d = d->next) {
+ if (d->ok)
+ continue;
+ kill(d->pid, SIGKILL);
+ unlink(d->lock);
+ }
+}
+
+/* download panel */
+DEFUN(ldDL, DOWNLOAD_LIST, "Display download list panel")
+{
+ Buffer *buf;
+ int replace = FALSE, new_tab = FALSE;
+#ifdef USE_ALARM
+ int reload;
+#endif
+
+ if (Currentbuf->bufferprop & BP_INTERNAL &&
+ !strcmp(Currentbuf->buffername, DOWNLOAD_LIST_TITLE))
+ replace = TRUE;
+ if (!FirstDL) {
+ if (replace) {
+ if (Currentbuf == Firstbuf && Currentbuf->nextBuffer == NULL) {
+ if (nTab > 1)
+ deleteTab(CurrentTab);
+ }
+ else
+ delBuffer(Currentbuf);
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+ }
+ return;
+ }
+#ifdef USE_ALARM
+ reload = checkDownloadList();
+#endif
+ buf = DownloadListBuffer();
+ if (!buf) {
+ displayBuffer(Currentbuf, B_NORMAL);
+ return;
+ }
+ buf->bufferprop |= (BP_INTERNAL | BP_NO_URL);
+ if (replace)
+ restorePosition(buf, Currentbuf);
+ if (!replace && open_tab_dl_list) {
+ _newT();
+ new_tab = TRUE;
+ }
+ pushBuffer(buf);
+ if (replace || new_tab)
+ deletePrevBuf();
+#ifdef USE_ALARM
+ if (reload)
+ Currentbuf->event = setAlarmEvent(Currentbuf->event, 1, AL_IMPLICIT,
+ FUNCNAME_reload, NULL);
+#endif
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+}
+
+static void
+save_buffer_position(Buffer *buf)
+{
+ BufferPos *b = buf->undo;
+
+ if (!buf->firstLine)
+ return;
+ if (b && b->top_linenumber == TOP_LINENUMBER(buf) &&
+ b->cur_linenumber == CUR_LINENUMBER(buf) &&
+ b->currentColumn == buf->currentColumn && b->pos == buf->pos)
+ return;
+ b = New(BufferPos);
+ b->top_linenumber = TOP_LINENUMBER(buf);
+ b->cur_linenumber = CUR_LINENUMBER(buf);
+ b->currentColumn = buf->currentColumn;
+ b->pos = buf->pos;
+ b->bpos = buf->currentLine ? buf->currentLine->bpos : 0;
+ b->next = NULL;
+ b->prev = buf->undo;
+ if (buf->undo)
+ buf->undo->next = b;
+ buf->undo = b;
+}
+
+static void
+resetPos(BufferPos * b)
+{
+ Buffer buf;
+ Line top, cur;
+
+ top.linenumber = b->top_linenumber;
+ cur.linenumber = b->cur_linenumber;
+ cur.bpos = b->bpos;
+ buf.topLine = &top;
+ buf.currentLine = &cur;
+ buf.pos = b->pos;
+ buf.currentColumn = b->currentColumn;
+ restorePosition(Currentbuf, &buf);
+ Currentbuf->undo = b;
+ displayBuffer(Currentbuf, B_FORCE_REDRAW);
+}
+
+DEFUN(undoPos, UNDO, "Cancel the last cursor movement")
+{
+ BufferPos *b = Currentbuf->undo;
+ int i;
+
+ if (!Currentbuf->firstLine)
+ return;
+ if (!b || !b->prev)
+ return;
+ for (i = 0; i < PREC_NUM && b->prev; i++, b = b->prev) ;
+ resetPos(b);
+}
+
+DEFUN(redoPos, REDO, "Cancel the last undo")
+{
+ BufferPos *b = Currentbuf->undo;
+ int i;
+
+ if (!Currentbuf->firstLine)
+ return;
+ if (!b || !b->next)
+ return;
+ for (i = 0; i < PREC_NUM && b->next; i++, b = b->next) ;
+ resetPos(b);
+}