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 }