(diff_view): make static.
[midnight-commander.git] / src / main.c
blob8fecc39ed230c55c460147ceee2c616659eb87b1
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, CONFIG_MISC_SECTION, "display_codepage",
151 cp_display);
154 #endif
156 mc_global.utf8_display = str_isutf8 (current_system_codepage);
159 /* --------------------------------------------------------------------------------------------- */
161 /** POSIX version. The only version we support. */
162 static void
163 OS_Setup (void)
165 const char *shell_env;
166 const char *datadir_env;
168 shell_env = getenv ("SHELL");
169 if ((shell_env == NULL) || (shell_env[0] == '\0'))
171 struct passwd *pwd;
172 pwd = getpwuid (geteuid ());
173 if (pwd != NULL)
174 shell = g_strdup (pwd->pw_shell);
176 else
177 shell = g_strdup (shell_env);
179 if ((shell == NULL) || (shell[0] == '\0'))
181 g_free (shell);
182 shell = g_strdup ("/bin/sh");
185 /* This is the directory, where MC was installed, on Unix this is DATADIR */
186 /* and can be overriden by the MC_DATADIR environment variable */
187 datadir_env = g_getenv ("MC_DATADIR");
188 if (datadir_env != NULL)
189 mc_global.sysconfig_dir = g_strdup (datadir_env);
190 else
191 mc_global.sysconfig_dir = g_strdup (SYSCONFDIR);
193 mc_global.share_data_dir = g_strdup (DATADIR);
195 /* Set up temporary directory */
196 mc_tmpdir ();
199 /* --------------------------------------------------------------------------------------------- */
201 static void
202 sigchld_handler_no_subshell (int sig)
204 #ifdef __linux__
205 int pid, status;
207 if (!mc_global.tty.console_flag != '\0')
208 return;
210 /* COMMENT: if it were true that after the call to handle_console(..INIT)
211 the value of mc_global.tty.console_flag never changed, we could simply not install
212 this handler at all if (!mc_global.tty.console_flag && !mc_global.tty.use_subshell). */
214 /* That comment is no longer true. We need to wait() on a sigchld
215 handler (that's at least what the tarfs code expects currently). */
217 pid = waitpid (cons_saver_pid, &status, WUNTRACED | WNOHANG);
219 if (pid == cons_saver_pid)
222 if (WIFSTOPPED (status))
224 /* Someone has stopped cons.saver - restart it */
225 kill (pid, SIGCONT);
227 else
229 /* cons.saver has died - disable console saving */
230 handle_console (CONSOLE_DONE);
231 mc_global.tty.console_flag = '\0';
234 /* If we got here, some other child exited; ignore it */
235 #endif /* __linux__ */
237 (void) sig;
240 /* --------------------------------------------------------------------------------------------- */
242 static void
243 init_sigchld (void)
245 struct sigaction sigchld_action;
247 sigchld_action.sa_handler =
248 #ifdef HAVE_SUBSHELL_SUPPORT
249 mc_global.tty.use_subshell ? sigchld_handler :
250 #endif /* HAVE_SUBSHELL_SUPPORT */
251 sigchld_handler_no_subshell;
253 sigemptyset (&sigchld_action.sa_mask);
255 #ifdef SA_RESTART
256 sigchld_action.sa_flags = SA_RESTART;
257 #else
258 sigchld_action.sa_flags = 0;
259 #endif /* !SA_RESTART */
261 if (sigaction (SIGCHLD, &sigchld_action, NULL) == -1)
263 #ifdef HAVE_SUBSHELL_SUPPORT
265 * This may happen on QNX Neutrino 6, where SA_RESTART
266 * is defined but not implemented. Fallback to no subshell.
268 mc_global.tty.use_subshell = FALSE;
269 #endif /* HAVE_SUBSHELL_SUPPORT */
273 /* --------------------------------------------------------------------------------------------- */
274 /*** public functions ****************************************************************************/
275 /* --------------------------------------------------------------------------------------------- */
277 gboolean
278 do_cd (const vfs_path_t * new_dir_vpath, enum cd_enum exact)
280 gboolean res;
281 const vfs_path_t *_new_dir_vpath = new_dir_vpath;
283 if (current_panel->is_panelized)
285 size_t new_vpath_len;
287 new_vpath_len = vfs_path_len (new_dir_vpath);
288 if (vfs_path_ncmp (new_dir_vpath, panelized_panel.root_vpath, new_vpath_len) == 0)
289 _new_dir_vpath = panelized_panel.root_vpath;
292 res = do_panel_cd (current_panel, _new_dir_vpath, exact);
294 #ifdef HAVE_CHARSET
295 if (res)
297 const vfs_path_element_t *path_element;
299 path_element = vfs_path_get_by_index (current_panel->cwd_vpath, -1);
300 if (path_element->encoding != NULL)
301 current_panel->codepage = get_codepage_index (path_element->encoding);
302 else
303 current_panel->codepage = SELECT_CHARSET_NO_TRANSLATE;
305 #endif /* HAVE_CHARSET */
307 return res;
310 /* --------------------------------------------------------------------------------------------- */
312 #ifdef HAVE_SUBSHELL_SUPPORT
313 gboolean
314 do_load_prompt (void)
316 gboolean ret = FALSE;
318 if (!read_subshell_prompt ())
319 return ret;
321 /* Don't actually change the prompt if it's invisible */
322 if (top_dlg != NULL && ((Dlg_head *) top_dlg->data == midnight_dlg) && command_prompt)
324 setup_cmdline ();
326 /* since the prompt has changed, and we are called from one of the
327 * tty_get_event channels, the prompt updating does not take place
328 * automatically: force a cursor update and a screen refresh
330 update_cursor (midnight_dlg);
331 mc_refresh ();
332 ret = TRUE;
334 update_subshell_prompt = TRUE;
335 return ret;
338 /* --------------------------------------------------------------------------------------------- */
341 load_prompt (int fd, void *unused)
343 (void) fd;
344 (void) unused;
346 do_load_prompt ();
347 return 0;
349 #endif /* HAVE_SUBSHELL_SUPPORT */
351 /* --------------------------------------------------------------------------------------------- */
353 void
354 title_path_prepare (char **path, char **login)
357 char host[BUF_TINY];
358 struct passwd *pw = NULL;
359 int res = 0;
361 *login = NULL;
364 *path =
365 vfs_path_to_str_flags (current_panel->cwd_vpath, 0, VPF_STRIP_HOME | VPF_STRIP_PASSWORD);
366 res = gethostname (host, sizeof (host));
367 if (res)
368 { /* On success, res = 0 */
369 host[0] = '\0';
371 else
373 host[sizeof (host) - 1] = '\0';
375 pw = getpwuid (getuid ());
376 if (pw)
378 *login = g_strdup_printf ("%s@%s", pw->pw_name, host);
380 else
382 *login = g_strdup (host);
386 /* --------------------------------------------------------------------------------------------- */
388 /** Show current directory in the xterm title */
389 void
390 update_xterm_title_path (void)
392 char *p;
393 char *path;
394 char *login;
396 if (!(mc_global.tty.xterm_flag && xterm_title))
397 return;
399 title_path_prepare (&path, &login);
401 p = g_strdup_printf ("mc [%s]:%s", login, path);
402 fprintf (stdout, "\33]0;%s\7", str_term_form (p));
403 g_free (login);
404 g_free (p);
405 if (!mc_global.tty.alternate_plus_minus)
406 numeric_keypad_mode ();
407 (void) fflush (stdout);
408 g_free (path);
411 /* --------------------------------------------------------------------------------------------- */
414 main (int argc, char *argv[])
416 GError *error = NULL;
417 int exit_code = EXIT_FAILURE;
419 /* We had LC_CTYPE before, LC_ALL includs LC_TYPE as well */
420 (void) setlocale (LC_ALL, "");
421 (void) bindtextdomain ("mc", LOCALEDIR);
422 (void) textdomain ("mc");
424 /* do this before args parsing */
425 str_init_strings (NULL);
427 if (!mc_args_parse (&argc, &argv, "mc", &error))
429 startup_exit_falure:
430 fprintf (stderr, _("Failed to run:\n%s\n"), error->message);
431 g_error_free (error);
432 g_free (shell);
433 startup_exit_ok:
434 str_uninit_strings ();
435 return exit_code;
438 /* do this before mc_args_show_info () to view paths in the --datadir-info output */
439 OS_Setup ();
441 if (!g_path_is_absolute (mc_config_get_home_dir ()))
443 error = g_error_new (MC_ERROR, 0, "%s: %s", _("Home directory path is not absolute"),
444 mc_config_get_home_dir ());
445 mc_event_deinit (NULL);
446 goto startup_exit_falure;
449 if (!mc_args_show_info ())
451 exit_code = EXIT_SUCCESS;
452 goto startup_exit_ok;
455 if (!events_init (&error))
456 goto startup_exit_falure;
458 mc_config_init_config_paths (&error);
459 if (error == NULL && mc_config_deprecated_dir_present ())
460 mc_config_migrate_from_old_place (&error);
461 if (error != NULL)
463 mc_event_deinit (NULL);
464 goto startup_exit_falure;
467 vfs_init ();
468 vfs_plugins_init ();
469 vfs_setup_work_dir ();
471 /* do this after vfs initialization due to mc_setctl() call in mc_setup_by_args() */
472 if (!mc_setup_by_args (argc, argv, &error))
474 vfs_shut ();
475 mc_event_deinit (NULL);
476 goto startup_exit_falure;
479 /* check terminal type
480 * $TEMR must be set and not empty
481 * mc_global.tty.xterm_flag is used in init_key() and tty_init()
482 * Do this after mc_args_handle() where mc_args__force_xterm is set up.
484 mc_global.tty.xterm_flag = tty_check_term (mc_args__force_xterm);
486 /* NOTE: This has to be called before tty_init or whatever routine
487 calls any define_sequence */
488 init_key ();
490 /* Must be done before installing the SIGCHLD handler [[FIXME]] */
491 handle_console (CONSOLE_INIT);
493 #ifdef HAVE_SUBSHELL_SUPPORT
494 /* Don't use subshell when invoked as viewer or editor */
495 if (mc_global.mc_run_mode != MC_RUN_FULL)
496 mc_global.tty.use_subshell = FALSE;
498 if (mc_global.tty.use_subshell)
499 subshell_get_console_attributes ();
500 #endif /* HAVE_SUBSHELL_SUPPORT */
502 /* Install the SIGCHLD handler; must be done before init_subshell() */
503 init_sigchld ();
505 /* We need this, since ncurses endwin () doesn't restore the signals */
506 save_stop_handler ();
508 /* Must be done before init_subshell, to set up the terminal size: */
509 /* FIXME: Should be removed and LINES and COLS computed on subshell */
510 tty_init (!mc_args__nomouse, mc_global.tty.xterm_flag);
512 load_setup ();
514 /* start check mc_global.display_codepage and mc_global.source_codepage */
515 check_codeset ();
517 /* Removing this from the X code let's us type C-c */
518 load_key_defs ();
520 load_keymap_defs (!mc_args__nokeymap);
522 macros_list = g_array_new (TRUE, FALSE, sizeof (macros_t));
524 tty_init_colors (mc_global.tty.disable_colors, mc_args__force_colors);
526 mc_skin_init (&error);
527 if (error != NULL)
529 message (D_ERROR, _("Warning"), "%s", error->message);
530 g_error_free (error);
531 error = NULL;
534 mc_filehighlight = mc_fhl_new (TRUE);
535 dlg_set_default_colors ();
537 #ifdef HAVE_SUBSHELL_SUPPORT
538 /* Done here to ensure that the subshell doesn't */
539 /* inherit the file descriptors opened below, etc */
540 if (mc_global.tty.use_subshell)
541 init_subshell ();
543 #endif /* HAVE_SUBSHELL_SUPPORT */
545 /* Also done after init_subshell, to save any shell init file messages */
546 if (mc_global.tty.console_flag != '\0')
547 handle_console (CONSOLE_SAVE);
549 if (mc_global.tty.alternate_plus_minus)
550 application_keypad_mode ();
552 #ifdef HAVE_SUBSHELL_SUPPORT
553 if (mc_global.tty.use_subshell)
555 mc_prompt = strip_ctrl_codes (subshell_prompt);
556 if (mc_prompt == NULL)
557 mc_prompt = (geteuid () == 0) ? "# " : "$ ";
559 else
560 #endif /* HAVE_SUBSHELL_SUPPORT */
561 mc_prompt = (geteuid () == 0) ? "# " : "$ ";
563 /* Program main loop */
564 if (mc_global.midnight_shutdown)
565 exit_code = EXIT_SUCCESS;
566 else
567 exit_code = do_nc ()? EXIT_SUCCESS : EXIT_FAILURE;
569 /* Save the tree store */
570 (void) tree_store_save ();
572 free_keymap_defs ();
574 /* Virtual File System shutdown */
575 vfs_shut ();
577 flush_extension_file (); /* does only free memory */
579 mc_fhl_free (&mc_filehighlight);
580 mc_skin_deinit ();
581 tty_colors_done ();
583 tty_shutdown ();
585 done_setup ();
587 if (mc_global.tty.console_flag != '\0' && (quit & SUBSHELL_EXIT) == 0)
588 handle_console (CONSOLE_RESTORE);
589 if (mc_global.tty.alternate_plus_minus)
590 numeric_keypad_mode ();
592 (void) signal (SIGCHLD, SIG_DFL); /* Disable the SIGCHLD handler */
594 if (mc_global.tty.console_flag != '\0')
595 handle_console (CONSOLE_DONE);
597 if (mc_global.mc_run_mode == MC_RUN_FULL && mc_args__last_wd_file != NULL
598 && last_wd_string != NULL && !print_last_revert)
600 int last_wd_fd;
602 last_wd_fd = open (mc_args__last_wd_file, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL,
603 S_IRUSR | S_IWUSR);
604 if (last_wd_fd != -1)
606 ssize_t ret1;
607 int ret2;
608 ret1 = write (last_wd_fd, last_wd_string, strlen (last_wd_string));
609 ret2 = close (last_wd_fd);
612 g_free (last_wd_string);
614 g_free (shell);
616 done_key ();
618 if (macros_list != NULL)
620 guint i;
621 macros_t *macros;
622 for (i = 0; i < macros_list->len; i++)
624 macros = &g_array_index (macros_list, struct macros_t, i);
625 if (macros != NULL && macros->macro != NULL)
626 (void) g_array_free (macros->macro, FALSE);
628 (void) g_array_free (macros_list, TRUE);
631 str_uninit_strings ();
633 g_free (mc_run_param0);
634 g_free (mc_run_param1);
636 mc_config_deinit_config_paths ();
638 (void) mc_event_deinit (&error);
639 if (error != NULL)
641 fprintf (stderr, _("\nFailed while close:\n%s\n"), error->message);
642 g_error_free (error);
643 exit_code = EXIT_FAILURE;
646 (void) putchar ('\n'); /* Hack to make shell's prompt start at left of screen */
648 return exit_code;
651 /* --------------------------------------------------------------------------------------------- */