Added unit test for do_panel_cd() functions.
[midnight-commander.git] / src / main.c
blob196191501705ac84b686008a90cfbc018c613eae
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 char *new_dir, enum cd_enum exact)
280 gboolean res;
281 const char *_new_dir = new_dir;
283 if (current_panel->is_panelized && _new_dir[0] == '.' && _new_dir[1] == '.' && _new_dir[2] == 0)
284 _new_dir = panelized_panel.root;
286 res = do_panel_cd (current_panel, _new_dir, exact);
288 #ifdef HAVE_CHARSET
289 if (res)
291 vfs_path_element_t *path_element;
293 path_element = vfs_path_get_by_index (current_panel->cwd_vpath, -1);
294 if (path_element->encoding != NULL)
295 current_panel->codepage = get_codepage_index (path_element->encoding);
296 else
297 current_panel->codepage = SELECT_CHARSET_NO_TRANSLATE;
299 #endif /* HAVE_CHARSET */
301 return res;
304 /* --------------------------------------------------------------------------------------------- */
306 #ifdef HAVE_SUBSHELL_SUPPORT
307 gboolean
308 do_load_prompt (void)
310 gboolean ret = FALSE;
312 if (!read_subshell_prompt ())
313 return ret;
315 /* Don't actually change the prompt if it's invisible */
316 if (top_dlg != NULL && ((Dlg_head *) top_dlg->data == midnight_dlg) && command_prompt)
318 setup_cmdline ();
320 /* since the prompt has changed, and we are called from one of the
321 * tty_get_event channels, the prompt updating does not take place
322 * automatically: force a cursor update and a screen refresh
324 update_cursor (midnight_dlg);
325 mc_refresh ();
326 ret = TRUE;
328 update_subshell_prompt = TRUE;
329 return ret;
332 /* --------------------------------------------------------------------------------------------- */
335 load_prompt (int fd, void *unused)
337 (void) fd;
338 (void) unused;
340 do_load_prompt ();
341 return 0;
343 #endif /* HAVE_SUBSHELL_SUPPORT */
345 /* --------------------------------------------------------------------------------------------- */
347 /** Show current directory in the xterm title */
348 void
349 update_xterm_title_path (void)
351 /* TODO: share code with midnight_get_title () */
353 const char *path;
354 char host[BUF_TINY];
355 char *p;
356 struct passwd *pw = NULL;
357 char *login = NULL;
358 int res = 0;
360 if (mc_global.tty.xterm_flag && xterm_title)
362 char *path_str;
364 path_str = vfs_path_to_str (current_panel->cwd_vpath);
365 path = strip_home_and_password (path_str);
366 g_free (path_str);
367 res = gethostname (host, sizeof (host));
368 if (res)
369 { /* On success, res = 0 */
370 host[0] = '\0';
372 else
374 host[sizeof (host) - 1] = '\0';
376 pw = getpwuid (getuid ());
377 if (pw)
379 login = g_strdup_printf ("%s@%s", pw->pw_name, host);
381 else
383 login = g_strdup (host);
385 p = g_strdup_printf ("mc [%s]:%s", login, path);
386 fprintf (stdout, "\33]0;%s\7", str_term_form (p));
387 g_free (login);
388 g_free (p);
389 if (!mc_global.tty.alternate_plus_minus)
390 numeric_keypad_mode ();
391 (void) fflush (stdout);
395 /* --------------------------------------------------------------------------------------------- */
398 main (int argc, char *argv[])
400 GError *error = NULL;
401 int exit_code = EXIT_FAILURE;
403 /* We had LC_CTYPE before, LC_ALL includs LC_TYPE as well */
404 (void) setlocale (LC_ALL, "");
405 (void) bindtextdomain ("mc", LOCALEDIR);
406 (void) textdomain ("mc");
408 /* do this before args parsing */
409 str_init_strings (NULL);
411 if (!mc_args_parse (&argc, &argv, "mc", &error))
413 startup_exit_falure:
414 fprintf (stderr, _("Failed to run:\n%s\n"), error->message);
415 g_error_free (error);
416 g_free (shell);
417 startup_exit_ok:
418 str_uninit_strings ();
419 return exit_code;
422 /* do this before mc_args_show_info () to view paths in the --datadir-info output */
423 OS_Setup ();
425 if (!g_path_is_absolute (mc_config_get_home_dir ()))
427 error = g_error_new (MC_ERROR, 0, "%s: %s", _("Home directory path is not absolute"),
428 mc_config_get_home_dir ());
429 mc_event_deinit (NULL);
430 goto startup_exit_falure;
433 if (!mc_args_show_info ())
435 exit_code = EXIT_SUCCESS;
436 goto startup_exit_ok;
439 if (!events_init (&error))
440 goto startup_exit_falure;
442 mc_config_init_config_paths (&error);
443 if (error == NULL && mc_config_deprecated_dir_present ())
444 mc_config_migrate_from_old_place (&error);
445 if (error != NULL)
447 mc_event_deinit (NULL);
448 goto startup_exit_falure;
451 vfs_init ();
452 vfs_plugins_init ();
453 vfs_setup_work_dir ();
455 /* do this after vfs initialization due to mc_setctl() call in mc_setup_by_args() */
456 if (!mc_setup_by_args (argc, argv, &error))
458 vfs_shut ();
459 mc_event_deinit (NULL);
460 goto startup_exit_falure;
463 /* check terminal type
464 * $TEMR must be set and not empty
465 * mc_global.tty.xterm_flag is used in init_key() and tty_init()
466 * Do this after mc_args_handle() where mc_args__force_xterm is set up.
468 mc_global.tty.xterm_flag = tty_check_term (mc_args__force_xterm);
470 /* NOTE: This has to be called before tty_init or whatever routine
471 calls any define_sequence */
472 init_key ();
474 /* Must be done before installing the SIGCHLD handler [[FIXME]] */
475 handle_console (CONSOLE_INIT);
477 #ifdef HAVE_SUBSHELL_SUPPORT
478 /* Don't use subshell when invoked as viewer or editor */
479 if (mc_global.mc_run_mode != MC_RUN_FULL)
480 mc_global.tty.use_subshell = FALSE;
482 if (mc_global.tty.use_subshell)
483 subshell_get_console_attributes ();
484 #endif /* HAVE_SUBSHELL_SUPPORT */
486 /* Install the SIGCHLD handler; must be done before init_subshell() */
487 init_sigchld ();
489 /* We need this, since ncurses endwin () doesn't restore the signals */
490 save_stop_handler ();
492 /* Must be done before init_subshell, to set up the terminal size: */
493 /* FIXME: Should be removed and LINES and COLS computed on subshell */
494 tty_init (!mc_args__nomouse, mc_global.tty.xterm_flag);
496 load_setup ();
498 /* start check mc_global.display_codepage and mc_global.source_codepage */
499 check_codeset ();
501 /* Removing this from the X code let's us type C-c */
502 load_key_defs ();
504 load_keymap_defs (!mc_args__nokeymap);
506 macros_list = g_array_new (TRUE, FALSE, sizeof (macros_t));
508 tty_init_colors (mc_global.tty.disable_colors, mc_args__force_colors);
510 mc_skin_init (&error);
511 if (error != NULL)
513 message (D_ERROR, _("Warning"), "%s", error->message);
514 g_error_free (error);
515 error = NULL;
518 mc_filehighlight = mc_fhl_new (TRUE);
519 dlg_set_default_colors ();
521 #ifdef HAVE_SUBSHELL_SUPPORT
522 /* Done here to ensure that the subshell doesn't */
523 /* inherit the file descriptors opened below, etc */
524 if (mc_global.tty.use_subshell)
525 init_subshell ();
527 #endif /* HAVE_SUBSHELL_SUPPORT */
529 /* Also done after init_subshell, to save any shell init file messages */
530 if (mc_global.tty.console_flag != '\0')
531 handle_console (CONSOLE_SAVE);
533 if (mc_global.tty.alternate_plus_minus)
534 application_keypad_mode ();
536 #ifdef HAVE_SUBSHELL_SUPPORT
537 if (mc_global.tty.use_subshell)
539 mc_prompt = strip_ctrl_codes (subshell_prompt);
540 if (mc_prompt == NULL)
541 mc_prompt = (geteuid () == 0) ? "# " : "$ ";
543 else
544 #endif /* HAVE_SUBSHELL_SUPPORT */
545 mc_prompt = (geteuid () == 0) ? "# " : "$ ";
547 /* Program main loop */
548 if (mc_global.midnight_shutdown)
549 exit_code = EXIT_SUCCESS;
550 else
551 exit_code = do_nc ()? EXIT_SUCCESS : EXIT_FAILURE;
553 /* Save the tree store */
554 (void) tree_store_save ();
556 free_keymap_defs ();
558 /* Virtual File System shutdown */
559 vfs_shut ();
561 flush_extension_file (); /* does only free memory */
563 mc_fhl_free (&mc_filehighlight);
564 mc_skin_deinit ();
565 tty_colors_done ();
567 tty_shutdown ();
569 done_setup ();
571 if (mc_global.tty.console_flag != '\0' && (quit & SUBSHELL_EXIT) == 0)
572 handle_console (CONSOLE_RESTORE);
573 if (mc_global.tty.alternate_plus_minus)
574 numeric_keypad_mode ();
576 (void) signal (SIGCHLD, SIG_DFL); /* Disable the SIGCHLD handler */
578 if (mc_global.tty.console_flag != '\0')
579 handle_console (CONSOLE_DONE);
581 if (mc_global.mc_run_mode == MC_RUN_FULL && mc_args__last_wd_file != NULL
582 && last_wd_string != NULL && !print_last_revert)
584 int last_wd_fd;
586 last_wd_fd = open (mc_args__last_wd_file, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL,
587 S_IRUSR | S_IWUSR);
588 if (last_wd_fd != -1)
590 ssize_t ret1;
591 int ret2;
592 ret1 = write (last_wd_fd, last_wd_string, strlen (last_wd_string));
593 ret2 = close (last_wd_fd);
596 g_free (last_wd_string);
598 g_free (shell);
600 done_key ();
602 if (macros_list != NULL)
604 guint i;
605 macros_t *macros;
606 for (i = 0; i < macros_list->len; i++)
608 macros = &g_array_index (macros_list, struct macros_t, i);
609 if (macros != NULL && macros->macro != NULL)
610 (void) g_array_free (macros->macro, FALSE);
612 (void) g_array_free (macros_list, TRUE);
615 str_uninit_strings ();
617 g_free (mc_run_param0);
618 g_free (mc_run_param1);
620 mc_config_deinit_config_paths ();
622 (void) mc_event_deinit (&error);
623 if (error != NULL)
625 fprintf (stderr, _("\nFailed while close:\n%s\n"), error->message);
626 g_error_free (error);
627 exit_code = EXIT_FAILURE;
630 (void) putchar ('\n'); /* Hack to make shell's prompt start at left of screen */
632 return exit_code;
635 /* --------------------------------------------------------------------------------------------- */