Ticket 1551: Update GPL version from 2 to 3
[midnight-commander.git] / src / main.c
blob9d442f2fb5214ea480139452437af177d7c5add8
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 */
63 #include "vfs/plugins_init.h"
65 #include "events_init.h"
66 #include "args.h"
67 #include "subshell.h"
68 #include "setup.h" /* load_setup() */
70 #ifdef HAVE_CHARSET
71 #include "lib/charsets.h"
72 #include "selcodepage.h"
73 #endif /* HAVE_CHARSET */
75 #include "consaver/cons.saver.h" /* cons_saver_pid */
77 #include "main.h"
79 /*** global variables ****************************************************************************/
81 mc_fhl_t *mc_filehighlight;
83 /* Set when main loop should be terminated */
84 int quit = 0;
86 #ifdef HAVE_CHARSET
87 /* Numbers of (file I/O) and (input/display) codepages. -1 if not selected */
88 int default_source_codepage = -1;
89 char *autodetect_codeset = NULL;
90 gboolean is_autodetect_codeset_enabled = FALSE;
91 #endif /* !HAVE_CHARSET */
93 /* If true use the internal viewer */
94 int use_internal_view = 1;
95 /* If set, use the builtin editor */
96 int use_internal_edit = 1;
98 char *mc_run_param0 = NULL;
99 char *mc_run_param1 = NULL;
101 /* The user's shell */
102 char *shell = NULL;
104 /* The prompt */
105 const char *mc_prompt = NULL;
107 /* Set to TRUE to suppress printing the last directory */
108 int print_last_revert = FALSE;
110 /* If set, then print to the given file the last directory we were at */
111 char *last_wd_string = NULL;
113 /* index to record_macro_buf[], -1 if not recording a macro */
114 int macro_index = -1;
116 /* macro stuff */
117 struct macro_action_t record_macro_buf[MAX_MACRO_LENGTH];
119 GArray *macros_list;
121 /*** file scope macro definitions ****************************************************************/
123 /*** file scope type declarations ****************************************************************/
125 /*** file scope variables ************************************************************************/
127 /*** file scope functions ************************************************************************/
128 /* --------------------------------------------------------------------------------------------- */
130 static void
131 check_codeset (void)
133 const char *current_system_codepage = NULL;
135 current_system_codepage = str_detect_termencoding ();
137 #ifdef HAVE_CHARSET
139 const char *_display_codepage;
141 _display_codepage = get_codepage_id (mc_global.display_codepage);
143 if (strcmp (_display_codepage, current_system_codepage) != 0)
145 mc_global.display_codepage = get_codepage_index (current_system_codepage);
146 if (mc_global.display_codepage == -1)
147 mc_global.display_codepage = 0;
149 mc_config_set_string (mc_main_config, "Misc", "display_codepage", cp_display);
152 #endif
154 mc_global.utf8_display = str_isutf8 (current_system_codepage);
157 /* --------------------------------------------------------------------------------------------- */
159 /** POSIX version. The only version we support. */
160 static void
161 OS_Setup (void)
163 const char *shell_env = getenv ("SHELL");
165 if ((shell_env == NULL) || (shell_env[0] == '\0'))
167 struct passwd *pwd;
168 pwd = getpwuid (geteuid ());
169 if (pwd != NULL)
170 shell = g_strdup (pwd->pw_shell);
172 else
173 shell = g_strdup (shell_env);
175 if ((shell == NULL) || (shell[0] == '\0'))
177 g_free (shell);
178 shell = g_strdup ("/bin/sh");
182 /* --------------------------------------------------------------------------------------------- */
184 static void
185 sigchld_handler_no_subshell (int sig)
187 #ifdef __linux__
188 int pid, status;
190 if (!mc_global.tty.console_flag != '\0')
191 return;
193 /* COMMENT: if it were true that after the call to handle_console(..INIT)
194 the value of mc_global.tty.console_flag never changed, we could simply not install
195 this handler at all if (!mc_global.tty.console_flag && !mc_global.tty.use_subshell). */
197 /* That comment is no longer true. We need to wait() on a sigchld
198 handler (that's at least what the tarfs code expects currently). */
200 pid = waitpid (cons_saver_pid, &status, WUNTRACED | WNOHANG);
202 if (pid == cons_saver_pid)
205 if (WIFSTOPPED (status))
207 /* Someone has stopped cons.saver - restart it */
208 kill (pid, SIGCONT);
210 else
212 /* cons.saver has died - disable console saving */
213 handle_console (CONSOLE_DONE);
214 mc_global.tty.console_flag = '\0';
217 /* If we got here, some other child exited; ignore it */
218 #endif /* __linux__ */
220 (void) sig;
223 /* --------------------------------------------------------------------------------------------- */
225 static void
226 init_sigchld (void)
228 struct sigaction sigchld_action;
230 sigchld_action.sa_handler =
231 #ifdef HAVE_SUBSHELL_SUPPORT
232 mc_global.tty.use_subshell ? sigchld_handler :
233 #endif /* HAVE_SUBSHELL_SUPPORT */
234 sigchld_handler_no_subshell;
236 sigemptyset (&sigchld_action.sa_mask);
238 #ifdef SA_RESTART
239 sigchld_action.sa_flags = SA_RESTART;
240 #else
241 sigchld_action.sa_flags = 0;
242 #endif /* !SA_RESTART */
244 if (sigaction (SIGCHLD, &sigchld_action, NULL) == -1)
246 #ifdef HAVE_SUBSHELL_SUPPORT
248 * This may happen on QNX Neutrino 6, where SA_RESTART
249 * is defined but not implemented. Fallback to no subshell.
251 mc_global.tty.use_subshell = FALSE;
252 #endif /* HAVE_SUBSHELL_SUPPORT */
256 /* --------------------------------------------------------------------------------------------- */
257 /*** public functions ****************************************************************************/
258 /* --------------------------------------------------------------------------------------------- */
261 do_cd (const char *new_dir, enum cd_enum exact)
263 gboolean res;
265 res = do_panel_cd (current_panel, new_dir, exact);
267 #if HAVE_CHARSET
268 if (res)
270 vfs_path_t *vpath = vfs_path_from_str (current_panel->cwd);
271 vfs_path_element_t *path_element = vfs_path_get_by_index (vpath, -1);
273 if (path_element->encoding != NULL)
274 current_panel->codepage = get_codepage_index (path_element->encoding);
275 else
276 current_panel->codepage = SELECT_CHARSET_NO_TRANSLATE;
278 vfs_path_free (vpath);
280 #endif /* HAVE_CHARSET */
282 return res ? 1 : 0;
285 /* --------------------------------------------------------------------------------------------- */
287 #ifdef HAVE_SUBSHELL_SUPPORT
289 load_prompt (int fd, void *unused)
291 (void) fd;
292 (void) unused;
294 if (!read_subshell_prompt ())
295 return 0;
297 /* Don't actually change the prompt if it's invisible */
298 if (((Dlg_head *) top_dlg->data == midnight_dlg) && command_prompt)
300 char *tmp_prompt;
301 int prompt_len;
303 tmp_prompt = strip_ctrl_codes (subshell_prompt);
304 prompt_len = str_term_width1 (tmp_prompt);
306 /* Check for prompts too big */
307 if (COLS > 8 && prompt_len > COLS - 8)
309 prompt_len = COLS - 8;
310 tmp_prompt[prompt_len] = '\0';
312 mc_prompt = tmp_prompt;
313 label_set_text (the_prompt, mc_prompt);
314 input_set_origin ((WInput *) cmdline, prompt_len, COLS - prompt_len);
316 /* since the prompt has changed, and we are called from one of the
317 * tty_get_event channels, the prompt updating does not take place
318 * automatically: force a cursor update and a screen refresh
320 update_cursor (midnight_dlg);
321 mc_refresh ();
323 update_subshell_prompt = TRUE;
324 return 0;
326 #endif /* HAVE_SUBSHELL_SUPPORT */
328 /* --------------------------------------------------------------------------------------------- */
330 /** Show current directory in the xterm title */
331 void
332 update_xterm_title_path (void)
334 /* TODO: share code with midnight_get_title () */
336 const char *path;
337 char host[BUF_TINY];
338 char *p;
339 struct passwd *pw = NULL;
340 char *login = NULL;
341 int res = 0;
343 if (mc_global.tty.xterm_flag && xterm_title)
345 path = strip_home_and_password (current_panel->cwd);
346 res = gethostname (host, sizeof (host));
347 if (res)
348 { /* On success, res = 0 */
349 host[0] = '\0';
351 else
353 host[sizeof (host) - 1] = '\0';
355 pw = getpwuid (getuid ());
356 if (pw)
358 login = g_strdup_printf ("%s@%s", pw->pw_name, host);
360 else
362 login = g_strdup (host);
364 p = g_strdup_printf ("mc [%s]:%s", login, path);
365 fprintf (stdout, "\33]0;%s\7", str_term_form (p));
366 g_free (login);
367 g_free (p);
368 if (!mc_global.tty.alternate_plus_minus)
369 numeric_keypad_mode ();
370 (void) fflush (stdout);
374 /* --------------------------------------------------------------------------------------------- */
377 main (int argc, char *argv[])
379 GError *error = NULL;
380 gboolean isInitialized;
382 /* We had LC_CTYPE before, LC_ALL includs LC_TYPE as well */
383 (void) setlocale (LC_ALL, "");
384 (void) bindtextdomain ("mc", LOCALEDIR);
385 (void) textdomain ("mc");
387 if (!events_init (&error))
389 fprintf (stderr, _("Failed to run:\n%s\n"), error->message);
390 g_error_free (error);
391 (void) mc_event_deinit (NULL);
392 exit (EXIT_FAILURE);
395 /* Set up temporary directory */
396 (void) mc_tmpdir ();
398 OS_Setup ();
400 str_init_strings (NULL);
402 /* Initialize and create home directories */
403 /* do it after the screen library initialization to show the error message */
404 mc_config_init_config_paths (&error);
406 if (error == NULL && mc_config_deprecated_dir_present ())
407 mc_config_migrate_from_old_place (&error);
409 vfs_init ();
410 vfs_plugins_init ();
411 vfs_setup_work_dir ();
413 if (!mc_args_handle (argc, argv, "mc"))
414 exit (EXIT_FAILURE);
416 /* check terminal type
417 * $TEMR must be set and not empty
418 * mc_global.tty.xterm_flag is used in init_key() and tty_init()
419 * Do this after mc_args_handle() where mc_args__force_xterm is set up.
421 mc_global.tty.xterm_flag = tty_check_term (mc_args__force_xterm);
423 /* NOTE: This has to be called before tty_init or whatever routine
424 calls any define_sequence */
425 init_key ();
427 /* Must be done before installing the SIGCHLD handler [[FIXME]] */
428 handle_console (CONSOLE_INIT);
430 #ifdef HAVE_SUBSHELL_SUPPORT
431 /* Don't use subshell when invoked as viewer or editor */
432 if (mc_global.mc_run_mode != MC_RUN_FULL)
433 mc_global.tty.use_subshell = FALSE;
435 if (mc_global.tty.use_subshell)
436 subshell_get_console_attributes ();
437 #endif /* HAVE_SUBSHELL_SUPPORT */
439 /* Install the SIGCHLD handler; must be done before init_subshell() */
440 init_sigchld ();
442 /* We need this, since ncurses endwin () doesn't restore the signals */
443 save_stop_handler ();
445 /* Must be done before init_subshell, to set up the terminal size: */
446 /* FIXME: Should be removed and LINES and COLS computed on subshell */
447 tty_init (!mc_args__nomouse, mc_global.tty.xterm_flag);
449 load_setup ();
451 /* start check mc_global.display_codepage and mc_global.source_codepage */
452 check_codeset ();
454 /* Removing this from the X code let's us type C-c */
455 load_key_defs ();
457 load_keymap_defs (!mc_args__nokeymap);
459 macros_list = g_array_new (TRUE, FALSE, sizeof (macros_t));
461 tty_init_colors (mc_global.tty.disable_colors, mc_args__force_colors);
464 GError *error2 = NULL;
465 isInitialized = mc_skin_init (&error2);
466 mc_filehighlight = mc_fhl_new (TRUE);
467 dlg_set_default_colors ();
469 if (!isInitialized)
471 message (D_ERROR, _("Warning"), "%s", error2->message);
472 g_error_free (error2);
473 error2 = NULL;
477 if (error != NULL)
479 message (D_ERROR, _("Warning"), "%s", error->message);
480 g_error_free (error);
481 error = NULL;
485 #ifdef HAVE_SUBSHELL_SUPPORT
486 /* Done here to ensure that the subshell doesn't */
487 /* inherit the file descriptors opened below, etc */
488 if (mc_global.tty.use_subshell)
489 init_subshell ();
491 #endif /* HAVE_SUBSHELL_SUPPORT */
493 /* Also done after init_subshell, to save any shell init file messages */
494 if (mc_global.tty.console_flag != '\0')
495 handle_console (CONSOLE_SAVE);
497 if (mc_global.tty.alternate_plus_minus)
498 application_keypad_mode ();
500 #ifdef HAVE_SUBSHELL_SUPPORT
501 if (mc_global.tty.use_subshell)
503 mc_prompt = strip_ctrl_codes (subshell_prompt);
504 if (mc_prompt == NULL)
505 mc_prompt = (geteuid () == 0) ? "# " : "$ ";
507 else
508 #endif /* HAVE_SUBSHELL_SUPPORT */
509 mc_prompt = (geteuid () == 0) ? "# " : "$ ";
511 /* Program main loop */
512 if (!mc_global.widget.midnight_shutdown)
513 do_nc ();
515 /* Save the tree store */
516 (void) tree_store_save ();
518 free_keymap_defs ();
520 /* Virtual File System shutdown */
521 vfs_shut ();
523 flush_extension_file (); /* does only free memory */
525 mc_fhl_free (&mc_filehighlight);
526 mc_skin_deinit ();
527 tty_colors_done ();
529 tty_shutdown ();
531 done_setup ();
533 if (mc_global.tty.console_flag != '\0' && (quit & SUBSHELL_EXIT) == 0)
534 handle_console (CONSOLE_RESTORE);
535 if (mc_global.tty.alternate_plus_minus)
536 numeric_keypad_mode ();
538 (void) signal (SIGCHLD, SIG_DFL); /* Disable the SIGCHLD handler */
540 if (mc_global.tty.console_flag != '\0')
541 handle_console (CONSOLE_DONE);
543 if (mc_global.mc_run_mode == MC_RUN_FULL && mc_args__last_wd_file != NULL
544 && last_wd_string != NULL && !print_last_revert)
546 int last_wd_fd;
548 last_wd_fd = open (mc_args__last_wd_file, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL,
549 S_IRUSR | S_IWUSR);
550 if (last_wd_fd != -1)
552 ssize_t ret1;
553 int ret2;
554 ret1 = write (last_wd_fd, last_wd_string, strlen (last_wd_string));
555 ret2 = close (last_wd_fd);
558 g_free (last_wd_string);
560 g_free (shell);
562 done_key ();
564 if (macros_list != NULL)
566 guint i;
567 macros_t *macros;
568 for (i = 0; i < macros_list->len; i++)
570 macros = &g_array_index (macros_list, struct macros_t, i);
571 if (macros != NULL && macros->macro != NULL)
572 (void) g_array_free (macros->macro, FALSE);
574 (void) g_array_free (macros_list, TRUE);
577 str_uninit_strings ();
579 g_free (mc_run_param0);
580 g_free (mc_run_param1);
582 (void) mc_event_deinit (&error);
584 mc_config_deinit_config_paths ();
586 if (error != NULL)
588 fprintf (stderr, _("\nFailed while close:\n%s\n"), error->message);
589 g_error_free (error);
590 exit (EXIT_FAILURE);
593 (void) putchar ('\n'); /* Hack to make shell's prompt start at left of screen */
595 return 0;
598 /* --------------------------------------------------------------------------------------------- */