Ticket #2738: handle errors at mc sturtup stage.
[midnight-commander.git] / src / main.c
blob3bdc817b02c8f7d5917803781c27cdd0e4b637ab
1 /*
2 Main program for the Midnight Commander
4 Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
5 2003, 2004, 2005, 2006, 2007, 2009, 2011
6 The Free Software Foundation, Inc.
8 Written by:
9 Miguel de Icaza, 1994, 1995, 1996, 1997
10 Janne Kukonlehto, 1994, 1995
11 Norbert Warmuth, 1997
13 This file is part of the Midnight Commander.
15 The Midnight Commander is free software: you can redistribute it
16 and/or modify it under the terms of the GNU General Public License as
17 published by the Free Software Foundation, either version 3 of the License,
18 or (at your option) any later version.
20 The Midnight Commander is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU General Public License for more details.
25 You should have received a copy of the GNU General Public License
26 along with this program. If not, see <http://www.gnu.org/licenses/>.
29 /** \file main.c
30 * \brief Source: this is a main module
33 #include <config.h>
35 #include <ctype.h>
36 #include <errno.h>
37 #include <locale.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <sys/wait.h>
42 #include <pwd.h> /* for username in xterm title */
43 #include <signal.h>
45 #include "lib/global.h"
47 #include "lib/event.h"
48 #include "lib/tty/tty.h"
49 #include "lib/tty/key.h" /* For init_key() */
50 #include "lib/skin.h"
51 #include "lib/filehighlight.h"
52 #include "lib/fileloc.h"
53 #include "lib/strutil.h"
54 #include "lib/util.h"
55 #include "lib/vfs/vfs.h" /* vfs_init(), vfs_shut() */
57 #include "filemanager/midnight.h" /* current_panel */
58 #include "filemanager/treestore.h" /* tree_store_save */
59 #include "filemanager/layout.h" /* command_prompt */
60 #include "filemanager/ext.h" /* flush_extension_file() */
61 #include "filemanager/command.h" /* cmdline */
62 #include "filemanager/panel.h" /* panalized_panel */
64 #include "vfs/plugins_init.h"
66 #include "events_init.h"
67 #include "args.h"
68 #include "subshell.h"
69 #include "setup.h" /* load_setup() */
71 #ifdef HAVE_CHARSET
72 #include "lib/charsets.h"
73 #include "selcodepage.h"
74 #endif /* HAVE_CHARSET */
76 #include "consaver/cons.saver.h" /* cons_saver_pid */
78 #include "main.h"
80 /*** global variables ****************************************************************************/
82 mc_fhl_t *mc_filehighlight;
84 /* Set when main loop should be terminated */
85 int quit = 0;
87 #ifdef HAVE_CHARSET
88 /* Numbers of (file I/O) and (input/display) codepages. -1 if not selected */
89 int default_source_codepage = -1;
90 char *autodetect_codeset = NULL;
91 gboolean is_autodetect_codeset_enabled = FALSE;
92 #endif /* !HAVE_CHARSET */
94 /* If true use the internal viewer */
95 int use_internal_view = 1;
96 /* If set, use the builtin editor */
97 int use_internal_edit = 1;
99 char *mc_run_param0 = NULL;
100 char *mc_run_param1 = NULL;
102 /* The user's shell */
103 char *shell = NULL;
105 /* The prompt */
106 const char *mc_prompt = NULL;
108 /* Set to TRUE to suppress printing the last directory */
109 int print_last_revert = FALSE;
111 /* If set, then print to the given file the last directory we were at */
112 char *last_wd_string = NULL;
114 /* index to record_macro_buf[], -1 if not recording a macro */
115 int macro_index = -1;
117 /* macro stuff */
118 struct macro_action_t record_macro_buf[MAX_MACRO_LENGTH];
120 GArray *macros_list;
122 /*** file scope macro definitions ****************************************************************/
124 /*** file scope type declarations ****************************************************************/
126 /*** file scope variables ************************************************************************/
128 /*** file scope functions ************************************************************************/
129 /* --------------------------------------------------------------------------------------------- */
131 static void
132 check_codeset (void)
134 const char *current_system_codepage = NULL;
136 current_system_codepage = str_detect_termencoding ();
138 #ifdef HAVE_CHARSET
140 const char *_display_codepage;
142 _display_codepage = get_codepage_id (mc_global.display_codepage);
144 if (strcmp (_display_codepage, current_system_codepage) != 0)
146 mc_global.display_codepage = get_codepage_index (current_system_codepage);
147 if (mc_global.display_codepage == -1)
148 mc_global.display_codepage = 0;
150 mc_config_set_string (mc_main_config, "Misc", "display_codepage", cp_display);
153 #endif
155 mc_global.utf8_display = str_isutf8 (current_system_codepage);
158 /* --------------------------------------------------------------------------------------------- */
160 /** POSIX version. The only version we support. */
161 static void
162 OS_Setup (void)
164 const char *shell_env = getenv ("SHELL");
166 if ((shell_env == NULL) || (shell_env[0] == '\0'))
168 struct passwd *pwd;
169 pwd = getpwuid (geteuid ());
170 if (pwd != NULL)
171 shell = g_strdup (pwd->pw_shell);
173 else
174 shell = g_strdup (shell_env);
176 if ((shell == NULL) || (shell[0] == '\0'))
178 g_free (shell);
179 shell = g_strdup ("/bin/sh");
183 /* --------------------------------------------------------------------------------------------- */
185 static void
186 sigchld_handler_no_subshell (int sig)
188 #ifdef __linux__
189 int pid, status;
191 if (!mc_global.tty.console_flag != '\0')
192 return;
194 /* COMMENT: if it were true that after the call to handle_console(..INIT)
195 the value of mc_global.tty.console_flag never changed, we could simply not install
196 this handler at all if (!mc_global.tty.console_flag && !mc_global.tty.use_subshell). */
198 /* That comment is no longer true. We need to wait() on a sigchld
199 handler (that's at least what the tarfs code expects currently). */
201 pid = waitpid (cons_saver_pid, &status, WUNTRACED | WNOHANG);
203 if (pid == cons_saver_pid)
206 if (WIFSTOPPED (status))
208 /* Someone has stopped cons.saver - restart it */
209 kill (pid, SIGCONT);
211 else
213 /* cons.saver has died - disable console saving */
214 handle_console (CONSOLE_DONE);
215 mc_global.tty.console_flag = '\0';
218 /* If we got here, some other child exited; ignore it */
219 #endif /* __linux__ */
221 (void) sig;
224 /* --------------------------------------------------------------------------------------------- */
226 static void
227 init_sigchld (void)
229 struct sigaction sigchld_action;
231 sigchld_action.sa_handler =
232 #ifdef HAVE_SUBSHELL_SUPPORT
233 mc_global.tty.use_subshell ? sigchld_handler :
234 #endif /* HAVE_SUBSHELL_SUPPORT */
235 sigchld_handler_no_subshell;
237 sigemptyset (&sigchld_action.sa_mask);
239 #ifdef SA_RESTART
240 sigchld_action.sa_flags = SA_RESTART;
241 #else
242 sigchld_action.sa_flags = 0;
243 #endif /* !SA_RESTART */
245 if (sigaction (SIGCHLD, &sigchld_action, NULL) == -1)
247 #ifdef HAVE_SUBSHELL_SUPPORT
249 * This may happen on QNX Neutrino 6, where SA_RESTART
250 * is defined but not implemented. Fallback to no subshell.
252 mc_global.tty.use_subshell = FALSE;
253 #endif /* HAVE_SUBSHELL_SUPPORT */
257 /* --------------------------------------------------------------------------------------------- */
258 /*** public functions ****************************************************************************/
259 /* --------------------------------------------------------------------------------------------- */
261 gboolean
262 do_cd (const char *new_dir, enum cd_enum exact)
264 gboolean res;
265 const char *_new_dir = new_dir;
267 if (current_panel->is_panelized && _new_dir[0] == '.' && _new_dir[1] == '.' && _new_dir[2] == 0)
268 _new_dir = panelized_panel.root;
270 res = do_panel_cd (current_panel, _new_dir, exact);
272 #if HAVE_CHARSET
273 if (res)
275 vfs_path_t *vpath = vfs_path_from_str (current_panel->cwd);
276 vfs_path_element_t *path_element = vfs_path_get_by_index (vpath, -1);
278 if (path_element->encoding != NULL)
279 current_panel->codepage = get_codepage_index (path_element->encoding);
280 else
281 current_panel->codepage = SELECT_CHARSET_NO_TRANSLATE;
283 vfs_path_free (vpath);
285 #endif /* HAVE_CHARSET */
287 return res;
290 /* --------------------------------------------------------------------------------------------- */
292 #ifdef HAVE_SUBSHELL_SUPPORT
293 gboolean
294 do_load_prompt (void)
296 gboolean ret = FALSE;
298 if (!read_subshell_prompt ())
299 return ret;
301 /* Don't actually change the prompt if it's invisible */
302 if (((Dlg_head *) top_dlg->data == midnight_dlg) && command_prompt)
304 setup_cmdline ();
306 /* since the prompt has changed, and we are called from one of the
307 * tty_get_event channels, the prompt updating does not take place
308 * automatically: force a cursor update and a screen refresh
310 update_cursor (midnight_dlg);
311 mc_refresh ();
312 ret = TRUE;
314 update_subshell_prompt = TRUE;
315 return ret;
318 /* --------------------------------------------------------------------------------------------- */
321 load_prompt (int fd, void *unused)
323 (void) fd;
324 (void) unused;
326 do_load_prompt ();
327 return 0;
329 #endif /* HAVE_SUBSHELL_SUPPORT */
331 /* --------------------------------------------------------------------------------------------- */
333 /** Show current directory in the xterm title */
334 void
335 update_xterm_title_path (void)
337 /* TODO: share code with midnight_get_title () */
339 const char *path;
340 char host[BUF_TINY];
341 char *p;
342 struct passwd *pw = NULL;
343 char *login = NULL;
344 int res = 0;
346 if (mc_global.tty.xterm_flag && xterm_title)
348 path = strip_home_and_password (current_panel->cwd);
349 res = gethostname (host, sizeof (host));
350 if (res)
351 { /* On success, res = 0 */
352 host[0] = '\0';
354 else
356 host[sizeof (host) - 1] = '\0';
358 pw = getpwuid (getuid ());
359 if (pw)
361 login = g_strdup_printf ("%s@%s", pw->pw_name, host);
363 else
365 login = g_strdup (host);
367 p = g_strdup_printf ("mc [%s]:%s", login, path);
368 fprintf (stdout, "\33]0;%s\7", str_term_form (p));
369 g_free (login);
370 g_free (p);
371 if (!mc_global.tty.alternate_plus_minus)
372 numeric_keypad_mode ();
373 (void) fflush (stdout);
377 /* --------------------------------------------------------------------------------------------- */
380 main (int argc, char *argv[])
382 GError *error = NULL;
383 int exit_code = EXIT_FAILURE;
384 gboolean isInitialized;
386 /* We had LC_CTYPE before, LC_ALL includs LC_TYPE as well */
387 (void) setlocale (LC_ALL, "");
388 (void) bindtextdomain ("mc", LOCALEDIR);
389 (void) textdomain ("mc");
391 if (!events_init (&error))
393 startup_exit_falure:
394 fprintf (stderr, _("Failed to run:\n%s\n"), error->message);
395 g_error_free (error);
396 startup_exit_ok:
397 (void) mc_event_deinit (NULL);
398 str_uninit_strings ();
399 return exit_code;
402 str_init_strings (NULL);
404 /* Set up temporary directory */
405 (void) mc_tmpdir ();
406 OS_Setup ();
407 /* Initialize and create home directories */
408 /* do it after the screen library initialization to show the error message */
409 mc_config_init_config_paths (&error);
410 if (error == NULL && mc_config_deprecated_dir_present ())
411 mc_config_migrate_from_old_place (&error);
412 if (error != NULL)
414 g_free (shell);
415 goto startup_exit_falure;
418 vfs_init ();
419 vfs_plugins_init ();
420 vfs_setup_work_dir ();
422 /* do this after vfs initialization due to mc_setctl() call in mc_args_handle() */
423 if (!mc_args_handle (argc, argv, "mc", &error))
425 if (error != NULL)
427 vfs_shut ();
428 goto startup_exit_falure;
431 /* for help messages */
432 exit_code = EXIT_SUCCESS;
433 goto startup_exit_ok;
436 /* check terminal type
437 * $TEMR must be set and not empty
438 * mc_global.tty.xterm_flag is used in init_key() and tty_init()
439 * Do this after mc_args_handle() where mc_args__force_xterm is set up.
441 mc_global.tty.xterm_flag = tty_check_term (mc_args__force_xterm);
443 /* NOTE: This has to be called before tty_init or whatever routine
444 calls any define_sequence */
445 init_key ();
447 /* Must be done before installing the SIGCHLD handler [[FIXME]] */
448 handle_console (CONSOLE_INIT);
450 #ifdef HAVE_SUBSHELL_SUPPORT
451 /* Don't use subshell when invoked as viewer or editor */
452 if (mc_global.mc_run_mode != MC_RUN_FULL)
453 mc_global.tty.use_subshell = FALSE;
455 if (mc_global.tty.use_subshell)
456 subshell_get_console_attributes ();
457 #endif /* HAVE_SUBSHELL_SUPPORT */
459 /* Install the SIGCHLD handler; must be done before init_subshell() */
460 init_sigchld ();
462 /* We need this, since ncurses endwin () doesn't restore the signals */
463 save_stop_handler ();
465 /* Must be done before init_subshell, to set up the terminal size: */
466 /* FIXME: Should be removed and LINES and COLS computed on subshell */
467 tty_init (!mc_args__nomouse, mc_global.tty.xterm_flag);
469 load_setup ();
471 /* start check mc_global.display_codepage and mc_global.source_codepage */
472 check_codeset ();
474 /* Removing this from the X code let's us type C-c */
475 load_key_defs ();
477 load_keymap_defs (!mc_args__nokeymap);
479 macros_list = g_array_new (TRUE, FALSE, sizeof (macros_t));
481 tty_init_colors (mc_global.tty.disable_colors, mc_args__force_colors);
483 isInitialized = mc_skin_init (&error);
484 if (error != NULL)
486 message (D_ERROR, _("Warning"), "%s", error->message);
487 g_error_free (error);
488 error = NULL;
491 mc_filehighlight = mc_fhl_new (TRUE);
492 dlg_set_default_colors ();
494 #ifdef HAVE_SUBSHELL_SUPPORT
495 /* Done here to ensure that the subshell doesn't */
496 /* inherit the file descriptors opened below, etc */
497 if (mc_global.tty.use_subshell)
498 init_subshell ();
500 #endif /* HAVE_SUBSHELL_SUPPORT */
502 /* Also done after init_subshell, to save any shell init file messages */
503 if (mc_global.tty.console_flag != '\0')
504 handle_console (CONSOLE_SAVE);
506 if (mc_global.tty.alternate_plus_minus)
507 application_keypad_mode ();
509 #ifdef HAVE_SUBSHELL_SUPPORT
510 if (mc_global.tty.use_subshell)
512 mc_prompt = strip_ctrl_codes (subshell_prompt);
513 if (mc_prompt == NULL)
514 mc_prompt = (geteuid () == 0) ? "# " : "$ ";
516 else
517 #endif /* HAVE_SUBSHELL_SUPPORT */
518 mc_prompt = (geteuid () == 0) ? "# " : "$ ";
520 /* Program main loop */
521 if (!mc_global.widget.midnight_shutdown)
522 do_nc ();
524 /* Save the tree store */
525 (void) tree_store_save ();
527 free_keymap_defs ();
529 /* Virtual File System shutdown */
530 vfs_shut ();
532 flush_extension_file (); /* does only free memory */
534 mc_fhl_free (&mc_filehighlight);
535 mc_skin_deinit ();
536 tty_colors_done ();
538 tty_shutdown ();
540 done_setup ();
542 if (mc_global.tty.console_flag != '\0' && (quit & SUBSHELL_EXIT) == 0)
543 handle_console (CONSOLE_RESTORE);
544 if (mc_global.tty.alternate_plus_minus)
545 numeric_keypad_mode ();
547 (void) signal (SIGCHLD, SIG_DFL); /* Disable the SIGCHLD handler */
549 if (mc_global.tty.console_flag != '\0')
550 handle_console (CONSOLE_DONE);
552 if (mc_global.mc_run_mode == MC_RUN_FULL && mc_args__last_wd_file != NULL
553 && last_wd_string != NULL && !print_last_revert)
555 int last_wd_fd;
557 last_wd_fd = open (mc_args__last_wd_file, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL,
558 S_IRUSR | S_IWUSR);
559 if (last_wd_fd != -1)
561 ssize_t ret1;
562 int ret2;
563 ret1 = write (last_wd_fd, last_wd_string, strlen (last_wd_string));
564 ret2 = close (last_wd_fd);
567 g_free (last_wd_string);
569 g_free (shell);
571 done_key ();
573 if (macros_list != NULL)
575 guint i;
576 macros_t *macros;
577 for (i = 0; i < macros_list->len; i++)
579 macros = &g_array_index (macros_list, struct macros_t, i);
580 if (macros != NULL && macros->macro != NULL)
581 (void) g_array_free (macros->macro, FALSE);
583 (void) g_array_free (macros_list, TRUE);
586 str_uninit_strings ();
588 g_free (mc_run_param0);
589 g_free (mc_run_param1);
591 mc_config_deinit_config_paths ();
593 (void) mc_event_deinit (&error);
595 if (error != NULL)
597 fprintf (stderr, _("\nFailed while close:\n%s\n"), error->message);
598 g_error_free (error);
599 exit (EXIT_FAILURE);
602 (void) putchar ('\n'); /* Hack to make shell's prompt start at left of screen */
604 return 0;
607 /* --------------------------------------------------------------------------------------------- */