links

lynx-like text mode web browser
git clone anongit@rnpnr.xyz:links.git
Log | Files | Refs | Feed | README | LICENSE

main.c (8610B)


      1 /* main.c
      2  * main()
      3  * (c) 2002 Mikulas Patocka
      4  * This file is a part of the Links program, released under GPL.
      5  */
      6 
      7 #include <errno.h>
      8 #include <limits.h>
      9 #include <stdarg.h>
     10 #include <stdio.h>
     11 #include <stdlib.h>
     12 #include <string.h>
     13 
     14 #ifdef __OpenBSD__
     15 	#include <unistd.h>
     16 #else
     17 	#define pledge(a, b) 0
     18 #endif
     19 
     20 #include "links.h"
     21 
     22 int retval = RET_OK;
     23 
     24 static void initialize_all_subsystems(void);
     25 static void initialize_all_subsystems_2(void);
     26 static void poll_fg(void *);
     27 
     28 static int init_b = 0;
     29 int g_argc;
     30 const char *argv0;
     31 char **g_argv;
     32 
     33 void
     34 die(const char *errstr, ...)
     35 {
     36 	va_list ap;
     37 
     38 	va_start(ap, errstr);
     39 	vfprintf(stderr, errstr, ap);
     40 	va_end(ap);
     41 	exit(1);
     42 }
     43 
     44 void
     45 usage(void)
     46 {
     47 	die("usage: %s [options] [url]\n", argv0);
     48 }
     49 
     50 void *
     51 xmalloc(size_t len)
     52 {
     53 	void *p;
     54 
     55 	if (!(p = malloc(len)))
     56 		die("malloc: %s\n", strerror(errno));
     57 
     58 	return p;
     59 }
     60 
     61 void *
     62 xrealloc(void *p, size_t len)
     63 {
     64 	if (!(p = realloc(p, len)))
     65 		die("realloc: %s\n", strerror(errno));
     66 
     67 	return p;
     68 }
     69 
     70 void *
     71 xreallocarray(void *o, size_t n, size_t s)
     72 {
     73 	void *p;
     74 
     75 	if (!(p = reallocarray(o, n, s)))
     76 		die("reallocarray: %s\n", strerror(errno));
     77 
     78 	return p;
     79 }
     80 
     81 static void
     82 sig_intr(void *t_)
     83 {
     84 	struct terminal *t = (struct terminal *)t_;
     85 	if (!t) {
     86 		unhandle_terminal_signals(t);
     87 		terminate_loop = 1;
     88 	} else {
     89 		unhandle_terminal_signals(t);
     90 		exit_prog(t, NULL, NULL);
     91 	}
     92 }
     93 
     94 static void
     95 sig_ctrl_c(void *t_)
     96 {
     97 	if (!is_blocked())
     98 		kbd_ctrl_c();
     99 }
    100 
    101 static void
    102 sig_ign(void *x)
    103 {
    104 }
    105 
    106 static struct timer *fg_poll_timer = NULL;
    107 
    108 void
    109 sig_tstp(void *t_)
    110 {
    111 	struct terminal *t = (struct terminal *)t_;
    112 	pid_t pid, newpid;
    113 	EINTRLOOP(pid, getpid());
    114 	block_itrm(1);
    115 	EINTRLOOP(newpid, fork());
    116 	if (!newpid) {
    117 		while (1) {
    118 			int rr;
    119 			portable_sleep(1000);
    120 			EINTRLOOP(rr, kill(pid, SIGCONT));
    121 		}
    122 	}
    123 	{
    124 		int rr;
    125 		EINTRLOOP(rr, raise(SIGSTOP));
    126 	}
    127 	if (newpid != -1) {
    128 		int rr;
    129 		EINTRLOOP(rr, kill(newpid, SIGKILL));
    130 	}
    131 	if (fg_poll_timer != NULL)
    132 		kill_timer(fg_poll_timer);
    133 	fg_poll_timer = install_timer(FG_POLL_TIME, poll_fg, t);
    134 }
    135 
    136 static void
    137 poll_fg(void *t_)
    138 {
    139 	struct terminal *t = (struct terminal *)t_;
    140 	int r;
    141 	fg_poll_timer = NULL;
    142 	r = unblock_itrm(1);
    143 	if (r == -1)
    144 		fg_poll_timer = install_timer(FG_POLL_TIME, poll_fg, t);
    145 	if (r == -2) {
    146 		/* This will unblock externally spawned viewer, if it exists */
    147 		EINTRLOOP(r, kill(0, SIGCONT));
    148 	}
    149 }
    150 
    151 void
    152 sig_cont(void *t_)
    153 {
    154 	unblock_itrm(1);
    155 }
    156 
    157 static void
    158 handle_basic_signals(struct terminal *term)
    159 {
    160 	install_signal_handler(SIGHUP, sig_intr, term, 0);
    161 	install_signal_handler(SIGINT, sig_ctrl_c, term, 0);
    162 	install_signal_handler(SIGTSTP, sig_tstp, term, 0);
    163 	install_signal_handler(SIGTTIN, sig_tstp, term, 0);
    164 	install_signal_handler(SIGTTOU, sig_ign, term, 0);
    165 	install_signal_handler(SIGCONT, sig_cont, term, 0);
    166 }
    167 
    168 void
    169 unhandle_terminal_signals(struct terminal *term)
    170 {
    171 	install_signal_handler(SIGHUP, NULL, NULL, 0);
    172 	install_signal_handler(SIGINT, NULL, NULL, 0);
    173 	install_signal_handler(SIGTSTP, NULL, NULL, 0);
    174 	install_signal_handler(SIGTTIN, NULL, NULL, 0);
    175 	install_signal_handler(SIGTTOU, NULL, NULL, 0);
    176 	install_signal_handler(SIGCONT, NULL, NULL, 0);
    177 	if (fg_poll_timer != NULL) {
    178 		kill_timer(fg_poll_timer);
    179 		fg_poll_timer = NULL;
    180 	}
    181 }
    182 
    183 int terminal_pipe[2] = { -1, -1 };
    184 
    185 int
    186 attach_terminal(void *info, int len)
    187 {
    188 	struct terminal *term;
    189 	set_nonblock(terminal_pipe[0]);
    190 	set_nonblock(terminal_pipe[1]);
    191 	handle_trm(terminal_pipe[1], info, len);
    192 	free(info);
    193 	if ((term = init_term(terminal_pipe[0], 1, win_func))) {
    194 		handle_basic_signals(
    195 		    term); /* OK, this is race condition, but it must be so; GPM
    196 		              installs it's own buggy TSTP handler */
    197 		return terminal_pipe[1];
    198 	}
    199 	close_socket(&terminal_pipe[0]);
    200 	close_socket(&terminal_pipe[1]);
    201 	return -1;
    202 }
    203 
    204 static struct object_request *dump_obj;
    205 static off_t dump_pos;
    206 
    207 static void
    208 end_dump(struct object_request *r, void *p)
    209 {
    210 	struct cache_entry *ce;
    211 	if (!r->state || (r->state == 1 && dmp != D_SOURCE))
    212 		return;
    213 	ce = r->ce;
    214 	if (dmp == D_SOURCE) {
    215 		if (ce) {
    216 			struct fragment *frag = NULL;
    217 			struct list_head *lfrag;
    218 nextfrag:
    219 			foreach (struct fragment, frag, lfrag, ce->frag)
    220 				if (frag->offset <= dump_pos
    221 				    && frag->offset + frag->length > dump_pos) {
    222 					off_t l;
    223 					int w;
    224 					l = frag->length
    225 					    - (dump_pos - frag->offset);
    226 					if (l >= INT_MAX)
    227 						l = INT_MAX;
    228 					w = hard_write(1,
    229 					               frag->data + dump_pos
    230 					                   - frag->offset,
    231 					               (int)l);
    232 					if (w != l) {
    233 						detach_object_connection(
    234 						    r, dump_pos);
    235 						if (w < 0)
    236 							fprintf(
    237 							    stderr,
    238 							    "Error writing to "
    239 							    "stdout: %s.\n",
    240 							    strerror(errno));
    241 						else
    242 							fprintf(stderr,
    243 							        "Can't write "
    244 							        "to stdout.\n");
    245 						retval = RET_ERROR;
    246 						goto terminate;
    247 					}
    248 					dump_pos += w;
    249 					detach_object_connection(r, dump_pos);
    250 					goto nextfrag;
    251 				}
    252 		}
    253 		if (r->state >= 0)
    254 			return;
    255 	} else if (ce) {
    256 		struct document_options o;
    257 		struct f_data_c *fd;
    258 		int err;
    259 		fd = create_f_data_c(NULL, NULL);
    260 		memset(&o, 0, sizeof(struct document_options));
    261 		o.xp = 0;
    262 		o.yp = 1;
    263 		o.xw = screen_width;
    264 		o.yw = 25;
    265 		o.col = 0;
    266 		o.cp = 0;
    267 		ds2do(&dds, &o, 0);
    268 		o.plain = 0;
    269 		o.frames = 0;
    270 		o.framename = cast_uchar "";
    271 		if (!casecmp(r->url, cast_uchar "file://", 7)
    272 		    && !o.hard_assume) {
    273 			o.assume_cp = 0;
    274 		}
    275 		if (!(fd->f_data =
    276 		          cached_format_html(fd, r, r->url, &o, NULL, 0)))
    277 			goto term_1;
    278 		if ((err = dump_to_file(fd->f_data, 1))) {
    279 			fprintf(stderr, "Error writing to stdout: %s.\n",
    280 			        get_err_msg(err));
    281 			retval = RET_ERROR;
    282 		}
    283 term_1:
    284 		reinit_f_data_c(fd);
    285 		free(fd);
    286 	}
    287 	if (r->state != O_OK) {
    288 		unsigned char *m = get_err_msg(r->stat.state);
    289 		fprintf(stderr, "%s\n", get_text_translation(m, NULL));
    290 		retval = RET_ERROR;
    291 		goto terminate;
    292 	}
    293 terminate:
    294 	terminate_loop = 1;
    295 }
    296 
    297 static void
    298 init(void)
    299 {
    300 	void *info;
    301 	int len;
    302 	unsigned char *u;
    303 
    304 	initialize_all_subsystems();
    305 
    306 	/* OS/2 has some stupid bug and the pipe must be created before socket
    307 	 * :-/ */
    308 	if (c_pipe(terminal_pipe)) {
    309 		fatal_exit(
    310 		    "ERROR: can't create pipe for internal communication");
    311 	}
    312 	if (!(u = parse_options(g_argc - 1, g_argv + 1))) {
    313 		retval = RET_SYNTAX;
    314 		goto ttt;
    315 	}
    316 	dds.assume_cp = 0;
    317 	load_config();
    318 	if (proxies.only_proxies)
    319 		reset_settings_for_tor();
    320 	if (!u) {
    321 ttt:
    322 		initialize_all_subsystems_2();
    323 tttt:
    324 		terminate_loop = 1;
    325 		return;
    326 	}
    327 	init_cookies();
    328 	if (!dmp) {
    329 		init_b = 1;
    330 		init_bookmarks();
    331 		create_initial_extensions();
    332 		load_url_history();
    333 		initialize_all_subsystems_2();
    334 		info =
    335 		    create_session_info(base_session, u, default_target, &len);
    336 		if (attach_terminal(info, len) < 0)
    337 			fatal_exit("Could not open initial session");
    338 	} else {
    339 		unsigned char *uu, *uuu, *wd;
    340 		initialize_all_subsystems_2();
    341 		close_socket(&terminal_pipe[0]);
    342 		close_socket(&terminal_pipe[1]);
    343 		if (!*u) {
    344 			fprintf(stderr, "URL expected after %s\n",
    345 			        dmp == D_DUMP ? "-dump" : "-source");
    346 			retval = RET_SYNTAX;
    347 			goto tttt;
    348 		}
    349 		uu = stracpy(u);
    350 		if (!(uuu = translate_url(uu, wd = get_cwd())))
    351 			uuu = stracpy(uu);
    352 		free(uu);
    353 		request_object(NULL, uuu, NULL, PRI_MAIN, NC_RELOAD, ALLOW_ALL,
    354 		               end_dump, NULL, &dump_obj);
    355 		free(uuu);
    356 		free(wd);
    357 	}
    358 }
    359 
    360 /* Is called before gaphics driver init */
    361 static void
    362 initialize_all_subsystems(void)
    363 {
    364 	set_sigcld();
    365 	init_home();
    366 	init_dns();
    367 	init_session_cache();
    368 	init_cache();
    369 	memset(&dd_opt, 0, sizeof dd_opt);
    370 }
    371 
    372 /* Is called sometimes after and sometimes before graphics driver init */
    373 static void
    374 initialize_all_subsystems_2(void)
    375 {
    376 	init_fcache();
    377 }
    378 
    379 static void
    380 terminate_all_subsystems(void)
    381 {
    382 	check_bottom_halves();
    383 	abort_all_downloads();
    384 	check_bottom_halves();
    385 	destroy_all_terminals();
    386 	check_bottom_halves();
    387 	free_all_itrms();
    388 	release_object(&dump_obj);
    389 	abort_all_connections();
    390 
    391 	free_all_caches();
    392 	ssl_finish();
    393 	if (init_b)
    394 		save_url_history();
    395 	free_history_lists();
    396 	free_term_specs();
    397 	free_types();
    398 	finalize_bookmarks();
    399 	free_blacklist();
    400 	free_cookies();
    401 	free_auth();
    402 	check_bottom_halves();
    403 	end_config();
    404 	free_strerror_buf();
    405 	os_free_clipboard();
    406 	if (fg_poll_timer != NULL) {
    407 		kill_timer(fg_poll_timer);
    408 		fg_poll_timer = NULL;
    409 	}
    410 	terminate_select();
    411 }
    412 
    413 int
    414 main(int argc, char *argv[])
    415 {
    416 	g_argc = argc;
    417 	g_argv = argv;
    418 	argv0 = argv[0];
    419 
    420 	if (pledge("stdio rpath wpath cpath inet dns tty unix", NULL) < 0)
    421 		die("pledge: %s\n", strerror(errno));
    422 
    423 	init_page_size();
    424 	select_loop(init);
    425 	terminate_all_subsystems();
    426 
    427 	return retval;
    428 }