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

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