links/main.c
2021-08-28 18:37:32 +03:00

792 lines
19 KiB
C

/* 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;
}