(size_trunc_len): fixed potential integer overflow if SI is used.
[midnight-commander.git] / src / main.c
blob43e99c5165e1f03bf0834a71725c0887cddd0f95
1 /* Main program for the Midnight Commander
2 Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
3 2003, 2004, 2005, 2006, 2007, 2009 Free Software Foundation, Inc.
5 Written by: 1994, 1995, 1996, 1997 Miguel de Icaza
6 1994, 1995 Janne Kukonlehto
7 1997 Norbert Warmuth
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
23 /** \file main.c
24 * \brief Source: this is a main module
27 #include <config.h>
29 #include <ctype.h>
30 #include <errno.h>
31 #include <locale.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <sys/wait.h>
36 #include <pwd.h> /* for username in xterm title */
37 #include <signal.h>
39 #include "lib/global.h"
41 #include "lib/event.h"
42 #include "lib/tty/tty.h"
43 #include "lib/tty/key.h" /* For init_key() */
44 #include "lib/skin.h"
45 #include "lib/filehighlight.h"
46 #include "lib/fileloc.h"
47 #include "lib/strutil.h"
48 #include "lib/util.h"
49 #include "lib/vfs/vfs.h" /* vfs_init(), vfs_shut() */
51 #include "filemanager/midnight.h" /* current_panel */
52 #include "filemanager/treestore.h" /* tree_store_save */
53 #include "filemanager/layout.h" /* command_prompt */
54 #include "filemanager/ext.h" /* flush_extension_file() */
55 #include "filemanager/command.h" /* cmdline */
57 #include "vfs/plugins_init.h"
59 #include "events_init.h"
60 #include "args.h"
61 #include "subshell.h"
62 #include "setup.h" /* load_setup() */
64 #ifdef HAVE_CHARSET
65 #include "lib/charsets.h"
66 #include "selcodepage.h"
67 #endif /* HAVE_CHARSET */
69 #include "consaver/cons.saver.h" /* cons_saver_pid */
71 #include "main.h"
73 /*** global variables ****************************************************************************/
75 mc_fhl_t *mc_filehighlight;
77 /* Set when main loop should be terminated */
78 int quit = 0;
80 #ifdef HAVE_CHARSET
81 /* Numbers of (file I/O) and (input/display) codepages. -1 if not selected */
82 int default_source_codepage = -1;
83 char *autodetect_codeset = NULL;
84 gboolean is_autodetect_codeset_enabled = FALSE;
85 #endif /* !HAVE_CHARSET */
87 /* If true use the internal viewer */
88 int use_internal_view = 1;
89 /* If set, use the builtin editor */
90 int use_internal_edit = 1;
92 char *mc_run_param0 = NULL;
93 char *mc_run_param1 = NULL;
95 /* The user's shell */
96 char *shell = NULL;
98 /* The prompt */
99 const char *mc_prompt = NULL;
101 /* Set to TRUE to suppress printing the last directory */
102 int print_last_revert = FALSE;
104 /* If set, then print to the given file the last directory we were at */
105 char *last_wd_string = NULL;
107 /* index to record_macro_buf[], -1 if not recording a macro */
108 int macro_index = -1;
110 /* macro stuff */
111 struct macro_action_t record_macro_buf[MAX_MACRO_LENGTH];
113 GArray *macros_list;
115 /*** file scope macro definitions ****************************************************************/
117 /*** file scope type declarations ****************************************************************/
119 /*** file scope variables ************************************************************************/
121 /*** file scope functions ************************************************************************/
122 /* --------------------------------------------------------------------------------------------- */
124 static void
125 check_codeset (void)
127 const char *current_system_codepage = NULL;
129 current_system_codepage = str_detect_termencoding ();
131 #ifdef HAVE_CHARSET
133 const char *_display_codepage;
135 _display_codepage = get_codepage_id (mc_global.display_codepage);
137 if (strcmp (_display_codepage, current_system_codepage) != 0)
139 mc_global.display_codepage = get_codepage_index (current_system_codepage);
140 if (mc_global.display_codepage == -1)
141 mc_global.display_codepage = 0;
143 mc_config_set_string (mc_main_config, "Misc", "display_codepage", cp_display);
146 #endif
148 mc_global.utf8_display = str_isutf8 (current_system_codepage);
151 /* --------------------------------------------------------------------------------------------- */
153 /** POSIX version. The only version we support. */
154 static void
155 OS_Setup (void)
157 const char *shell_env = getenv ("SHELL");
159 if ((shell_env == NULL) || (shell_env[0] == '\0'))
161 struct passwd *pwd;
162 pwd = getpwuid (geteuid ());
163 if (pwd != NULL)
164 shell = g_strdup (pwd->pw_shell);
166 else
167 shell = g_strdup (shell_env);
169 if ((shell == NULL) || (shell[0] == '\0'))
171 g_free (shell);
172 shell = g_strdup ("/bin/sh");
176 /* --------------------------------------------------------------------------------------------- */
178 static void
179 sigchld_handler_no_subshell (int sig)
181 #ifdef __linux__
182 int pid, status;
184 if (!mc_global.tty.console_flag != '\0')
185 return;
187 /* COMMENT: if it were true that after the call to handle_console(..INIT)
188 the value of mc_global.tty.console_flag never changed, we could simply not install
189 this handler at all if (!mc_global.tty.console_flag && !mc_global.tty.use_subshell). */
191 /* That comment is no longer true. We need to wait() on a sigchld
192 handler (that's at least what the tarfs code expects currently). */
194 pid = waitpid (cons_saver_pid, &status, WUNTRACED | WNOHANG);
196 if (pid == cons_saver_pid)
199 if (WIFSTOPPED (status))
201 /* Someone has stopped cons.saver - restart it */
202 kill (pid, SIGCONT);
204 else
206 /* cons.saver has died - disable console saving */
207 handle_console (CONSOLE_DONE);
208 mc_global.tty.console_flag = '\0';
211 /* If we got here, some other child exited; ignore it */
212 #endif /* __linux__ */
214 (void) sig;
217 /* --------------------------------------------------------------------------------------------- */
219 static void
220 init_sigchld (void)
222 struct sigaction sigchld_action;
224 sigchld_action.sa_handler =
225 #ifdef HAVE_SUBSHELL_SUPPORT
226 mc_global.tty.use_subshell ? sigchld_handler :
227 #endif /* HAVE_SUBSHELL_SUPPORT */
228 sigchld_handler_no_subshell;
230 sigemptyset (&sigchld_action.sa_mask);
232 #ifdef SA_RESTART
233 sigchld_action.sa_flags = SA_RESTART;
234 #else
235 sigchld_action.sa_flags = 0;
236 #endif /* !SA_RESTART */
238 if (sigaction (SIGCHLD, &sigchld_action, NULL) == -1)
240 #ifdef HAVE_SUBSHELL_SUPPORT
242 * This may happen on QNX Neutrino 6, where SA_RESTART
243 * is defined but not implemented. Fallback to no subshell.
245 mc_global.tty.use_subshell = FALSE;
246 #endif /* HAVE_SUBSHELL_SUPPORT */
250 /* --------------------------------------------------------------------------------------------- */
251 /*** public functions ****************************************************************************/
252 /* --------------------------------------------------------------------------------------------- */
255 do_cd (const char *new_dir, enum cd_enum exact)
257 gboolean res;
259 res = do_panel_cd (current_panel, new_dir, exact);
261 #if HAVE_CHARSET
262 if (res)
264 vfs_path_t *vpath = vfs_path_from_str (current_panel->cwd);
265 vfs_path_element_t *path_element = vfs_path_get_by_index (vpath, -1);
267 if (path_element->encoding != NULL)
268 current_panel->codepage = get_codepage_index (path_element->encoding);
269 else
270 current_panel->codepage = SELECT_CHARSET_NO_TRANSLATE;
272 vfs_path_free (vpath);
274 #endif /* HAVE_CHARSET */
276 return res ? 1 : 0;
279 /* --------------------------------------------------------------------------------------------- */
281 #ifdef HAVE_SUBSHELL_SUPPORT
283 load_prompt (int fd, void *unused)
285 (void) fd;
286 (void) unused;
288 if (!read_subshell_prompt ())
289 return 0;
291 /* Don't actually change the prompt if it's invisible */
292 if (((Dlg_head *) top_dlg->data == midnight_dlg) && command_prompt)
294 char *tmp_prompt;
295 int prompt_len;
297 tmp_prompt = strip_ctrl_codes (subshell_prompt);
298 prompt_len = str_term_width1 (tmp_prompt);
300 /* Check for prompts too big */
301 if (COLS > 8 && prompt_len > COLS - 8)
303 prompt_len = COLS - 8;
304 tmp_prompt[prompt_len] = '\0';
306 mc_prompt = tmp_prompt;
307 label_set_text (the_prompt, mc_prompt);
308 input_set_origin ((WInput *) cmdline, prompt_len, COLS - prompt_len);
310 /* since the prompt has changed, and we are called from one of the
311 * tty_get_event channels, the prompt updating does not take place
312 * automatically: force a cursor update and a screen refresh
314 update_cursor (midnight_dlg);
315 mc_refresh ();
317 update_subshell_prompt = TRUE;
318 return 0;
320 #endif /* HAVE_SUBSHELL_SUPPORT */
322 /* --------------------------------------------------------------------------------------------- */
324 /** Show current directory in the xterm title */
325 void
326 update_xterm_title_path (void)
328 /* TODO: share code with midnight_get_title () */
330 const char *path;
331 char host[BUF_TINY];
332 char *p;
333 struct passwd *pw = NULL;
334 char *login = NULL;
335 int res = 0;
337 if (mc_global.tty.xterm_flag && xterm_title)
339 path = strip_home_and_password (current_panel->cwd);
340 res = gethostname (host, sizeof (host));
341 if (res)
342 { /* On success, res = 0 */
343 host[0] = '\0';
345 else
347 host[sizeof (host) - 1] = '\0';
349 pw = getpwuid (getuid ());
350 if (pw)
352 login = g_strdup_printf ("%s@%s", pw->pw_name, host);
354 else
356 login = g_strdup (host);
358 p = g_strdup_printf ("mc [%s]:%s", login, path);
359 fprintf (stdout, "\33]0;%s\7", str_term_form (p));
360 g_free (login);
361 g_free (p);
362 if (!mc_global.tty.alternate_plus_minus)
363 numeric_keypad_mode ();
364 (void) fflush (stdout);
368 /* --------------------------------------------------------------------------------------------- */
371 main (int argc, char *argv[])
373 GError *error = NULL;
374 gboolean isInitialized;
376 /* We had LC_CTYPE before, LC_ALL includs LC_TYPE as well */
377 (void) setlocale (LC_ALL, "");
378 (void) bindtextdomain ("mc", LOCALEDIR);
379 (void) textdomain ("mc");
381 if (!events_init (&error))
383 fprintf (stderr, _("Failed to run:\n%s\n"), error->message);
384 g_error_free (error);
385 (void) mc_event_deinit (NULL);
386 exit (EXIT_FAILURE);
389 /* Set up temporary directory */
390 (void) mc_tmpdir ();
392 OS_Setup ();
394 str_init_strings (NULL);
396 /* Initialize and create home directories */
397 /* do it after the screen library initialization to show the error message */
398 mc_config_init_config_paths (&error);
400 if (error == NULL && mc_config_deprecated_dir_present ())
401 mc_config_migrate_from_old_place (&error);
403 vfs_init ();
404 vfs_plugins_init ();
405 vfs_setup_work_dir ();
407 if (!mc_args_handle (argc, argv, "mc"))
408 exit (EXIT_FAILURE);
410 /* check terminal type
411 * $TEMR must be set and not empty
412 * mc_global.tty.xterm_flag is used in init_key() and tty_init()
413 * Do this after mc_args_handle() where mc_args__force_xterm is set up.
415 mc_global.tty.xterm_flag = tty_check_term (mc_args__force_xterm);
417 /* NOTE: This has to be called before tty_init or whatever routine
418 calls any define_sequence */
419 init_key ();
421 /* Must be done before installing the SIGCHLD handler [[FIXME]] */
422 handle_console (CONSOLE_INIT);
424 #ifdef HAVE_SUBSHELL_SUPPORT
425 /* Don't use subshell when invoked as viewer or editor */
426 if (mc_global.mc_run_mode != MC_RUN_FULL)
427 mc_global.tty.use_subshell = FALSE;
429 if (mc_global.tty.use_subshell)
430 subshell_get_console_attributes ();
431 #endif /* HAVE_SUBSHELL_SUPPORT */
433 /* Install the SIGCHLD handler; must be done before init_subshell() */
434 init_sigchld ();
436 /* We need this, since ncurses endwin () doesn't restore the signals */
437 save_stop_handler ();
439 /* Must be done before init_subshell, to set up the terminal size: */
440 /* FIXME: Should be removed and LINES and COLS computed on subshell */
441 tty_init (!mc_args__nomouse, mc_global.tty.xterm_flag);
443 load_setup ();
445 /* start check mc_global.display_codepage and mc_global.source_codepage */
446 check_codeset ();
448 /* Removing this from the X code let's us type C-c */
449 load_key_defs ();
451 load_keymap_defs (!mc_args__nokeymap);
453 macros_list = g_array_new (TRUE, FALSE, sizeof (macros_t));
455 tty_init_colors (mc_global.tty.disable_colors, mc_args__force_colors);
458 GError *error2 = NULL;
459 isInitialized = mc_skin_init (&error2);
460 mc_filehighlight = mc_fhl_new (TRUE);
461 dlg_set_default_colors ();
463 if (!isInitialized)
465 message (D_ERROR, _("Warning"), "%s", error2->message);
466 g_error_free (error2);
467 error2 = NULL;
471 if (error != NULL)
473 message (D_ERROR, _("Warning"), "%s", error->message);
474 g_error_free (error);
475 error = NULL;
479 #ifdef HAVE_SUBSHELL_SUPPORT
480 /* Done here to ensure that the subshell doesn't */
481 /* inherit the file descriptors opened below, etc */
482 if (mc_global.tty.use_subshell)
483 init_subshell ();
485 #endif /* HAVE_SUBSHELL_SUPPORT */
487 /* Also done after init_subshell, to save any shell init file messages */
488 if (mc_global.tty.console_flag != '\0')
489 handle_console (CONSOLE_SAVE);
491 if (mc_global.tty.alternate_plus_minus)
492 application_keypad_mode ();
494 #ifdef HAVE_SUBSHELL_SUPPORT
495 if (mc_global.tty.use_subshell)
497 mc_prompt = strip_ctrl_codes (subshell_prompt);
498 if (mc_prompt == NULL)
499 mc_prompt = (geteuid () == 0) ? "# " : "$ ";
501 else
502 #endif /* HAVE_SUBSHELL_SUPPORT */
503 mc_prompt = (geteuid () == 0) ? "# " : "$ ";
505 /* Program main loop */
506 if (!mc_global.widget.midnight_shutdown)
507 do_nc ();
509 /* Save the tree store */
510 (void) tree_store_save ();
512 free_keymap_defs ();
514 /* Virtual File System shutdown */
515 vfs_shut ();
517 flush_extension_file (); /* does only free memory */
519 mc_fhl_free (&mc_filehighlight);
520 mc_skin_deinit ();
521 tty_colors_done ();
523 tty_shutdown ();
525 done_setup ();
527 if (mc_global.tty.console_flag != '\0' && (quit & SUBSHELL_EXIT) == 0)
528 handle_console (CONSOLE_RESTORE);
529 if (mc_global.tty.alternate_plus_minus)
530 numeric_keypad_mode ();
532 (void) signal (SIGCHLD, SIG_DFL); /* Disable the SIGCHLD handler */
534 if (mc_global.tty.console_flag != '\0')
535 handle_console (CONSOLE_DONE);
537 if (mc_global.mc_run_mode == MC_RUN_FULL && mc_args__last_wd_file != NULL
538 && last_wd_string != NULL && !print_last_revert)
540 int last_wd_fd;
542 last_wd_fd = open (mc_args__last_wd_file, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL,
543 S_IRUSR | S_IWUSR);
544 if (last_wd_fd != -1)
546 ssize_t ret1;
547 int ret2;
548 ret1 = write (last_wd_fd, last_wd_string, strlen (last_wd_string));
549 ret2 = close (last_wd_fd);
552 g_free (last_wd_string);
554 g_free (shell);
556 done_key ();
558 if (macros_list != NULL)
560 guint i;
561 macros_t *macros;
562 for (i = 0; i < macros_list->len; i++)
564 macros = &g_array_index (macros_list, struct macros_t, i);
565 if (macros != NULL && macros->macro != NULL)
566 (void) g_array_free (macros->macro, FALSE);
568 (void) g_array_free (macros_list, TRUE);
571 str_uninit_strings ();
573 g_free (mc_run_param0);
574 g_free (mc_run_param1);
576 (void) mc_event_deinit (&error);
578 mc_config_deinit_config_paths ();
580 if (error != NULL)
582 fprintf (stderr, _("\nFailed while close:\n%s\n"), error->message);
583 g_error_free (error);
584 exit (EXIT_FAILURE);
587 (void) putchar ('\n'); /* Hack to make shell's prompt start at left of screen */
589 return 0;
592 /* --------------------------------------------------------------------------------------------- */