editor: add option to show EOL
[midnight-commander.git] / src / main.c
blob77b41f9203b58e219b485329429800fb128f578b
1 /* Main program for the Midnight Commander
2 Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
3 2003, 2004, 2005, 2006, 2007, 2009 Free Software Foundation, Inc.
5 Written by: 1994, 1995, 1996, 1997 Miguel de Icaza
6 1994, 1995 Janne Kukonlehto
7 1997 Norbert Warmuth
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
23 /** \file main.c
24 * \brief Source: this is a main module
27 #include <config.h>
29 #include <ctype.h>
30 #include <errno.h>
31 #include <locale.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <fcntl.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <sys/wait.h>
39 #include <unistd.h>
40 #include <pwd.h> /* for username in xterm title */
42 #include "lib/global.h"
44 #include "lib/event.h"
45 #include "lib/tty/tty.h"
46 #include "lib/tty/key.h" /* For init_key() */
47 #include "lib/tty/win.h" /* xterm_flag */
48 #include "lib/skin.h"
49 #include "lib/filehighlight.h"
50 #include "lib/fileloc.h"
51 #include "lib/strutil.h"
52 #include "lib/util.h"
53 #include "lib/vfs/vfs.h" /* vfs_init(), vfs_shut() */
55 #include "filemanager/midnight.h" /* current_panel */
56 #include "filemanager/treestore.h" /* tree_store_save */
57 #include "filemanager/layout.h" /* command_prompt */
58 #include "filemanager/ext.h" /* flush_extension_file() */
59 #include "filemanager/command.h" /* cmdline */
61 #include "vfs/plugins_init.h"
63 #include "events_init.h"
64 #include "args.h"
65 #include "subshell.h"
66 #include "setup.h" /* load_setup() */
68 #ifdef HAVE_CHARSET
69 #include "lib/charsets.h"
70 #include "selcodepage.h"
71 #endif /* HAVE_CHARSET */
73 #include "consaver/cons.saver.h" /* cons_saver_pid */
75 #include "main.h"
77 /*** global variables ****************************************************************************/
79 mc_fhl_t *mc_filehighlight;
81 /* Set when main loop should be terminated */
82 int quit = 0;
84 #ifdef HAVE_CHARSET
85 /* Numbers of (file I/O) and (input/display) codepages. -1 if not selected */
86 int default_source_codepage = -1;
87 char *autodetect_codeset = NULL;
88 gboolean is_autodetect_codeset_enabled = FALSE;
89 #endif /* !HAVE_CHARSET */
91 /* If true use the internal viewer */
92 int use_internal_view = 1;
93 /* If set, use the builtin editor */
94 int use_internal_edit = 1;
96 char *mc_run_param0 = NULL;
97 char *mc_run_param1 = NULL;
99 /* The user's shell */
100 char *shell = NULL;
102 /* The prompt */
103 const char *mc_prompt = NULL;
105 /* Set to TRUE to suppress printing the last directory */
106 int print_last_revert = FALSE;
108 /* If set, then print to the given file the last directory we were at */
109 char *last_wd_string = NULL;
111 /* some widget decoration */
112 char *widget_btn_left_sign = NULL;
113 char *widget_btn_right_sign = NULL;
114 char *widget_btn_left_default_sign = NULL;
115 char *widget_btn_right_default_sign = NULL;
117 int widget_edit_tab_first = '<';
118 int widget_edit_tab_middle = '-';
119 int widget_edit_tab_last = '>';
120 int widget_edit_eol_char = ' ';
122 /* index to record_macro_buf[], -1 if not recording a macro */
123 int macro_index = -1;
125 GArray *macros_list;
127 /*** file scope macro definitions ****************************************************************/
129 /*** file scope type declarations ****************************************************************/
131 /*** file scope variables ************************************************************************/
133 /*** file scope functions ************************************************************************/
134 /* --------------------------------------------------------------------------------------------- */
136 static void
137 check_codeset (void)
139 const char *current_system_codepage = NULL;
141 current_system_codepage = str_detect_termencoding ();
143 #ifdef HAVE_CHARSET
145 const char *_display_codepage;
147 _display_codepage = get_codepage_id (mc_global.display_codepage);
149 if (strcmp (_display_codepage, current_system_codepage) != 0)
151 mc_global.display_codepage = get_codepage_index (current_system_codepage);
152 if (mc_global.display_codepage == -1)
153 mc_global.display_codepage = 0;
155 mc_config_set_string (mc_main_config, "Misc", "display_codepage", cp_display);
158 #endif
160 mc_global.utf8_display = str_isutf8 (current_system_codepage);
163 /* --------------------------------------------------------------------------------------------- */
165 /** POSIX version. The only version we support. */
166 static void
167 OS_Setup (void)
169 const char *shell_env = getenv ("SHELL");
171 if ((shell_env == NULL) || (shell_env[0] == '\0'))
173 struct passwd *pwd;
174 pwd = getpwuid (geteuid ());
175 if (pwd != NULL)
176 shell = g_strdup (pwd->pw_shell);
178 else
179 shell = g_strdup (shell_env);
181 if ((shell == NULL) || (shell[0] == '\0'))
183 g_free (shell);
184 shell = g_strdup ("/bin/sh");
188 /* --------------------------------------------------------------------------------------------- */
190 static void
191 sigchld_handler_no_subshell (int sig)
193 #ifdef __linux__
194 int pid, status;
196 if (!mc_global.tty.console_flag)
197 return;
199 /* COMMENT: if it were true that after the call to handle_console(..INIT)
200 the value of mc_global.tty.console_flag never changed, we could simply not install
201 this handler at all if (!mc_global.tty.console_flag && !mc_global.tty.use_subshell). */
203 /* That comment is no longer true. We need to wait() on a sigchld
204 handler (that's at least what the tarfs code expects currently). */
206 pid = waitpid (cons_saver_pid, &status, WUNTRACED | WNOHANG);
208 if (pid == cons_saver_pid)
211 if (WIFSTOPPED (status))
213 /* Someone has stopped cons.saver - restart it */
214 kill (pid, SIGCONT);
216 else
218 /* cons.saver has died - disable console saving */
219 handle_console (CONSOLE_DONE);
220 mc_global.tty.console_flag = '\0';
223 /* If we got here, some other child exited; ignore it */
224 #endif /* __linux__ */
226 (void) sig;
229 /* --------------------------------------------------------------------------------------------- */
231 static void
232 init_sigchld (void)
234 struct sigaction sigchld_action;
236 sigchld_action.sa_handler =
237 #ifdef HAVE_SUBSHELL_SUPPORT
238 mc_global.tty.use_subshell ? sigchld_handler :
239 #endif /* HAVE_SUBSHELL_SUPPORT */
240 sigchld_handler_no_subshell;
242 sigemptyset (&sigchld_action.sa_mask);
244 #ifdef SA_RESTART
245 sigchld_action.sa_flags = SA_RESTART;
246 #else
247 sigchld_action.sa_flags = 0;
248 #endif /* !SA_RESTART */
250 if (sigaction (SIGCHLD, &sigchld_action, NULL) == -1)
252 #ifdef HAVE_SUBSHELL_SUPPORT
254 * This may happen on QNX Neutrino 6, where SA_RESTART
255 * is defined but not implemented. Fallback to no subshell.
257 mc_global.tty.use_subshell = FALSE;
258 #endif /* HAVE_SUBSHELL_SUPPORT */
262 /* --------------------------------------------------------------------------------------------- */
263 /*** public functions ****************************************************************************/
264 /* --------------------------------------------------------------------------------------------- */
267 do_cd (const char *new_dir, enum cd_enum exact)
269 gboolean res;
271 res = do_panel_cd (current_panel, new_dir, exact);
273 #if HAVE_CHARSET
274 if (res)
276 vfs_path_t *vpath = vfs_path_from_str (current_panel->cwd);
277 vfs_path_element_t *path_element = vfs_path_get_by_index (vpath, -1);
279 if (path_element->encoding != NULL)
280 current_panel->codepage = get_codepage_index (path_element->encoding);
281 else
282 current_panel->codepage = SELECT_CHARSET_NO_TRANSLATE;
284 vfs_path_free (vpath);
286 #endif /* HAVE_CHARSET */
288 return res ? 1 : 0;
291 /* --------------------------------------------------------------------------------------------- */
293 #ifdef HAVE_SUBSHELL_SUPPORT
295 load_prompt (int fd, void *unused)
297 (void) fd;
298 (void) unused;
300 if (!read_subshell_prompt ())
301 return 0;
303 /* Don't actually change the prompt if it's invisible */
304 if (((Dlg_head *) top_dlg->data == midnight_dlg) && command_prompt)
306 char *tmp_prompt;
307 int prompt_len;
309 tmp_prompt = strip_ctrl_codes (subshell_prompt);
310 prompt_len = str_term_width1 (tmp_prompt);
312 /* Check for prompts too big */
313 if (COLS > 8 && prompt_len > COLS - 8)
315 prompt_len = COLS - 8;
316 tmp_prompt[prompt_len] = '\0';
318 mc_prompt = tmp_prompt;
319 label_set_text (the_prompt, mc_prompt);
320 input_set_origin ((WInput *) cmdline, prompt_len, COLS - prompt_len);
322 /* since the prompt has changed, and we are called from one of the
323 * tty_get_event channels, the prompt updating does not take place
324 * automatically: force a cursor update and a screen refresh
326 update_cursor (midnight_dlg);
327 mc_refresh ();
329 update_subshell_prompt = TRUE;
330 return 0;
332 #endif /* HAVE_SUBSHELL_SUPPORT */
334 /* --------------------------------------------------------------------------------------------- */
336 /** Show current directory in the xterm title */
337 void
338 update_xterm_title_path (void)
340 /* TODO: share code with midnight_get_title () */
342 const char *path;
343 char host[BUF_TINY];
344 char *p;
345 struct passwd *pw = NULL;
346 char *login = NULL;
347 int res = 0;
349 if (xterm_flag && xterm_title)
351 path = strip_home_and_password (current_panel->cwd);
352 res = gethostname (host, sizeof (host));
353 if (res)
354 { /* On success, res = 0 */
355 host[0] = '\0';
357 else
359 host[sizeof (host) - 1] = '\0';
361 pw = getpwuid (getuid ());
362 if (pw)
364 login = g_strdup_printf ("%s@%s", pw->pw_name, host);
366 else
368 login = g_strdup (host);
370 p = g_strdup_printf ("mc [%s]:%s", login, path);
371 fprintf (stdout, "\33]0;%s\7", str_term_form (p));
372 g_free (login);
373 g_free (p);
374 if (!alternate_plus_minus)
375 numeric_keypad_mode ();
376 fflush (stdout);
380 /* --------------------------------------------------------------------------------------------- */
383 main (int argc, char *argv[])
385 GError *error = NULL;
386 gboolean isInitialized;
388 /* We had LC_CTYPE before, LC_ALL includs LC_TYPE as well */
389 setlocale (LC_ALL, "");
390 bindtextdomain ("mc", LOCALEDIR);
391 textdomain ("mc");
393 if (!events_init (&error))
395 fprintf (stderr, _("Failed to run:\n%s\n"), error->message);
396 g_error_free (error);
397 (void) mc_event_deinit (NULL);
398 exit (EXIT_FAILURE);
401 /* Set up temporary directory */
402 mc_tmpdir ();
404 OS_Setup ();
406 str_init_strings (NULL);
408 /* Initialize and create home directories */
409 /* do it after the screen library initialization to show the error message */
410 mc_config_init_config_paths (&error);
412 if (error == NULL && mc_config_deprecated_dir_present ())
413 mc_config_migrate_from_old_place (&error);
415 vfs_init ();
416 vfs_plugins_init ();
417 vfs_setup_work_dir ();
419 if (!mc_args_handle (argc, argv, "mc"))
420 exit (EXIT_FAILURE);
422 /* NOTE: This has to be called before tty_init or whatever routine
423 calls any define_sequence */
424 init_key ();
426 /* Must be done before installing the SIGCHLD handler [[FIXME]] */
427 handle_console (CONSOLE_INIT);
429 #ifdef HAVE_SUBSHELL_SUPPORT
430 /* Don't use subshell when invoked as viewer or editor */
431 if (mc_global.mc_run_mode != MC_RUN_FULL)
432 mc_global.tty.use_subshell = FALSE;
434 if (mc_global.tty.use_subshell)
435 subshell_get_console_attributes ();
436 #endif /* HAVE_SUBSHELL_SUPPORT */
438 /* Install the SIGCHLD handler; must be done before init_subshell() */
439 init_sigchld ();
441 /* We need this, since ncurses endwin () doesn't restore the signals */
442 save_stop_handler ();
444 /* Must be done before init_subshell, to set up the terminal size: */
445 /* FIXME: Should be removed and LINES and COLS computed on subshell */
446 tty_init (mc_global.args.slow_terminal, mc_global.args.ugly_line_drawing);
448 load_setup ();
450 /* start check mc_global.display_codepage and mc_global.source_codepage */
451 check_codeset ();
453 /* Removing this from the X code let's us type C-c */
454 load_key_defs ();
456 load_keymap_defs (!mc_args__nokeymap);
458 macros_list = g_array_new (TRUE, FALSE, sizeof (macros_t));
460 tty_init_colors (mc_global.args.disable_colors, mc_args__force_colors);
463 GError *error2 = NULL;
464 isInitialized = mc_skin_init (&error2);
465 mc_filehighlight = mc_fhl_new (TRUE);
466 dlg_set_default_colors ();
467 dlg_load_skin_decor ();
468 if (!isInitialized)
470 message (D_ERROR, _("Warning"), "%s", error2->message);
471 g_error_free (error2);
472 error2 = NULL;
476 if (error != NULL)
478 message (D_ERROR, _("Warning"), "%s", error->message);
479 g_error_free (error);
480 error = NULL;
484 #ifdef HAVE_SUBSHELL_SUPPORT
485 /* Done here to ensure that the subshell doesn't */
486 /* inherit the file descriptors opened below, etc */
487 if (mc_global.tty.use_subshell)
488 init_subshell ();
490 #endif /* HAVE_SUBSHELL_SUPPORT */
492 /* Also done after init_subshell, to save any shell init file messages */
493 if (mc_global.tty.console_flag)
494 handle_console (CONSOLE_SAVE);
496 if (alternate_plus_minus)
497 application_keypad_mode ();
499 #ifdef HAVE_SUBSHELL_SUPPORT
500 if (mc_global.tty.use_subshell)
502 mc_prompt = strip_ctrl_codes (subshell_prompt);
503 if (mc_prompt == NULL)
504 mc_prompt = (geteuid () == 0) ? "# " : "$ ";
506 else
507 #endif /* HAVE_SUBSHELL_SUPPORT */
508 mc_prompt = (geteuid () == 0) ? "# " : "$ ";
510 /* Program main loop */
511 if (!mc_global.widget.midnight_shutdown)
512 do_nc ();
514 /* Save the tree store */
515 tree_store_save ();
517 free_keymap_defs ();
519 /* Virtual File System shutdown */
520 vfs_shut ();
522 flush_extension_file (); /* does only free memory */
524 mc_fhl_free (&mc_filehighlight);
525 mc_skin_deinit ();
526 tty_colors_done ();
528 tty_shutdown ();
530 done_setup ();
532 if (mc_global.tty.console_flag && (quit & SUBSHELL_EXIT) == 0)
533 handle_console (CONSOLE_RESTORE);
534 if (alternate_plus_minus)
535 numeric_keypad_mode ();
537 signal (SIGCHLD, SIG_DFL); /* Disable the SIGCHLD handler */
539 if (mc_global.tty.console_flag)
540 handle_console (CONSOLE_DONE);
542 if (mc_global.mc_run_mode == MC_RUN_FULL && mc_args__last_wd_file != NULL
543 && last_wd_string != NULL && !print_last_revert)
545 int last_wd_fd;
547 last_wd_fd = open (mc_args__last_wd_file, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL,
548 S_IRUSR | S_IWUSR);
549 if (last_wd_fd != -1)
551 ssize_t ret1;
552 int ret2;
553 ret1 = write (last_wd_fd, last_wd_string, strlen (last_wd_string));
554 ret2 = close (last_wd_fd);
557 g_free (last_wd_string);
559 g_free (shell);
561 done_key ();
563 if (macros_list != NULL)
565 guint i;
566 macros_t *macros;
567 for (i = 0; i < macros_list->len; i++)
569 macros = &g_array_index (macros_list, struct macros_t, i);
570 if (macros != NULL && macros->macro != NULL)
571 g_array_free (macros->macro, FALSE);
573 g_array_free (macros_list, TRUE);
576 str_uninit_strings ();
578 g_free (mc_run_param0);
579 g_free (mc_run_param1);
581 mc_event_deinit (&error);
583 mc_config_deinit_config_paths ();
585 if (error != NULL)
587 fprintf (stderr, _("\nFailed while close:\n%s\n"), error->message);
588 g_error_free (error);
589 exit (EXIT_FAILURE);
592 putchar ('\n'); /* Hack to make shell's prompt start at left of screen */
594 return 0;
597 /* --------------------------------------------------------------------------------------------- */