diff options
Diffstat (limited to '')
-rw-r--r-- | main.c | 6597 |
1 files changed, 6597 insertions, 0 deletions
@@ -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 --> %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 = ⊤ + 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); +} |