/* main.c * main() * (c) 2002 Mikulas Patocka * This file is a part of the Links program, released under GPL. */ #include "links.h" int retval = RET_OK; static void unhandle_basic_signals(struct terminal *); static void poll_fg(void *); #ifdef WIN static void sig_terminate(void *t_) { struct terminal *t = (struct terminal *)t_; unhandle_basic_signals(t); terminate_loop = 1; retval = RET_SIGNAL; } #endif static void sig_intr(void *t_) { struct terminal *t = (struct terminal *)t_; if (!t) { unhandle_basic_signals(t); terminate_loop = 1; } else { unhandle_basic_signals(t); exit_prog(t, NULL, NULL); } } static void sig_ctrl_c(void *t_) { if (!is_blocked()) kbd_ctrl_c(); } #ifdef SIGTTOU static void sig_ign(void *x) { } #endif static struct timer *fg_poll_timer = NULL; void sig_tstp(void *t_) { struct terminal *t = (struct terminal *)t_; #if defined(SIGSTOP) && !defined(NO_CTRL_Z) #if defined(SIGCONT) && defined(SIGTTOU) && defined(HAVE_GETPID) pid_t pid, newpid; EINTRLOOP(pid, getpid()); #endif if (!F) block_itrm(1); #ifdef G else if (drv->block) drv->block(NULL); #endif #if defined(SIGCONT) && defined(SIGTTOU) && defined(HAVE_GETPID) EINTRLOOP(newpid, fork()); if (!newpid) { while (1) { int rr; portable_sleep(1000); EINTRLOOP(rr, kill(pid, SIGCONT)); } } #endif { int rr; EINTRLOOP(rr, raise(SIGSTOP)); } #if defined(SIGCONT) && defined(SIGTTOU) && defined(HAVE_GETPID) if (newpid != -1) { int rr; EINTRLOOP(rr, kill(newpid, SIGKILL)); } #endif #endif if (fg_poll_timer != NULL) kill_timer(fg_poll_timer); fg_poll_timer = install_timer(FG_POLL_TIME, poll_fg, t); } static void poll_fg(void *t_) { struct terminal *t = (struct terminal *)t_; int r = 0; fg_poll_timer = NULL; if (!F) r = unblock_itrm(1); #ifdef G else if (drv->unblock) r = drv->unblock(NULL); #endif if (r == -1) { fg_poll_timer = install_timer(FG_POLL_TIME, poll_fg, t); } if (r == -2) { /* This will unblock externally spawned viewer, if it exists */ #ifdef SIGCONT EINTRLOOP(r, kill(0, SIGCONT)); #endif } } void sig_cont(void *t_) { if (!F) unblock_itrm(1); #ifdef G else if (drv->unblock) drv->unblock(NULL); #endif } static void handle_basic_signals(struct terminal *term) { install_signal_handler(SIGHUP, sig_intr, term, 0); if (!F) install_signal_handler(SIGINT, sig_ctrl_c, term, 0); /*install_signal_handler(SIGTERM, sig_terminate, term, 0);*/ #ifdef WIN install_signal_handler(SIGQUIT, sig_terminate, term, 0); #endif #ifdef SIGTSTP if (!F) install_signal_handler(SIGTSTP, sig_tstp, term, 0); #endif #ifdef SIGTTIN if (!F) install_signal_handler(SIGTTIN, sig_tstp, term, 0); #endif #ifdef SIGTTOU install_signal_handler(SIGTTOU, sig_ign, term, 0); #endif #ifdef SIGCONT if (!F) install_signal_handler(SIGCONT, sig_cont, term, 0); #endif } void unhandle_terminal_signals(struct terminal *term) { install_signal_handler(SIGHUP, NULL, NULL, 0); if (!F) install_signal_handler(SIGINT, NULL, NULL, 0); #ifdef SIGTSTP install_signal_handler(SIGTSTP, NULL, NULL, 0); #endif #ifdef SIGTTIN install_signal_handler(SIGTTIN, NULL, NULL, 0); #endif #ifdef SIGTTOU install_signal_handler(SIGTTOU, NULL, NULL, 0); #endif #ifdef SIGCONT install_signal_handler(SIGCONT, NULL, NULL, 0); #endif if (fg_poll_timer != NULL) kill_timer(fg_poll_timer), fg_poll_timer = NULL; } static void unhandle_basic_signals(struct terminal *term) { install_signal_handler(SIGHUP, NULL, NULL, 0); if (!F) install_signal_handler(SIGINT, NULL, NULL, 0); /*install_signal_handler(SIGTERM, NULL, NULL, 0);*/ #ifdef SIGTSTP install_signal_handler(SIGTSTP, NULL, NULL, 0); #endif #ifdef SIGTTIN install_signal_handler(SIGTTIN, NULL, NULL, 0); #endif #ifdef SIGTTOU install_signal_handler(SIGTTOU, NULL, NULL, 0); #endif #ifdef SIGCONT install_signal_handler(SIGCONT, NULL, NULL, 0); #endif if (fg_poll_timer != NULL) kill_timer(fg_poll_timer), fg_poll_timer = NULL; } int terminal_pipe[2] = { -1, -1 }; int attach_terminal(int in, int out, int ctl, void *info, int len) { struct terminal *term; set_nonblock(terminal_pipe[0]); set_nonblock(terminal_pipe[1]); handle_trm(in, out, out, terminal_pipe[1], ctl, info, len); mem_free(info); if ((term = init_term(terminal_pipe[0], out, win_func))) { handle_basic_signals(term); /* OK, this is race condition, but it must be so; GPM installs it's own buggy TSTP handler */ return terminal_pipe[1]; } close_socket(&terminal_pipe[0]); close_socket(&terminal_pipe[1]); return -1; } #ifdef G int attach_g_terminal(unsigned char *cwd, void *info, int len) { struct terminal *term; term = init_gfx_term(win_func, cwd, info, len); mem_free(info); return term ? 0 : -1; } void gfx_connection(int h) { int r; unsigned char cwd[MAX_CWD_LEN]; unsigned char hold_conn; void *info; int info_len; struct terminal *term; if (os_send_fg_cookie(h)) goto err_close; if (hard_read(h, cwd, MAX_CWD_LEN) != MAX_CWD_LEN) goto err_close; cwd[MAX_CWD_LEN - 1] = 0; if (hard_read(h, &hold_conn, 1) != 1) goto err_close; if (hard_read(h, (unsigned char *)&info_len, sizeof(int)) != sizeof(int) || info_len < 0) goto err_close; info = mem_alloc(info_len); if (hard_read(h, info, info_len) != info_len) goto err_close_free; term = init_gfx_term(win_func, cwd, info, info_len); if (term) { if (hold_conn) { term->handle_to_close = h; } else { hard_write(h, cast_uchar "x", 1); EINTRLOOP(r, close(h)); } mem_free(info); return; } err_close_free: mem_free(info); err_close: EINTRLOOP(r, close(h)); } static void gfx_connection_terminate(void *p) { int h = (int)(my_intptr_t)p; set_handlers(h, NULL, NULL, NULL); terminate_loop = 1; } #endif static struct object_request *dump_obj; static off_t dump_pos; static void end_dump(struct object_request *r, void *p) { struct cache_entry *ce; int oh; if (!r->state || (r->state == 1 && dmp != D_SOURCE)) return; if ((oh = get_output_handle()) == -1) return; ce = r->ce; if (dmp == D_SOURCE) { if (ce) { struct fragment *frag; struct list_head *lfrag; nextfrag: foreach(struct fragment, frag, lfrag, ce->frag) if (frag->offset <= dump_pos && frag->offset + frag->length > dump_pos) { off_t l; int w; l = frag->length - (dump_pos - frag->offset); if (l >= MAXINT) l = MAXINT; w = hard_write(oh, frag->data + dump_pos - frag->offset, (int)l); if (w != l) { unsigned char *msg = strerror_alloc(errno, NULL); detach_object_connection(r, dump_pos); if (w < 0) fprintf(stderr, "Error writing to stdout: %s.\n", msg); else fprintf(stderr, "Can't write to stdout.\n"); mem_free(msg); retval = RET_ERROR; goto terminate; } dump_pos += w; detach_object_connection(r, dump_pos); goto nextfrag; } } if (r->state >= 0) return; } else if (ce) { struct document_options o; struct f_data_c *fd; int err; fd = create_f_data_c(NULL, NULL); memset(&o, 0, sizeof(struct document_options)); o.xp = 0; o.yp = 1; o.xw = screen_width; o.yw = 25; o.col = 0; o.cp = get_commandline_charset(); ds2do(&dds, &o, 0); o.plain = 0; o.frames = 0; o.js_enable = 0; o.framename = cast_uchar ""; if (!casecmp(r->url, cast_uchar "file://", 7) && !o.hard_assume) { o.assume_cp = get_commandline_charset(); } if (!(fd->f_data = cached_format_html(fd, r, r->url, &o, NULL, 0))) goto term_1; if ((err = dump_to_file(fd->f_data, oh))) { fprintf(stderr, "Error writing to stdout: %s.\n", get_err_msg(err, NULL)); retval = RET_ERROR; } term_1: reinit_f_data_c(fd); mem_free(fd); } if (r->state != O_OK) { unsigned char *m = get_err_msg(r->stat.state, NULL); fprintf(stderr, "%s\n", get_english_translation(m)); retval = RET_ERROR; goto terminate; } terminate: terminate_loop = 1; } int g_argc; char **g_argv; unsigned char *path_to_exe; static unsigned char init_b = 0; static void initialize_all_subsystems(void); static void initialize_all_subsystems_2(void); static void fixup_g(void) { if (ggr_drv[0] || ggr_mode[0] || force_g) ggr = 1; if (dmp) ggr = 0; } static void init(void) { int uh; void *info; int len; unsigned char *u; initialize_all_subsystems(); /* OS/2 has some stupid bug and the pipe must be created before socket :-/ */ if (c_pipe(terminal_pipe)) { fatal_exit("ERROR: can't create pipe for internal communication"); } if (!(u = parse_options(g_argc - 1, g_argv + 1))) { retval = RET_SYNTAX; goto ttt; } fixup_g(); if (!dmp && !ggr) { init_os_terminal(); } if (!ggr && !no_connect && (uh = bind_to_af_unix(NULL)) != -1) { close_socket(&terminal_pipe[0]); close_socket(&terminal_pipe[1]); info = create_session_info(base_session, u, default_target, &len); initialize_all_subsystems_2(); handle_trm(get_input_handle(), get_output_handle(), uh, uh, get_ctl_handle(), info, len); handle_basic_signals(NULL); /* OK, this is race condition, but it must be so; GPM installs it's own buggy TSTP handler */ mem_free(info); #if defined(HAVE_MALLOC_TRIM) malloc_trim(8192); #endif return; } if ((dds.assume_cp = get_cp_index(cast_uchar "ISO-8859-1")) == -1) dds.assume_cp = 0; load_config(); if (proxies.only_proxies) reset_settings_for_tor(); u = parse_options(g_argc - 1, g_argv + 1); fixup_g(); if (!u) { ttt: initialize_all_subsystems_2(); tttt: terminate_loop = 1; return; } init_cookies(); if (!dmp) { if (ggr) { close_socket(&terminal_pipe[0]); close_socket(&terminal_pipe[1]); #ifdef G { unsigned char *r; if ((r = init_graphics(ggr_drv, ggr_mode, ggr_display))) { fprintf(stderr, "%s", r); mem_free(r); retval = RET_SYNTAX; goto ttt; } handle_basic_signals(NULL); if (drv->get_af_unix_name && !no_connect) { unsigned char *n = stracpy(drv->name); unsigned char *nn = drv->get_af_unix_name(); if (*nn) { add_to_strn(&n, cast_uchar "-"); add_to_strn(&n, nn); } uh = bind_to_af_unix(n); mem_free(n); if (uh != -1) { unsigned char hold_conn; unsigned char *w; int lw; shutdown_graphics(); if (os_receive_fg_cookie(uh)) { retval = RET_ERROR; goto ttt; } w = get_cwd(); if (!w) w = stracpy(cast_uchar ""); if (strlen(cast_const_char w) >= MAX_CWD_LEN) w[MAX_CWD_LEN - 1] = 0; lw = (int)strlen(cast_const_char w) + 1; if (hard_write(uh, w, lw) != lw) { mem_free(w); retval = RET_ERROR; goto ttt; } mem_free(w); w = mem_calloc(MAX_CWD_LEN - lw); if (hard_write(uh, w, MAX_CWD_LEN - lw) != MAX_CWD_LEN - lw) { mem_free(w); retval = RET_ERROR; goto ttt; } mem_free(w); hold_conn = *u != 0; if (hard_write(uh, &hold_conn, 1) != 1) { retval = RET_ERROR; goto ttt; } info = create_session_info(base_session, u, default_target, &len); if (hard_write(uh, (unsigned char *)&len, sizeof len) != sizeof len) { mem_free(info); retval = RET_ERROR; goto ttt; } if (hard_write(uh, info, len) != len) { mem_free(info); retval = RET_ERROR; goto ttt; } mem_free(info); set_handlers(uh, gfx_connection_terminate, NULL, (void *)(my_intptr_t)uh); initialize_all_subsystems_2(); heap_trim(); return; } } init_dither(drv->depth); fontconfig_init(); freetype_init(); } #else fprintf(stderr, "Graphics not enabled when compiling\n"); retval = RET_SYNTAX; goto ttt; #endif } init_b = 1; init_bookmarks(); create_initial_extensions(); load_url_history(); initialize_all_subsystems_2(); info = create_session_info(base_session, u, default_target, &len); if (!F) { if (attach_terminal(get_input_handle(), get_output_handle(), get_ctl_handle(), info, len) < 0) fatal_exit("Could not open initial session"); } #ifdef G else { unsigned char *cwd = get_cwd(); if (!cwd) cwd = stracpy(cast_uchar ""); if (attach_g_terminal(cwd, info, len) < 0) fatal_exit("Could not open initial session"); mem_free(cwd); } #endif } else { unsigned char *uu, *uuu, *wd; initialize_all_subsystems_2(); close_socket(&terminal_pipe[0]); close_socket(&terminal_pipe[1]); if (!*u) { fprintf(stderr, "URL expected after %s\n", dmp == D_DUMP ? "-dump" : "-source"); retval = RET_SYNTAX; goto tttt; } uu = convert(get_commandline_charset(), utf8_table, u, NULL); if (!(uuu = translate_url(uu, wd = get_cwd()))) uuu = stracpy(uu); mem_free(uu); request_object(NULL, uuu, NULL, PRI_MAIN, NC_RELOAD, ALLOW_ALL, end_dump, NULL, &dump_obj); mem_free(uuu); if (wd) mem_free(wd); } } /* Is called before gaphics driver init */ static void initialize_all_subsystems(void) { init_charset(); init_trans(); set_sigcld(); init_home(); init_dns(); init_session_cache(); init_cache(); init_blocks(); memset(&dd_opt, 0, sizeof dd_opt); } /* Is called sometimes after and sometimes before graphics driver init */ static void initialize_all_subsystems_2(void) { GF(init_dip()); init_bfu(); GF(init_imgcache()); init_fcache(); GF(init_grview()); } static void terminate_all_subsystems(void) { check_bottom_halves(); abort_all_downloads(); check_bottom_halves(); destroy_all_terminals(); check_bottom_halves(); shutdown_bfu(); if (!F) free_all_itrms(); release_object(&dump_obj); abort_all_connections(); free_all_caches(); #ifdef HAVE_SSL ssl_finish(); #endif if (init_b) save_url_history(); free_history_lists(); free_term_specs(); free_types(); free_blocks(); finalize_bookmarks(); free_conv_table(); free_blacklist(); do_save_cookies(); free_cookies(); free_auth(); check_bottom_halves(); end_config(); free_strerror_buf(); shutdown_trans(); GF(freetype_done()); GF(free_dither()); GF(shutdown_graphics()); af_unix_close(); os_free_clipboard(); if (fg_poll_timer != NULL) kill_timer(fg_poll_timer), fg_poll_timer = NULL; terminate_select(); terminate_osdep(); } int main(int argc, char *argv[]) { g_argc = argc; g_argv = (char **)argv; /* we fix this up in init_os in vms.c */ #if 0 if (argc != 3) fprintf(stderr, "two args expected\n"), exit(1); printf("cookie %saccepted\n", allow_cookie_domain(cast_uchar argv[1], cast_uchar argv[2]) ? "" : "not "); return 0; #endif #if 0 { unsigned char a[216]; double dist[216]; int i; memset(&a, 0, sizeof a); for (i = 0; i < 216; i++) dist[i] = 1e30; for (i = 0; i < 216; i++) { int j, b = -1; double maxdist = 0; for (j = 0; j < 216; j++) { if ((j / 36 == 0 || j / 36 == 5) && (j / 6 % 6 == 0 || j / 6 % 6 == 5) && (j % 6 == 0 || j % 6 == 5)) if (dist[j] && dist[j] > maxdist) { maxdist = dist[j]; b = j; } } if (b == -1) for (j = 0; j < 216; j++) { if (dist[j] > maxdist) { maxdist = dist[j]; b = j; } } for (j = 0; j < 216; j++) { double d; unsigned rgb1[3]; unsigned rgb2[3]; q_palette(216, b, 65535, rgb1); q_palette(216, j, 65535, rgb2); d = rgb_distance(rgb1[0], rgb1[1], rgb1[2], rgb2[0], rgb2[1], rgb2[2]); if (d < dist[j]) dist[j] = d; } fprintf(stderr, "%d: (%d, %d, %d)\n", b, b / 36, b / 6 % 6, b % 6); } return 0; } #endif init_page_size(); init_heap(); init_os(); get_path_to_exe(); #if 0 { int i; int ix, iy, ox, oy, rep; ulonglong tm = 0; parse_options(g_argc - 1, g_argv + 1); ix = getenv("SRC_X") ? atoi(getenv("SRC_X")) : 100; iy = getenv("SRC_Y") ? atoi(getenv("SRC_Y")) : ix; ox = getenv("DST_X") ? atoi(getenv("DST_X")) : 100; oy = getenv("DST_Y") ? atoi(getenv("DST_Y")) : ox; rep = getenv("REP") ? atoi(getenv("REP")) : 1; for (i = 0; i <= rep; i++) { unsigned short *dst; unsigned short *src; struct timeval tv1, tv2; src = mem_alloc(sizeof(unsigned short) * ix * iy * 3); memset(src, 0x12, sizeof(unsigned short) * ix * iy * 3); gettimeofday(&tv1, NULL); scale_color(src, ix, iy, &dst, ox, oy); gettimeofday(&tv2, NULL); if (dst) mem_free(dst); if (i) tm += ((ulonglong)tv2.tv_sec * 1000000 + tv2.tv_usec) - ((ulonglong)tv1.tv_sec * 1000000 + tv1.tv_usec); } fprintf(stderr, "time: %f\n", (double)tm / 1000 / rep); check_memory_leaks(); return 0; } #endif #if 0 { int i; for (i = 0; i < 100; i++) { unsigned char *a = mem_calloc(i); unsigned char *b = base64_encode(a, i, cast_uchar "", cast_uchar "", 5); fprintf(stderr, "X:\n%.*s\n", (int)strlen(cast_const_char b), b); mem_free(a); mem_free(b); } check_memory_leaks(); return 0; } #endif #if 0 { unsigned char *puny_encode(unsigned char *s); unsigned char *puny_decode(unsigned char *s); int i; for (i = 1; i < g_argc; i++) { unsigned char *str = puny_encode(cast_uchar g_argv[i]); if (str) { fprintf(stderr, "'%s'\n", str); unsigned char *d = puny_decode(str); if (!d || strcmp(cast_const_char d, cast_const_char g_argv[i])) { internal_error("mismatch - %s", d); } mem_free(str); mem_free(d); } } check_memory_leaks(); return 0; } #endif #if 0 while (1) { #define maxn 12 static unsigned long long count = 0; unsigned char *puny_encode(unsigned char *s, int len); unsigned char *puny_decode(unsigned char *s, int len); int punycode_encode(size_t input_length, const uint32_t input[], const unsigned char case_flags[], size_t *output_length, char output[]); int punycode_decode(size_t input_length, const char input[], size_t * output_length, uint32_t output[], unsigned char case_flags[]); uint32_t unistr[maxn]; unsigned i, n; unsigned char *utfstr, *puny, *dec; int utfstr_l; char pce_o[60]; size_t pce_ol; int pce_s; n = random() % (maxn + 1); for (i = 0; i < n; i++) { uint32_t uni; uni = random() % (0x10FFFF + 1); if (uni >= 0xd800 && uni <= 0xdfff) uni = random() % 128; while (uni < 128 && !( uni == '-' || (uni >= '0' && uni <= '9') || (uni >= 'A' && uni <= 'Z') || (uni >= 'a' && uni <= 'z'))) { uni = random() % 128; } unistr[i] = uni; } utfstr = init_str(); utfstr_l = 0; for (i = 0; i < n; i++) { unsigned char *us = encode_utf_8(unistr[i]); add_to_str(&utfstr, &utfstr_l, us); } puny = puny_encode(utfstr, utfstr_l); if (!puny) { fprintf(stderr, "failed:"); goto err; } dec = puny_decode(puny, (int)strlen(cast_const_char puny)); if (!dec) dec = stracpy(puny); if (strcmp(cast_const_char utfstr, cast_const_char dec)) { fprintf(stderr, "mismatch(%s,%s):", utfstr, dec); goto err; } pce_ol = 59; pce_s = punycode_encode(n, unistr, NULL, &pce_ol, pce_o); if (pce_s) { fprintf(stderr, "punycode_encode(%d):", pce_s); goto err; } pce_o[pce_ol] = 0; if (!strncmp(cast_const_char puny, "xn--", 4) && strcmp(cast_const_char puny + 4, pce_o)) { fprintf(stderr, "punycode_encode differs(%s,%s):", puny, pce_o); goto err; } mem_free(dec); mem_free(puny); mem_free(utfstr); if (0) { err: for (i = 0; i < n; i++) { fprintf(stderr, " %u", unistr[i]); } internal_error("failed"); } count++; if (!(count % 10000)) { printf("%llu\r", count); fflush(stdout); } } #endif select_loop(init); terminate_all_subsystems(); #if 0 { char msg[65536] = ""; char *p = msg; int i = 1; while (p - msg < 60000) { sprintf(p, "some text %d (%ld) ", i++, p - msg); p = strchr(p, 0); } error(msg); } #endif check_memory_leaks(); return retval; }