Bug 1082: mem_free_set in abort_preloading
[elinks.git] / src / terminal / terminal.c
blob8d263150410743dc598ee9bb4bac747a77053eae
1 /** Terminal interface - low-level displaying implementation.
2 * @file */
4 #ifdef HAVE_CONFIG_H
5 #include "config.h"
6 #endif
8 #include <stdlib.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include <sys/types.h>
12 #ifdef HAVE_UNISTD_H
13 #include <unistd.h>
14 #endif
16 #include "elinks.h"
18 #include "bookmarks/bookmarks.h"
19 #include "config/options.h"
20 #include "main/main.h"
21 #include "main/module.h"
22 #include "main/object.h"
23 #include "main/select.h"
24 #include "osdep/osdep.h"
25 #include "osdep/signals.h"
26 #include "session/session.h"
27 #include "terminal/draw.h"
28 #include "terminal/event.h"
29 #include "terminal/hardio.h"
30 #include "terminal/kbd.h"
31 #include "terminal/screen.h"
32 #include "terminal/terminal.h"
33 #include "terminal/window.h"
34 #include "util/error.h"
35 #include "util/memory.h"
36 #include "util/string.h"
37 #include "viewer/text/textarea.h"
40 INIT_LIST_OF(struct terminal, terminals);
42 static void check_if_no_terminal(void);
44 void
45 redraw_terminal(struct terminal *term)
47 struct term_event ev;
49 set_redraw_term_event(&ev, term->width, term->height);
50 term_send_event(term, &ev);
53 void
54 redraw_terminal_cls(struct terminal *term)
56 struct term_event ev;
58 set_resize_term_event(&ev, term->width, term->height);
59 term_send_event(term, &ev);
62 void
63 cls_redraw_all_terminals(void)
65 struct terminal *term;
67 foreach (term, terminals)
68 redraw_terminal_cls(term);
71 /** Get the terminal in which message boxes should be displayed, if
72 * there is no specific reason to use some other terminal. This
73 * returns NULL if all terminals have been closed. (ELinks keeps
74 * running anyway if ui.sessions.keep_session_active is true.) */
75 struct terminal *
76 get_default_terminal(void)
78 if (list_empty(terminals))
79 return NULL;
80 else
81 return terminals.next;
84 struct terminal *
85 init_term(int fdin, int fdout)
87 unsigned char name[MAX_TERM_LEN + 9] = "terminal.";
88 struct terminal *term = mem_calloc(1, sizeof(*term));
90 if (!term) {
91 check_if_no_terminal();
92 return NULL;
95 term->screen = init_screen();
96 if (!term->screen) {
97 mem_free(term);
98 return NULL;
101 init_list(term->windows);
103 term->fdin = fdin;
104 term->fdout = fdout;
105 term->master = (term->fdout == get_output_handle());
106 term->blocked = -1;
108 get_terminal_name(name + 9);
109 term->spec = get_opt_rec(config_options, name);
110 object_lock(term->spec);
112 /* It's a new terminal, so assume the user is using it right now,
113 * and sort it to the front of the list. */
114 add_to_list(terminals, term);
116 set_handlers(fdin, (select_handler_T) in_term, NULL,
117 (select_handler_T) destroy_terminal, term);
118 return term;
121 /** Get the codepage of a terminal. The UTF-8 I/O option does not
122 * affect this.
124 * @todo Perhaps cache the value in struct terminal?
126 * @bug Bug 1064: If the charset has been set as "System", this should
127 * apply the locale environment variables of the slave ELinks process,
128 * not those of the master ELinks process that parsed the configuration
129 * file. That is why the parameter points to struct terminal and not
130 * merely to its option tree (term->spec).
132 * @see get_translation_table(), get_cp_mime_name() */
134 get_terminal_codepage(const struct terminal *term)
136 return get_opt_codepage_tree(term->spec, "charset", NULL);
139 void
140 redraw_all_terminals(void)
142 struct terminal *term;
144 foreach (term, terminals)
145 redraw_screen(term);
148 void
149 destroy_terminal(struct terminal *term)
151 #ifdef CONFIG_BOOKMARKS
152 bookmark_auto_save_tabs(term);
153 #endif
154 detach_downloads_from_terminal(term);
156 free_textarea_data(term);
158 /* delete_window doesn't update term->current_tab, but it
159 calls redraw_terminal, which requires term->current_tab
160 to be valid if there are any tabs left. So set a value
161 that will be valid for that long. */
162 term->current_tab = 0;
164 while (!list_empty(term->windows))
165 delete_window(term->windows.next);
167 /* mem_free_if(term->cwd); */
168 mem_free_if(term->title);
169 if (term->screen) done_screen(term->screen);
171 clear_handlers(term->fdin);
172 mem_free_if(term->interlink);
174 if (term->blocked != -1) {
175 close(term->blocked);
176 clear_handlers(term->blocked);
179 del_from_list(term);
180 close(term->fdin);
182 if (term->fdout != 1) {
183 if (term->fdout != term->fdin) close(term->fdout);
184 } else {
185 unhandle_terminal_signals(term);
186 free_all_itrms();
187 #ifndef NO_FORK_ON_EXIT
188 if (!list_empty(terminals)) {
189 if (fork()) exit(0);
191 #endif
194 object_unlock(term->spec);
195 mem_free(term);
196 check_if_no_terminal();
199 void
200 destroy_all_terminals(void)
202 while (!list_empty(terminals))
203 destroy_terminal(terminals.next);
206 static void
207 check_if_no_terminal(void)
209 program.terminate = list_empty(terminals)
210 && !get_opt_bool("ui.sessions.keep_session_active", NULL);
213 void
214 exec_thread(unsigned char *path, int p)
216 int plen = strlen(path + 1) + 2;
218 #if defined(HAVE_SETPGID) && !defined(CONFIG_OS_BEOS) && !defined(HAVE_BEGINTHREAD)
219 if (path[0] == TERM_EXEC_NEWWIN) setpgid(0, 0);
220 #endif
221 exe(path + 1);
222 if (path[plen]) unlink(path + plen);
225 void
226 close_handle(void *h)
228 close((long) h);
229 clear_handlers((long) h);
232 static void
233 unblock_terminal(struct terminal *term)
235 close_handle((void *) (long) term->blocked);
236 term->blocked = -1;
237 set_handlers(term->fdin, (select_handler_T) in_term, NULL,
238 (select_handler_T) destroy_terminal, term);
239 unblock_itrm();
240 redraw_terminal_cls(term);
241 if (term->textarea_data) /* XXX */
242 textarea_edit(1, term, NULL, NULL, NULL);
245 #ifndef CONFIG_FASTMEM
246 void
247 assert_terminal_ptr_not_dangling(const struct terminal *suspect)
249 struct terminal *term;
251 if (suspect == NULL)
252 return;
254 foreach (term, terminals) {
255 if (term == suspect)
256 return;
259 assertm(0, "Dangling pointer to struct terminal");
261 #endif /* !CONFIG_FASTMEM */
263 static void
264 exec_on_master_terminal(struct terminal *term,
265 unsigned char *path, int plen,
266 unsigned char *delete, int dlen,
267 enum term_exec fg)
269 int blockh;
270 int param_size = plen + dlen + 2 /* 2 null char */ + 1 /* fg */;
271 unsigned char *param = fmem_alloc(param_size);
273 if (!param) return;
275 param[0] = fg;
276 memcpy(param + 1, path, plen + 1);
277 memcpy(param + 1 + plen + 1, delete, dlen + 1);
279 if (fg == TERM_EXEC_FG) block_itrm();
281 blockh = start_thread((void (*)(void *, int)) exec_thread,
282 param, param_size);
283 fmem_free(param);
284 if (blockh == -1) {
285 if (fg == TERM_EXEC_FG) unblock_itrm();
286 return;
289 if (fg == TERM_EXEC_FG) {
290 term->blocked = blockh;
291 set_handlers(blockh,
292 (select_handler_T) unblock_terminal,
293 NULL,
294 (select_handler_T) unblock_terminal,
295 term);
296 set_handlers(term->fdin, NULL, NULL,
297 (select_handler_T) destroy_terminal,
298 term);
300 } else {
301 set_handlers(blockh, close_handle, NULL,
302 close_handle, (void *) (long) blockh);
306 static void
307 exec_on_slave_terminal( struct terminal *term,
308 unsigned char *path, int plen,
309 unsigned char *delete, int dlen,
310 enum term_exec fg)
312 int data_size = plen + dlen + 1 /* 0 */ + 1 /* fg */ + 2 /* 2 null char */;
313 unsigned char *data = fmem_alloc(data_size);
315 if (!data) return;
317 data[0] = 0;
318 data[1] = fg;
319 memcpy(data + 2, path, plen + 1);
320 memcpy(data + 2 + plen + 1, delete, dlen + 1);
321 hard_write(term->fdout, data, data_size);
322 fmem_free(data);
325 void
326 exec_on_terminal(struct terminal *term, unsigned char *path,
327 unsigned char *delete, enum term_exec fg)
329 if (path) {
330 if (!*path) return;
331 } else {
332 path = "";
335 #ifdef NO_FG_EXEC
336 fg = TERM_EXEC_BG;
337 #endif
339 if (term->master) {
340 if (!*path) {
341 dispatch_special(delete);
342 return;
345 /* TODO: Should this be changed to allow TERM_EXEC_NEWWIN
346 * in a blocked terminal? There is similar code in
347 * in_sock(). --KON, 2007 */
348 if (fg != TERM_EXEC_BG && is_blocked()) {
349 unlink(delete);
350 return;
353 exec_on_master_terminal(term,
354 path, strlen(path),
355 delete, strlen(delete),
356 fg);
357 } else {
358 exec_on_slave_terminal( term,
359 path, strlen(path),
360 delete, strlen(delete),
361 fg);
365 void
366 exec_shell(struct terminal *term)
368 unsigned char *sh;
370 if (!can_open_os_shell(term->environment)) return;
372 sh = get_shell();
373 if (sh && *sh)
374 exec_on_terminal(term, sh, "", TERM_EXEC_FG);
378 void
379 do_terminal_function(struct terminal *term, unsigned char code,
380 unsigned char *data)
382 int data_len = strlen(data);
383 unsigned char *x_data = fmem_alloc(data_len + 1 /* code */ + 1 /* null char */);
385 if (!x_data) return;
386 x_data[0] = code;
387 memcpy(x_data + 1, data, data_len + 1);
388 exec_on_terminal(term, NULL, x_data, TERM_EXEC_BG);
389 fmem_free(x_data);
392 /** @return negative on error; zero or positive on success. */
394 set_terminal_title(struct terminal *term, unsigned char *title)
396 int from_cp;
397 int to_cp;
398 unsigned char *converted = NULL;
400 if (term->title && !strcmp(title, term->title)) return 0;
402 /* In which codepage was the title parameter given? */
403 from_cp = get_terminal_codepage(term);
405 /* In which codepage does the terminal want the title? */
406 if (get_opt_bool_tree(term->spec, "latin1_title", NULL))
407 to_cp = get_cp_index("ISO-8859-1");
408 else if (get_opt_bool_tree(term->spec, "utf_8_io", NULL))
409 to_cp = get_cp_index("UTF-8");
410 else
411 to_cp = from_cp;
413 if (from_cp != to_cp) {
414 struct conv_table *convert_table;
416 convert_table = get_translation_table(from_cp, to_cp);
417 if (!convert_table) return -1;
418 converted = convert_string(convert_table, title, strlen(title),
419 to_cp, CSM_NONE, NULL, NULL, NULL);
420 if (!converted) return -1;
423 mem_free_set(&term->title, stracpy(title));
424 do_terminal_function(term, TERM_FN_TITLE_CODEPAGE,
425 get_cp_mime_name(to_cp));
426 do_terminal_function(term, TERM_FN_TITLE,
427 converted ? converted : title);
428 mem_free_if(converted);
429 return 0;
432 static int terminal_pipe[2];
435 check_terminal_pipes(void)
437 return c_pipe(terminal_pipe);
440 void
441 close_terminal_pipes(void)
443 close(terminal_pipe[0]);
444 close(terminal_pipe[1]);
447 struct terminal *
448 attach_terminal(int in, int out, int ctl, void *info, int len)
450 struct terminal *term;
452 if (set_nonblocking_fd(terminal_pipe[0]) < 0) return NULL;
453 if (set_nonblocking_fd(terminal_pipe[1]) < 0) return NULL;
454 handle_trm(in, out, out, terminal_pipe[1], ctl, info, len, 0);
456 term = init_term(terminal_pipe[0], out);
457 if (!term) {
458 close_terminal_pipes();
459 return NULL;
462 return term;
465 static struct module *terminal_submodules[] = {
466 &terminal_screen_module,
467 NULL
470 struct module terminal_module = struct_module(
471 /* Because this module is listed in main_modules rather than
472 * in builtin_modules, its name does not appear in the user
473 * interface and so need not be translatable. */
474 /* name: */ "Terminal",
475 /* options: */ NULL,
476 /* hooks: */ NULL,
477 /* submodules: */ terminal_submodules,
478 /* data: */ NULL,
479 /* init: */ NULL,
480 /* done: */ NULL