(fish_file_store): type accuracy to avoid counter overflow while copiyng huge files.
[midnight-commander.git] / src / main.c
blob71482e75c8a36c7bdfb4b9388b30ff77773cf314
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 #ifdef HAVE_SETLOCALE
421 (void) setlocale (LC_ALL, "");
422 #endif
423 (void) bindtextdomain (PACKAGE, LOCALEDIR);
424 (void) textdomain (PACKAGE);
426 /* do this before args parsing */
427 str_init_strings (NULL);
429 if (!mc_args_parse (&argc, &argv, "mc", &error))
431 startup_exit_falure:
432 fprintf (stderr, _("Failed to run:\n%s\n"), error->message);
433 g_error_free (error);
434 g_free (shell);
435 startup_exit_ok:
436 str_uninit_strings ();
437 return exit_code;
440 /* do this before mc_args_show_info () to view paths in the --datadir-info output */
441 OS_Setup ();
443 if (!g_path_is_absolute (mc_config_get_home_dir ()))
445 error = g_error_new (MC_ERROR, 0, "%s: %s", _("Home directory path is not absolute"),
446 mc_config_get_home_dir ());
447 mc_event_deinit (NULL);
448 goto startup_exit_falure;
451 if (!mc_args_show_info ())
453 exit_code = EXIT_SUCCESS;
454 goto startup_exit_ok;
457 if (!events_init (&error))
458 goto startup_exit_falure;
460 mc_config_init_config_paths (&error);
461 if (error == NULL && mc_config_deprecated_dir_present ())
462 mc_config_migrate_from_old_place (&error);
463 if (error != NULL)
465 mc_event_deinit (NULL);
466 goto startup_exit_falure;
469 vfs_init ();
470 vfs_plugins_init ();
471 vfs_setup_work_dir ();
473 /* do this after vfs initialization due to mc_setctl() call in mc_setup_by_args() */
474 if (!mc_setup_by_args (argc, argv, &error))
476 vfs_shut ();
477 mc_event_deinit (NULL);
478 goto startup_exit_falure;
481 /* check terminal type
482 * $TEMR must be set and not empty
483 * mc_global.tty.xterm_flag is used in init_key() and tty_init()
484 * Do this after mc_args_handle() where mc_args__force_xterm is set up.
486 mc_global.tty.xterm_flag = tty_check_term (mc_args__force_xterm);
488 /* NOTE: This has to be called before tty_init or whatever routine
489 calls any define_sequence */
490 init_key ();
492 /* Must be done before installing the SIGCHLD handler [[FIXME]] */
493 handle_console (CONSOLE_INIT);
495 #ifdef HAVE_SUBSHELL_SUPPORT
496 /* Don't use subshell when invoked as viewer or editor */
497 if (mc_global.mc_run_mode != MC_RUN_FULL)
498 mc_global.tty.use_subshell = FALSE;
500 if (mc_global.tty.use_subshell)
501 subshell_get_console_attributes ();
502 #endif /* HAVE_SUBSHELL_SUPPORT */
504 /* Install the SIGCHLD handler; must be done before init_subshell() */
505 init_sigchld ();
507 /* We need this, since ncurses endwin () doesn't restore the signals */
508 save_stop_handler ();
510 /* Must be done before init_subshell, to set up the terminal size: */
511 /* FIXME: Should be removed and LINES and COLS computed on subshell */
512 tty_init (!mc_args__nomouse, mc_global.tty.xterm_flag);
514 load_setup ();
516 /* start check mc_global.display_codepage and mc_global.source_codepage */
517 check_codeset ();
519 /* Removing this from the X code let's us type C-c */
520 load_key_defs ();
522 load_keymap_defs (!mc_args__nokeymap);
524 macros_list = g_array_new (TRUE, FALSE, sizeof (macros_t));
526 tty_init_colors (mc_global.tty.disable_colors, mc_args__force_colors);
528 mc_skin_init (&error);
529 if (error != NULL)
531 message (D_ERROR, _("Warning"), "%s", error->message);
532 g_error_free (error);
533 error = NULL;
536 mc_filehighlight = mc_fhl_new (TRUE);
537 dlg_set_default_colors ();
539 #ifdef HAVE_SUBSHELL_SUPPORT
540 /* Done here to ensure that the subshell doesn't */
541 /* inherit the file descriptors opened below, etc */
542 if (mc_global.tty.use_subshell)
543 init_subshell ();
545 #endif /* HAVE_SUBSHELL_SUPPORT */
547 /* Also done after init_subshell, to save any shell init file messages */
548 if (mc_global.tty.console_flag != '\0')
549 handle_console (CONSOLE_SAVE);
551 if (mc_global.tty.alternate_plus_minus)
552 application_keypad_mode ();
554 #ifdef HAVE_SUBSHELL_SUPPORT
555 if (mc_global.tty.use_subshell)
557 mc_prompt = strip_ctrl_codes (subshell_prompt);
558 if (mc_prompt == NULL)
559 mc_prompt = (geteuid () == 0) ? "# " : "$ ";
561 else
562 #endif /* HAVE_SUBSHELL_SUPPORT */
563 mc_prompt = (geteuid () == 0) ? "# " : "$ ";
565 /* Program main loop */
566 if (mc_global.midnight_shutdown)
567 exit_code = EXIT_SUCCESS;
568 else
569 exit_code = do_nc ()? EXIT_SUCCESS : EXIT_FAILURE;
571 /* Save the tree store */
572 (void) tree_store_save ();
574 free_keymap_defs ();
576 /* Virtual File System shutdown */
577 vfs_shut ();
579 flush_extension_file (); /* does only free memory */
581 mc_fhl_free (&mc_filehighlight);
582 mc_skin_deinit ();
583 tty_colors_done ();
585 tty_shutdown ();
587 done_setup ();
589 if (mc_global.tty.console_flag != '\0' && (quit & SUBSHELL_EXIT) == 0)
590 handle_console (CONSOLE_RESTORE);
591 if (mc_global.tty.alternate_plus_minus)
592 numeric_keypad_mode ();
594 (void) signal (SIGCHLD, SIG_DFL); /* Disable the SIGCHLD handler */
596 if (mc_global.tty.console_flag != '\0')
597 handle_console (CONSOLE_DONE);
599 if (mc_global.mc_run_mode == MC_RUN_FULL && mc_args__last_wd_file != NULL
600 && last_wd_string != NULL && !print_last_revert)
602 int last_wd_fd;
604 last_wd_fd = open (mc_args__last_wd_file, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL,
605 S_IRUSR | S_IWUSR);
606 if (last_wd_fd != -1)
608 ssize_t ret1;
609 int ret2;
610 ret1 = write (last_wd_fd, last_wd_string, strlen (last_wd_string));
611 ret2 = close (last_wd_fd);
614 g_free (last_wd_string);
616 g_free (shell);
618 done_key ();
620 if (macros_list != NULL)
622 guint i;
623 macros_t *macros;
624 for (i = 0; i < macros_list->len; i++)
626 macros = &g_array_index (macros_list, struct macros_t, i);
627 if (macros != NULL && macros->macro != NULL)
628 (void) g_array_free (macros->macro, FALSE);
630 (void) g_array_free (macros_list, TRUE);
633 str_uninit_strings ();
635 g_free (mc_run_param0);
636 g_free (mc_run_param1);
638 mc_config_deinit_config_paths ();
640 (void) mc_event_deinit (&error);
641 if (error != NULL)
643 fprintf (stderr, _("\nFailed while close:\n%s\n"), error->message);
644 g_error_free (error);
645 exit_code = EXIT_FAILURE;
648 (void) putchar ('\n'); /* Hack to make shell's prompt start at left of screen */
650 return exit_code;
653 /* --------------------------------------------------------------------------------------------- */