Manual pages: mcmcdiffmceditmcview

root/src/main.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. check_codeset
  2. OS_Setup
  3. sigchld_handler_no_subshell
  4. init_sigchld
  5. check_sid
  6. main

   1 /*
   2    Main program for the Midnight Commander
   3 
   4    Copyright (C) 1994-2025
   5    Free Software Foundation, Inc.
   6 
   7    Written by:
   8    Miguel de Icaza, 1994, 1995, 1996, 1997
   9    Janne Kukonlehto, 1994, 1995
  10    Norbert Warmuth, 1997
  11 
  12    This file is part of the Midnight Commander.
  13 
  14    The Midnight Commander is free software: you can redistribute it
  15    and/or modify it under the terms of the GNU General Public License as
  16    published by the Free Software Foundation, either version 3 of the License,
  17    or (at your option) any later version.
  18 
  19    The Midnight Commander is distributed in the hope that it will be useful,
  20    but WITHOUT ANY WARRANTY; without even the implied warranty of
  21    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  22    GNU General Public License for more details.
  23 
  24    You should have received a copy of the GNU General Public License
  25    along with this program.  If not, see <https://www.gnu.org/licenses/>.
  26  */
  27 
  28 /** \file main.c
  29  *  \brief Source: this is a main module
  30  */
  31 
  32 #include <config.h>
  33 
  34 #include <ctype.h>
  35 #include <errno.h>
  36 #include <locale.h>
  37 #include <pwd.h>  // for username in xterm title
  38 #include <stdio.h>
  39 #include <stdlib.h>
  40 #include <string.h>
  41 #include <sys/types.h>
  42 #include <sys/wait.h>
  43 #include <signal.h>
  44 #include <unistd.h>  // getsid()
  45 
  46 #include "lib/global.h"
  47 
  48 #include "lib/event.h"
  49 #include "lib/tty/tty.h"
  50 #include "lib/tty/key.h"    // For init_key()
  51 #include "lib/tty/mouse.h"  // init_mouse()
  52 #include "lib/skin.h"
  53 #include "lib/filehighlight.h"
  54 #include "lib/fileloc.h"
  55 #include "lib/strutil.h"
  56 #include "lib/util.h"
  57 #include "lib/vfs/vfs.h"  // vfs_init(), vfs_shut()
  58 
  59 #include "filemanager/filemanager.h"
  60 #include "filemanager/treestore.h"  // tree_store_save
  61 #include "filemanager/layout.h"
  62 #include "filemanager/ext.h"      // flush_extension_file()
  63 #include "filemanager/command.h"  // cmdline
  64 #include "filemanager/panel.h"    // panalized_panel
  65 #include "filemanager/filenot.h"  // my_rmdir()
  66 
  67 #ifdef USE_INTERNAL_EDIT
  68 #include "editor/edit.h"  // edit_arg_free()
  69 #endif
  70 
  71 #include "vfs/plugins_init.h"
  72 
  73 #include "events_init.h"
  74 #include "args.h"
  75 #ifdef ENABLE_SUBSHELL
  76 #include "subshell/subshell.h"
  77 #endif
  78 #include "keymap.h"
  79 #include "setup.h"  // load_setup()
  80 
  81 #include "lib/charsets.h"
  82 #include "selcodepage.h"
  83 
  84 #include "consaver/cons.saver.h"  // cons_saver_pid
  85 
  86 /*** global variables ****************************************************************************/
  87 
  88 /*** file scope macro definitions ****************************************************************/
  89 
  90 /*** file scope type declarations ****************************************************************/
  91 
  92 /*** forward declarations (file scope functions) *************************************************/
  93 
  94 /*** file scope variables ************************************************************************/
  95 
  96 /* --------------------------------------------------------------------------------------------- */
  97 /*** file scope functions ************************************************************************/
  98 /* --------------------------------------------------------------------------------------------- */
  99 
 100 static void
 101 check_codeset (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 102 {
 103     const char *current_system_codepage = NULL;
 104 
 105     current_system_codepage = str_detect_termencoding ();
 106 
 107     {
 108         const char *_display_codepage;
 109 
 110         _display_codepage = get_codepage_id (mc_global.display_codepage);
 111 
 112         if (strcmp (_display_codepage, current_system_codepage) != 0)
 113         {
 114             mc_global.display_codepage = get_codepage_index (current_system_codepage);
 115             if (mc_global.display_codepage == -1)
 116                 mc_global.display_codepage = 0;
 117 
 118             mc_config_set_string (mc_global.main_config, CONFIG_MISC_SECTION, "display_codepage",
 119                                   cp_display);
 120         }
 121     }
 122 
 123     mc_global.utf8_display = str_isutf8 (current_system_codepage);
 124 }
 125 
 126 /* --------------------------------------------------------------------------------------------- */
 127 /** POSIX version.  The only version we support.  */
 128 static void
 129 OS_Setup (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 130 {
 131     mc_shell_init ();
 132 
 133     // This is the directory, where MC was installed, on Unix this is DATADIR
 134     // and can be overridden by the MC_DATADIR environment variable
 135     const char *datadir_env = g_getenv ("MC_DATADIR");
 136 
 137     if (datadir_env != NULL)
 138         mc_global.sysconfig_dir = g_strdup (datadir_env);
 139     else
 140         mc_global.sysconfig_dir = g_strdup (SYSCONFDIR);
 141 
 142     mc_global.share_data_dir = g_strdup (DATADIR);
 143 }
 144 
 145 /* --------------------------------------------------------------------------------------------- */
 146 
 147 static void
 148 sigchld_handler_no_subshell (int sig)
     /* [previous][next][first][last][top][bottom][index][help]  */
 149 {
 150 #ifdef __linux__
 151     int pid, status;
 152 
 153     if (mc_global.tty.console_flag == '\0')
 154         return;
 155 
 156     /* COMMENT: if it were true that after the call to handle_console(..INIT)
 157        the value of mc_global.tty.console_flag never changed, we could simply not install
 158        this handler at all if (!mc_global.tty.console_flag && !mc_global.tty.use_subshell). */
 159 
 160     /* That comment is no longer true.  We need to wait() on a sigchld
 161        handler (that's at least what the tarfs code expects currently). */
 162 
 163     pid = waitpid (cons_saver_pid, &status, WUNTRACED | WNOHANG);
 164 
 165     if (pid == cons_saver_pid)
 166     {
 167         if (WIFSTOPPED (status))
 168         {
 169             // Someone has stopped cons.saver - restart it
 170             kill (pid, SIGCONT);
 171         }
 172         else
 173         {
 174             // cons.saver has died - disable console saving
 175             handle_console (CONSOLE_DONE);
 176             mc_global.tty.console_flag = '\0';
 177         }
 178     }
 179     // If we got here, some other child exited; ignore it
 180 #endif
 181 
 182     (void) sig;
 183 }
 184 
 185 /* --------------------------------------------------------------------------------------------- */
 186 
 187 static void
 188 init_sigchld (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 189 {
 190     struct sigaction sigchld_action;
 191 
 192     memset (&sigchld_action, 0, sizeof (sigchld_action));
 193     sigchld_action.sa_handler =
 194 #ifdef ENABLE_SUBSHELL
 195         mc_global.tty.use_subshell ? sigchld_handler :
 196 #endif
 197                                    sigchld_handler_no_subshell;
 198 
 199     sigemptyset (&sigchld_action.sa_mask);
 200 
 201 #ifdef SA_RESTART
 202     sigchld_action.sa_flags = SA_RESTART;
 203 #endif
 204 
 205     if (my_sigaction (SIGCHLD, &sigchld_action, NULL) == -1)
 206     {
 207 #ifdef ENABLE_SUBSHELL
 208         /*
 209          * This may happen on QNX Neutrino 6, where SA_RESTART
 210          * is defined but not implemented.  Fallback to no subshell.
 211          */
 212         mc_global.tty.use_subshell = FALSE;
 213 #endif
 214     }
 215 }
 216 
 217 /* --------------------------------------------------------------------------------------------- */
 218 /**
 219  * Check MC_SID to prevent running one mc from another.
 220  *
 221  * @return TRUE if no parent mc in our session was found, FALSE otherwise.
 222  */
 223 
 224 static gboolean
 225 check_sid (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 226 {
 227     pid_t my_sid, old_sid;
 228     const char *sid_str;
 229 
 230     sid_str = getenv ("MC_SID");
 231     if (sid_str == NULL)
 232         return TRUE;
 233 
 234     old_sid = (pid_t) strtol (sid_str, NULL, 0);
 235     if (old_sid == 0)
 236         return TRUE;
 237 
 238     my_sid = getsid (0);
 239     if (my_sid == -1)
 240         return TRUE;
 241 
 242     // The parent mc is in a different session, it's OK
 243     return (old_sid != my_sid);
 244 }
 245 
 246 /* --------------------------------------------------------------------------------------------- */
 247 /*** public functions ****************************************************************************/
 248 /* --------------------------------------------------------------------------------------------- */
 249 
 250 int
 251 main (int argc, char *argv[])
     /* [previous][next][first][last][top][bottom][index][help]  */
 252 {
 253     GError *mcerror = NULL;
 254     int exit_code = EXIT_FAILURE;
 255     const char *tmpdir = NULL;
 256 
 257     mc_global.run_from_parent_mc = !check_sid ();
 258 
 259     // We had LC_CTYPE before, LC_ALL includs LC_TYPE as well
 260 #ifdef HAVE_SETLOCALE
 261     (void) setlocale (LC_ALL, "");
 262 #endif
 263     (void) bindtextdomain (PACKAGE, LOCALEDIR);
 264     (void) textdomain (PACKAGE);
 265 
 266     // do this before args parsing
 267     str_init_strings (NULL);
 268 
 269     mc_setup_run_mode (argv);  // are we mc? editor? viewer? etc...
 270 
 271     if (!mc_args_parse (&argc, &argv, "mc", &mcerror))
 272     {
 273     startup_exit_falure:
 274         fprintf (stderr, _ ("Failed to run:\n%s\n"), mcerror->message);
 275         g_error_free (mcerror);
 276     startup_exit_ok:
 277         mc_shell_deinit ();
 278         str_uninit_strings ();
 279         return exit_code;
 280     }
 281 
 282     /* check terminal type
 283      * $TERM must be set and not empty
 284      * mc_global.tty.xterm_flag is used in init_key() and tty_init()
 285      * Do this after mc_args_parse() where mc_args__force_xterm is set up.
 286      */
 287     mc_global.tty.xterm_flag = tty_check_xterm_compat (mc_args__force_xterm);
 288 
 289     // do this before mc_args_show_info () to view paths in the --datadir-info output
 290     OS_Setup ();
 291 
 292     if (!g_path_is_absolute (mc_config_get_home_dir ()))
 293     {
 294         mc_propagate_error (&mcerror, 0, "%s: %s", _ ("Home directory path is not absolute"),
 295                             mc_config_get_home_dir ());
 296         mc_event_deinit (NULL);
 297         goto startup_exit_falure;
 298     }
 299 
 300     if (!mc_args_show_info ())
 301     {
 302         exit_code = EXIT_SUCCESS;
 303         goto startup_exit_ok;
 304     }
 305 
 306     if (!events_init (&mcerror))
 307         goto startup_exit_falure;
 308 
 309     mc_config_init_config_paths (&mcerror);
 310     if (mcerror != NULL)
 311     {
 312         mc_event_deinit (NULL);
 313         goto startup_exit_falure;
 314     }
 315 
 316     vfs_init ();
 317     vfs_plugins_init ();
 318 
 319     load_setup ();
 320 
 321     // Must be done after load_setup because depends on mc_global.vfs.cd_symlinks
 322     vfs_setup_work_dir ();
 323 
 324     // Set up temporary directory after VFS initialization
 325     tmpdir = mc_tmpdir ();
 326 
 327     /* do this after vfs initialization and vfs working directory setup
 328        due to mc_setctl() and mcedit_arg_vpath_new() calls in mc_setup_by_args() */
 329     if (!mc_setup_by_args (argc, argv, &mcerror))
 330     {
 331         /* At exit, do this before vfs_shut():
 332            normally, temporary directory should be empty */
 333         vfs_expire (TRUE);
 334         (void) my_rmdir (tmpdir);
 335 
 336         vfs_shut ();
 337         done_setup ();
 338         g_free (saved_other_dir);
 339         mc_event_deinit (NULL);
 340         goto startup_exit_falure;
 341     }
 342 
 343     /* Resolve the other_dir panel option.
 344      * 1. Must be done after vfs_setup_work_dir().
 345      * 2. Must be done after mc_setup_by_args() because of mc_run_mode.
 346      */
 347     if (mc_global.mc_run_mode == MC_RUN_FULL)
 348     {
 349         char *buffer;
 350         vfs_path_t *vpath;
 351 
 352         buffer = mc_config_get_string (mc_global.panels_config, "Dirs", "other_dir", ".");
 353         vpath = vfs_path_from_str (buffer);
 354         if (vfs_file_is_local (vpath))
 355             saved_other_dir = buffer;
 356         else
 357             g_free (buffer);
 358         vfs_path_free (vpath, TRUE);
 359     }
 360 
 361     /* NOTE: This has to be called before tty_init or whatever routine
 362        calls any define_sequence */
 363     init_key ();
 364 
 365     // Must be done before installing the SIGCHLD handler [[FIXME]]
 366     handle_console (CONSOLE_INIT);
 367 
 368 #ifdef ENABLE_SUBSHELL
 369     // Disallow subshell when invoked as standalone viewer or editor from running mc
 370     if (mc_global.mc_run_mode != MC_RUN_FULL && mc_global.run_from_parent_mc)
 371         mc_global.tty.use_subshell = FALSE;
 372 
 373     if (mc_global.tty.use_subshell)
 374         subshell_get_console_attributes ();
 375 #endif
 376 
 377     // Install the SIGCHLD handler; must be done before init_subshell()
 378     init_sigchld ();
 379 
 380     // We need this, since ncurses endwin () doesn't restore the signals
 381     save_stop_handler ();
 382 
 383     // Must be done before init_subshell, to set up the terminal size:
 384     // FIXME: Should be removed and LINES and COLS computed on subshell
 385     tty_init (!mc_args__nomouse, mc_global.tty.xterm_flag);
 386 
 387     // start check mc_global.display_codepage and mc_global.source_codepage
 388     check_codeset ();
 389 
 390     // Removing this from the X code let's us type C-c
 391     load_key_defs ();
 392 
 393     keymap_load (!mc_args__nokeymap);
 394 
 395 #ifdef USE_INTERNAL_EDIT
 396     macros_list = g_array_new (TRUE, FALSE, sizeof (macros_t));
 397 #endif
 398 
 399     tty_init_colors (mc_global.tty.disable_colors, mc_args__force_colors);
 400 
 401     mc_skin_init (NULL, &mcerror);
 402     dlg_set_default_colors ();
 403     input_set_default_colors ();
 404     if (mc_global.mc_run_mode == MC_RUN_FULL)
 405         command_set_default_colors ();
 406 
 407     mc_error_message (&mcerror, NULL);
 408 
 409 #ifdef ENABLE_SUBSHELL
 410     // Done here to ensure that the subshell doesn't
 411     // inherit the file descriptors opened below, etc
 412     if (mc_global.tty.use_subshell && mc_global.run_from_parent_mc)
 413     {
 414         const int quit_mc =
 415             query_dialog (_ ("Warning"),
 416                           _ ("GNU Midnight Commander\nis already running on this terminal.\n"
 417                              "Subshell support will be disabled."),
 418                           D_ERROR, 2, _ ("&OK"), _ ("&Quit"));
 419 
 420         if (quit_mc != 0)
 421         {
 422             // parent mc was found and the user wants to quit mc
 423             mc_global.midnight_shutdown = TRUE;
 424         }
 425 
 426         // quit or not, disable nested subshell
 427         mc_global.tty.use_subshell = FALSE;
 428     }
 429 
 430     if (mc_global.tty.use_subshell)
 431         init_subshell ();
 432 #endif
 433 
 434     if (!mc_global.midnight_shutdown)
 435     {
 436         // Also done after init_subshell, to save any shell init file messages
 437         if (mc_global.tty.console_flag != '\0')
 438             handle_console (CONSOLE_SAVE);
 439 
 440         if (mc_global.tty.alternate_plus_minus)
 441             application_keypad_mode ();
 442 
 443         /* Done after subshell initialization to allow select and paste text by mouse
 444            w/o Shift button in subshell in the native console */
 445         init_mouse ();
 446 
 447         /* Done after tty_enter_ca_mode (tty_init) because in VTE bracketed mode is
 448            separate for the normal and alternate screens */
 449         enable_bracketed_paste ();
 450 
 451         // subshell_prompt is NULL here
 452         mc_prompt = (geteuid () == 0) ? "# " : "$ ";
 453     }
 454 
 455     // Program main loop
 456     if (mc_global.midnight_shutdown)
 457         exit_code = EXIT_SUCCESS;
 458     else
 459         exit_code = do_nc () ? EXIT_SUCCESS : EXIT_FAILURE;
 460 
 461     disable_bracketed_paste ();
 462 
 463     disable_mouse ();
 464 
 465     // Save the tree store
 466     (void) tree_store_save ();
 467 
 468     keymap_free ();
 469 
 470     /* At exit, do this before vfs_shut():
 471        normally, temporary directory should be empty */
 472     vfs_expire (TRUE);
 473     (void) my_rmdir (tmpdir);
 474 
 475     // Virtual File System shutdown
 476     vfs_shut ();
 477 
 478     flush_extension_file ();  // does only free memory
 479 
 480     mc_skin_deinit ();
 481     tty_colors_done ();
 482 
 483     tty_shutdown ();
 484 
 485     done_setup ();
 486 
 487     if (mc_global.tty.console_flag != '\0' && (quit & SUBSHELL_EXIT) == 0)
 488         handle_console (CONSOLE_RESTORE);
 489     if (mc_global.tty.alternate_plus_minus)
 490         numeric_keypad_mode ();
 491 
 492     (void) my_signal (SIGCHLD, SIG_DFL);  // Disable the SIGCHLD handler
 493 
 494     if (mc_global.tty.console_flag != '\0')
 495         handle_console (CONSOLE_DONE);
 496 
 497     if (mc_global.mc_run_mode == MC_RUN_FULL && mc_args__last_wd_file != NULL && last_wd_str != NULL
 498         && !print_last_revert)
 499     {
 500         const int last_wd_fd =
 501             open (mc_args__last_wd_file, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
 502         if (last_wd_fd != -1)
 503         {
 504             MC_UNUSED const ssize_t ret1 = write (last_wd_fd, last_wd_str, strlen (last_wd_str));
 505             MC_UNUSED const int ret2 = close (last_wd_fd);
 506         }
 507     }
 508 
 509     g_free (last_wd_str);
 510 
 511     mc_shell_deinit ();
 512 
 513     done_key ();
 514 
 515 #ifdef USE_INTERNAL_EDIT
 516     if (macros_list != NULL)
 517     {
 518         guint i;
 519 
 520         for (i = 0; i < macros_list->len; i++)
 521         {
 522             macros_t *macros;
 523 
 524             macros = &g_array_index (macros_list, struct macros_t, i);
 525             if (macros != NULL && macros->macro != NULL)
 526                 (void) g_array_free (macros->macro, TRUE);
 527         }
 528         (void) g_array_free (macros_list, TRUE);
 529     }
 530 #endif
 531 
 532     str_uninit_strings ();
 533 
 534     if (mc_global.mc_run_mode != MC_RUN_EDITOR)
 535         g_free (mc_run_param0);
 536 #ifdef USE_INTERNAL_EDIT
 537     else
 538         g_list_free_full ((GList *) mc_run_param0, (GDestroyNotify) edit_arg_free);
 539 #endif
 540 
 541     g_free (mc_run_param1);
 542     g_free (saved_other_dir);
 543 
 544     mc_config_deinit_config_paths ();
 545 
 546     (void) mc_event_deinit (&mcerror);
 547     if (mcerror != NULL)
 548     {
 549         fprintf (stderr, _ ("\nFailed while close:\n%s\n"), mcerror->message);
 550         g_error_free (mcerror);
 551         exit_code = EXIT_FAILURE;
 552     }
 553 
 554     (void) putchar ('\n');  // Hack to make shell's prompt start at left of screen
 555 
 556     return exit_code;
 557 }
 558 
 559 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */