Manual pages: mcmcdiffmceditmcview

root/src/filemanager/ext.c

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

DEFINITIONS

This source file includes following definitions.
  1. exec_cleanup_script
  2. exec_cleanup_file_name
  3. exec_get_file_name
  4. exec_expand_format
  5. exec_get_export_variables
  6. exec_make_shell_string
  7. exec_extension_view
  8. exec_extension_cd
  9. exec_extension
  10. get_popen_information
  11. get_file_type_local
  12. get_file_encoding_local
  13. regex_check_type
  14. check_old_extension_file
  15. load_extension_file
  16. flush_extension_file
  17. regex_command_for

   1 /*
   2    Extension dependent execution.
   3 
   4    Copyright (C) 1994-2025
   5    Free Software Foundation, Inc.
   6 
   7    Written by:
   8    Jakub Jelinek, 1995
   9    Miguel de Icaza, 1994
  10    Slava Zanko <slavazanko@gmail.com>, 2013
  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  ext.c
  29  *  \brief Source: extension dependent execution
  30  */
  31 
  32 #include <config.h>
  33 
  34 #include <ctype.h>
  35 #include <errno.h>
  36 #include <stdlib.h>
  37 #include <stdio.h>
  38 #include <string.h>
  39 #include <unistd.h>
  40 
  41 #include "lib/global.h"
  42 #include "lib/tty/tty.h"
  43 #include "lib/search.h"
  44 #include "lib/fileloc.h"
  45 #include "lib/mcconfig.h"
  46 #include "lib/util.h"
  47 #include "lib/vfs/vfs.h"
  48 #include "lib/widget.h"
  49 #include "lib/charsets.h"  // get_codepage_index
  50 
  51 #ifdef USE_FILE_CMD
  52 #include "src/setup.h"  // use_file_to_check_type
  53 #endif
  54 #include "src/execute.h"
  55 #include "src/history.h"
  56 #include "src/usermenu.h"
  57 
  58 #include "src/consaver/cons.saver.h"
  59 #include "src/viewer/mcviewer.h"
  60 #include "src/selcodepage.h"  // do_set_codepage
  61 
  62 #include "filemanager.h"  // current_panel
  63 #include "panel.h"        // panel_cd
  64 
  65 #include "ext.h"
  66 
  67 /*** global variables ****************************************************************************/
  68 
  69 /*** file scope macro definitions ****************************************************************/
  70 
  71 #ifdef USE_FILE_CMD
  72 #ifdef FILE_B
  73 #define FILE_CMD "file -z " FILE_B FILE_S FILE_L
  74 #else
  75 #define FILE_CMD "file -z " FILE_S FILE_L
  76 #endif
  77 #endif
  78 
  79 /*** file scope type declarations ****************************************************************/
  80 
  81 typedef char *(*quote_func_t) (const char *name, gboolean quote_percent);
  82 
  83 /*** forward declarations (file scope functions) *************************************************/
  84 
  85 /*** file scope variables ************************************************************************/
  86 
  87 /* This variable points to a copy of the mc.ext file in memory
  88  * With this we avoid loading/parsing the file each time we
  89  * need it
  90  */
  91 static mc_config_t *ext_ini = NULL;
  92 static gchar **ext_ini_groups = NULL;
  93 static vfs_path_t *localfilecopy_vpath = NULL;
  94 static char buffer[BUF_1K];
  95 
  96 static char *pbuffer = NULL;
  97 static time_t localmtime = 0;
  98 static quote_func_t quote_func = name_quote;
  99 static gboolean run_view = FALSE;
 100 static gboolean is_cd = FALSE;
 101 static gboolean written_nonspace = FALSE;
 102 static gboolean do_local_copy = FALSE;
 103 
 104 static const char *descr_group = "mc.ext.ini";
 105 static const char *default_group = "Default";
 106 
 107 /* --------------------------------------------------------------------------------------------- */
 108 /*** file scope functions ************************************************************************/
 109 /* --------------------------------------------------------------------------------------------- */
 110 
 111 static void
 112 exec_cleanup_script (vfs_path_t *script_vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 113 {
 114     if (script_vpath != NULL)
 115     {
 116         (void) mc_unlink (script_vpath);
 117         vfs_path_free (script_vpath, TRUE);
 118     }
 119 }
 120 
 121 /* --------------------------------------------------------------------------------------------- */
 122 
 123 static void
 124 exec_cleanup_file_name (const vfs_path_t *filename_vpath, gboolean has_changed)
     /* [previous][next][first][last][top][bottom][index][help]  */
 125 {
 126     if (localfilecopy_vpath == NULL)
 127         return;
 128 
 129     if (has_changed)
 130     {
 131         struct stat mystat;
 132 
 133         mc_stat (localfilecopy_vpath, &mystat);
 134         has_changed = localmtime != mystat.st_mtime;
 135     }
 136     mc_ungetlocalcopy (filename_vpath, localfilecopy_vpath, has_changed);
 137     vfs_path_free (localfilecopy_vpath, TRUE);
 138     localfilecopy_vpath = NULL;
 139 }
 140 
 141 /* --------------------------------------------------------------------------------------------- */
 142 
 143 static char *
 144 exec_get_file_name (const vfs_path_t *filename_vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 145 {
 146     if (!do_local_copy)
 147         return quote_func (vfs_path_get_last_path_str (filename_vpath), FALSE);
 148 
 149     if (localfilecopy_vpath == NULL)
 150     {
 151         struct stat mystat;
 152         localfilecopy_vpath = mc_getlocalcopy (filename_vpath);
 153         if (localfilecopy_vpath == NULL)
 154             return NULL;
 155 
 156         mc_stat (localfilecopy_vpath, &mystat);
 157         localmtime = mystat.st_mtime;
 158     }
 159 
 160     return quote_func (vfs_path_get_last_path_str (localfilecopy_vpath), FALSE);
 161 }
 162 
 163 /* --------------------------------------------------------------------------------------------- */
 164 
 165 static char *
 166 exec_expand_format (char symbol, gboolean is_result_quoted)
     /* [previous][next][first][last][top][bottom][index][help]  */
 167 {
 168     char *text;
 169 
 170     text = expand_format (NULL, symbol, TRUE);
 171     if (is_result_quoted && text != NULL)
 172     {
 173         char *quoted_text;
 174 
 175         quoted_text = g_strdup_printf ("\"%s\"", text);
 176         g_free (text);
 177         text = quoted_text;
 178     }
 179     return text;
 180 }
 181 
 182 /* --------------------------------------------------------------------------------------------- */
 183 
 184 static GString *
 185 exec_get_export_variables (const vfs_path_t *filename_vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 186 {
 187     char *text;
 188     GString *export_vars_string;
 189     size_t i;
 190 
 191     struct
 192     {
 193         const char symbol;
 194         const char *name;
 195         const gboolean is_result_quoted;
 196     } export_variables[] = {
 197         { 'p', "MC_EXT_BASENAME", FALSE },
 198         { 'd', "MC_EXT_CURRENTDIR", FALSE },
 199         { 's', "MC_EXT_SELECTED", TRUE },
 200         { 't', "MC_EXT_ONLYTAGGED", TRUE },
 201         { '\0', NULL, FALSE },
 202     };
 203 
 204     text = exec_get_file_name (filename_vpath);
 205     if (text == NULL)
 206         return NULL;
 207 
 208     export_vars_string = g_string_new ("MC_EXT_FILENAME=");
 209     g_string_append_printf (export_vars_string, "%s\nexport MC_EXT_FILENAME\n", text);
 210     g_free (text);
 211 
 212     for (i = 0; export_variables[i].name != NULL; i++)
 213     {
 214         text =
 215             exec_expand_format (export_variables[i].symbol, export_variables[i].is_result_quoted);
 216         if (text != NULL)
 217         {
 218             g_string_append_printf (export_vars_string, "%s=%s\nexport %s\n",
 219                                     export_variables[i].name, text, export_variables[i].name);
 220             g_free (text);
 221         }
 222     }
 223 
 224     return export_vars_string;
 225 }
 226 
 227 /* --------------------------------------------------------------------------------------------- */
 228 
 229 static GString *
 230 exec_make_shell_string (const char *lc_data, const vfs_path_t *filename_vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 231 {
 232     GString *shell_string;
 233     char lc_prompt[80] = "\0";
 234     gboolean parameter_found = FALSE;
 235     gboolean expand_prefix_found = FALSE;
 236 
 237     shell_string = g_string_new ("");
 238 
 239     for (; *lc_data != '\0' && *lc_data != '\n'; lc_data++)
 240     {
 241         if (parameter_found)
 242         {
 243             if (*lc_data == '}')
 244             {
 245                 char *parameter;
 246 
 247                 parameter_found = FALSE;
 248                 parameter = input_dialog (_ ("Parameter"), lc_prompt, MC_HISTORY_EXT_PARAMETER, "",
 249                                           INPUT_COMPLETE_NONE);
 250                 if (parameter == NULL)
 251                 {
 252                     // User canceled
 253                     g_string_free (shell_string, TRUE);
 254                     exec_cleanup_file_name (filename_vpath, FALSE);
 255                     return NULL;
 256                 }
 257                 g_string_append (shell_string, parameter);
 258                 written_nonspace = TRUE;
 259                 g_free (parameter);
 260             }
 261             else
 262             {
 263                 size_t len = strlen (lc_prompt);
 264 
 265                 if (len < sizeof (lc_prompt) - 1)
 266                 {
 267                     lc_prompt[len] = *lc_data;
 268                     lc_prompt[len + 1] = '\0';
 269                 }
 270             }
 271         }
 272         else if (expand_prefix_found)
 273         {
 274             expand_prefix_found = FALSE;
 275             if (*lc_data == '{')
 276                 parameter_found = TRUE;
 277             else
 278             {
 279                 int i;
 280 
 281                 i = check_format_view (lc_data);
 282                 if (i != 0)
 283                 {
 284                     lc_data += i - 1;
 285                     run_view = TRUE;
 286                 }
 287                 else
 288                 {
 289                     i = check_format_cd (lc_data);
 290                     if (i > 0)
 291                     {
 292                         is_cd = TRUE;
 293                         quote_func = fake_name_quote;
 294                         do_local_copy = FALSE;
 295                         pbuffer = buffer;
 296                         lc_data += i - 1;
 297                     }
 298                     else
 299                     {
 300                         char *v;
 301 
 302                         i = check_format_var (lc_data, &v);
 303                         if (i > 0)
 304                         {
 305                             g_string_append (shell_string, v);
 306                             g_free (v);
 307                             lc_data += i;
 308                         }
 309                         else
 310                         {
 311                             char *text;
 312 
 313                             if (*lc_data != 'f')
 314                                 text = expand_format (NULL, *lc_data, !is_cd);
 315                             else
 316                             {
 317                                 text = exec_get_file_name (filename_vpath);
 318                                 if (text == NULL)
 319                                 {
 320                                     g_string_free (shell_string, TRUE);
 321                                     return NULL;
 322                                 }
 323                             }
 324 
 325                             if (text != NULL)
 326                             {
 327                                 if (!is_cd)
 328                                     g_string_append (shell_string, text);
 329                                 else
 330                                 {
 331                                     strcpy (pbuffer, text);
 332                                     pbuffer = strchr (pbuffer, '\0');
 333                                 }
 334 
 335                                 g_free (text);
 336                             }
 337 
 338                             written_nonspace = TRUE;
 339                         }
 340                     }
 341                 }
 342             }
 343         }
 344         else if (*lc_data == '%')
 345             expand_prefix_found = TRUE;
 346         else
 347         {
 348             if (!whitespace (*lc_data))
 349                 written_nonspace = TRUE;
 350             if (is_cd)
 351                 *(pbuffer++) = *lc_data;
 352             else
 353                 g_string_append_c (shell_string, *lc_data);
 354         }
 355     }  // for
 356 
 357     return shell_string;
 358 }
 359 
 360 /* --------------------------------------------------------------------------------------------- */
 361 
 362 static void
 363 exec_extension_view (void *target, char *cmd, const vfs_path_t *filename_vpath, int start_line)
     /* [previous][next][first][last][top][bottom][index][help]  */
 364 {
 365     mcview_mode_flags_t def_flags = {
 366         .wrap = FALSE,
 367         .hex = mcview_global_flags.hex,
 368         .magic = FALSE,
 369         .nroff = mcview_global_flags.nroff,
 370     };
 371 
 372     mcview_mode_flags_t changed_flags;
 373 
 374     mcview_clear_mode_flags (&changed_flags);
 375     mcview_altered_flags.hex = FALSE;
 376     mcview_altered_flags.nroff = FALSE;
 377     if (def_flags.hex != mcview_global_flags.hex)
 378         changed_flags.hex = TRUE;
 379     if (def_flags.nroff != mcview_global_flags.nroff)
 380         changed_flags.nroff = TRUE;
 381 
 382     if (target == NULL)
 383         mcview_viewer (cmd, filename_vpath, start_line, 0, 0);
 384     else
 385         mcview_load ((WView *) target, cmd, vfs_path_as_str (filename_vpath), start_line, 0, 0);
 386 
 387     if (changed_flags.hex && !mcview_altered_flags.hex)
 388         mcview_global_flags.hex = def_flags.hex;
 389     if (changed_flags.nroff && !mcview_altered_flags.nroff)
 390         mcview_global_flags.nroff = def_flags.nroff;
 391 
 392     dialog_switch_process_pending ();
 393 }
 394 
 395 /* --------------------------------------------------------------------------------------------- */
 396 
 397 static void
 398 exec_extension_cd (WPanel *panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
 399 {
 400     char *q;
 401     vfs_path_t *p_vpath;
 402 
 403     *pbuffer = '\0';
 404     pbuffer = buffer;
 405     /* Search last non-space character. Start search at the end in order
 406        not to short filenames containing spaces. */
 407     q = pbuffer + strlen (pbuffer) - 1;
 408     while (q >= pbuffer && whitespace (*q))
 409         q--;
 410     q[1] = 0;
 411 
 412     p_vpath = vfs_path_from_str_flags (pbuffer, VPF_NO_CANON);
 413     panel_cd (panel, p_vpath, cd_parse_command);
 414     vfs_path_free (p_vpath, TRUE);
 415 }
 416 
 417 /* --------------------------------------------------------------------------------------------- */
 418 
 419 static vfs_path_t *
 420 exec_extension (WPanel *panel, void *target, const vfs_path_t *filename_vpath, const char *lc_data,
     /* [previous][next][first][last][top][bottom][index][help]  */
 421                 int start_line)
 422 {
 423     GString *shell_string, *export_variables;
 424     vfs_path_t *script_vpath = NULL;
 425     int cmd_file_fd;
 426     FILE *cmd_file;
 427     char *cmd = NULL;
 428 
 429     pbuffer = NULL;
 430     localmtime = 0;
 431     quote_func = name_quote;
 432     run_view = FALSE;
 433     is_cd = FALSE;
 434     written_nonspace = FALSE;
 435 
 436     // Avoid making a local copy if we are doing a cd
 437     do_local_copy = !vfs_file_is_local (filename_vpath);
 438 
 439     shell_string = exec_make_shell_string (lc_data, filename_vpath);
 440     if (shell_string == NULL)
 441         goto ret;
 442 
 443     if (is_cd)
 444     {
 445         exec_extension_cd (panel);
 446         g_string_free (shell_string, TRUE);
 447         goto ret;
 448     }
 449 
 450     /*
 451      * All commands should be run in /bin/sh regardless of user shell.
 452      * To do that, create temporary shell script and run it.
 453      * Sometimes it's not needed (e.g. for %cd and %view commands),
 454      * but it's easier to create it anyway.
 455      */
 456     cmd_file_fd = mc_mkstemps (&script_vpath, "mcext", SCRIPT_SUFFIX);
 457 
 458     if (cmd_file_fd == -1)
 459     {
 460         message (D_ERROR, MSG_ERROR, _ ("Cannot create temporary command file\n%s"),
 461                  unix_error_string (errno));
 462         g_string_free (shell_string, TRUE);
 463         goto ret;
 464     }
 465 
 466     cmd_file = fdopen (cmd_file_fd, "w");
 467     fputs ("#! /bin/sh\n\n", cmd_file);
 468 
 469     export_variables = exec_get_export_variables (filename_vpath);
 470     if (export_variables != NULL)
 471     {
 472         fputs (export_variables->str, cmd_file);
 473         g_string_free (export_variables, TRUE);
 474     }
 475 
 476     fputs (shell_string->str, cmd_file);
 477     g_string_free (shell_string, TRUE);
 478 
 479     /*
 480      * Make the script remove itself when it finishes.
 481      * Don't do it for the viewer - it may need to rerun the script,
 482      * so we clean up after calling view().
 483      */
 484     if (!run_view)
 485         fprintf (cmd_file, "\n/bin/rm -f %s\n", vfs_path_as_str (script_vpath));
 486 
 487     fclose (cmd_file);
 488 
 489     if ((run_view && !written_nonspace) || is_cd)
 490     {
 491         exec_cleanup_script (script_vpath);
 492         script_vpath = NULL;
 493     }
 494     else
 495     {
 496         // Set executable flag on the command file ...
 497         mc_chmod (script_vpath, S_IRWXU);
 498         // ... but don't rely on it - run /bin/sh explicitly
 499         cmd = g_strconcat ("/bin/sh ", vfs_path_as_str (script_vpath), (char *) NULL);
 500     }
 501 
 502     if (run_view)
 503     {
 504         // If we've written whitespace only, then just load filename into view
 505         if (!written_nonspace)
 506             exec_extension_view (target, NULL, filename_vpath, start_line);
 507         else
 508             exec_extension_view (target, cmd, filename_vpath, start_line);
 509     }
 510     else
 511     {
 512         shell_execute (cmd, EXECUTE_INTERNAL);
 513         if (mc_global.tty.console_flag != '\0')
 514         {
 515             handle_console (CONSOLE_SAVE);
 516             if (output_lines != 0 && mc_global.keybar_visible)
 517             {
 518                 unsigned char end_line;
 519 
 520                 end_line = LINES - (mc_global.keybar_visible ? 1 : 0) - 1;
 521                 show_console_contents (output_start_y, end_line - output_lines, end_line);
 522             }
 523         }
 524     }
 525 
 526     g_free (cmd);
 527 
 528     exec_cleanup_file_name (filename_vpath, TRUE);
 529 ret:
 530     return script_vpath;
 531 }
 532 
 533 /* --------------------------------------------------------------------------------------------- */
 534 /**
 535  * Run cmd_file with args, put result into buf.
 536  * If error, put '\0' into buf[0]
 537  * Return 1 if the data is valid, 0 otherwise, -1 for fatal errors.
 538  *
 539  * NOTES: buf is null-terminated string.
 540  */
 541 
 542 #ifdef USE_FILE_CMD
 543 static int
 544 get_popen_information (const char *cmd_file, const char *args, char *buf, int buflen)
     /* [previous][next][first][last][top][bottom][index][help]  */
 545 {
 546     gboolean read_bytes = FALSE;
 547     char *command;
 548     FILE *f;
 549 
 550     command = g_strconcat (cmd_file, args, " 2>/dev/null", (char *) NULL);
 551     f = popen (command, "r");
 552     g_free (command);
 553 
 554     if (f != NULL)
 555     {
 556 #ifdef __QNXNTO__
 557         if (setvbuf (f, NULL, _IOFBF, 0) != 0)
 558         {
 559             (void) pclose (f);
 560             return -1;
 561         }
 562 #endif
 563         read_bytes = (fgets (buf, buflen, f) != NULL);
 564         if (!read_bytes)
 565             buf[0] = '\0';  // Paranoid termination
 566         pclose (f);
 567     }
 568     else
 569     {
 570         buf[0] = '\0';  // Paranoid termination
 571         return -1;
 572     }
 573 
 574     buf[buflen - 1] = '\0';
 575 
 576     return read_bytes ? 1 : 0;
 577 }
 578 
 579 /* --------------------------------------------------------------------------------------------- */
 580 /**
 581  * Run the "file" command on the local file.
 582  * Return 1 if the data is valid, 0 otherwise, -1 for fatal errors.
 583  */
 584 
 585 static int
 586 get_file_type_local (const vfs_path_t *filename_vpath, char *buf, int buflen)
     /* [previous][next][first][last][top][bottom][index][help]  */
 587 {
 588     char *filename_quoted;
 589     int ret = 0;
 590 
 591     filename_quoted = name_quote (vfs_path_get_last_path_str (filename_vpath), FALSE);
 592     if (filename_quoted != NULL)
 593     {
 594         ret = get_popen_information (FILE_CMD, filename_quoted, buf, buflen);
 595         g_free (filename_quoted);
 596     }
 597 
 598     return ret;
 599 }
 600 
 601 /* --------------------------------------------------------------------------------------------- */
 602 /**
 603  * Run the "enca" command on the local file.
 604  * Return 1 if the data is valid, 0 otherwise, -1 for fatal errors.
 605  */
 606 
 607 static int
 608 get_file_encoding_local (const vfs_path_t *filename_vpath, char *buf, int buflen)
     /* [previous][next][first][last][top][bottom][index][help]  */
 609 {
 610     char *filename_quoted;
 611     int ret = 0;
 612 
 613     filename_quoted = name_quote (vfs_path_get_last_path_str (filename_vpath), FALSE);
 614     if (filename_quoted != NULL)
 615     {
 616         char *lang;
 617 
 618         lang = name_quote (autodetect_codeset, FALSE);
 619         if (lang != NULL)
 620         {
 621             char *args;
 622 
 623             args = g_strdup_printf (" -L %s -i %s", lang, filename_quoted);
 624             g_free (lang);
 625 
 626             ret = get_popen_information ("enca", args, buf, buflen);
 627             g_free (args);
 628         }
 629 
 630         g_free (filename_quoted);
 631     }
 632 
 633     return ret;
 634 }
 635 
 636 /* --------------------------------------------------------------------------------------------- */
 637 /**
 638  * Invoke the "file" command on the file and match its output against PTR.
 639  * have_type is a flag that is set if we already have tried to determine
 640  * the type of that file.
 641  * Return TRUE for match, FALSE otherwise.
 642  */
 643 
 644 static gboolean
 645 regex_check_type (const vfs_path_t *filename_vpath, const char *ptr, gboolean case_insense,
     /* [previous][next][first][last][top][bottom][index][help]  */
 646                   gboolean *have_type, GError **mcerror)
 647 {
 648     gboolean found = FALSE;
 649 
 650     // Following variables are valid if *have_type is TRUE
 651     static char content_string[BUF_2K];
 652     static size_t content_shift = 0;
 653     static int got_data = 0;
 654 
 655     mc_return_val_if_error (mcerror, FALSE);
 656 
 657     if (!*have_type)
 658     {
 659         vfs_path_t *localfile_vpath;
 660 
 661         static char encoding_id[21];  // CSISO51INISCYRILLIC -- 20
 662         int got_encoding_data;
 663 
 664         // Don't repeate even unsuccessful checks
 665         *have_type = TRUE;
 666 
 667         localfile_vpath = mc_getlocalcopy (filename_vpath);
 668         if (localfile_vpath == NULL)
 669         {
 670             mc_propagate_error (mcerror, 0, _ ("Cannot fetch a local copy of %s"),
 671                                 vfs_path_as_str (filename_vpath));
 672             return FALSE;
 673         }
 674 
 675         got_encoding_data = is_autodetect_codeset_enabled
 676             ? get_file_encoding_local (localfile_vpath, encoding_id, sizeof (encoding_id))
 677             : 0;
 678 
 679         if (got_encoding_data > 0)
 680         {
 681             char *pp;
 682             int cp_id;
 683 
 684             pp = strchr (encoding_id, '\n');
 685             if (pp != NULL)
 686                 *pp = '\0';
 687 
 688             cp_id = get_codepage_index (encoding_id);
 689             if (cp_id == -1)
 690                 cp_id = default_source_codepage;
 691 
 692             do_set_codepage (cp_id);
 693         }
 694 
 695         got_data = get_file_type_local (localfile_vpath, content_string, sizeof (content_string));
 696 
 697         mc_ungetlocalcopy (filename_vpath, localfile_vpath, FALSE);
 698 
 699         if (got_data > 0)
 700         {
 701             char *pp;
 702 
 703             pp = strchr (content_string, '\n');
 704             if (pp != NULL)
 705                 *pp = '\0';
 706 
 707 #ifndef FILE_B
 708             {
 709                 const char *real_name;  // name used with "file"
 710                 size_t real_len;
 711 
 712                 real_name = vfs_path_get_last_path_str (localfile_vpath);
 713                 real_len = strlen (real_name);
 714 
 715                 if (strncmp (content_string, real_name, real_len) == 0)
 716                 {
 717                     // Skip "real_name: "
 718                     content_shift = real_len;
 719 
 720                     // Solaris' file prints tab(s) after ':'
 721                     if (content_string[content_shift] == ':')
 722                         for (content_shift++; whitespace (content_string[content_shift]);
 723                              content_shift++)
 724                             ;
 725                 }
 726             }
 727 #endif
 728         }
 729         else
 730         {
 731             // No data
 732             content_string[0] = '\0';
 733         }
 734         vfs_path_free (localfile_vpath, TRUE);
 735     }
 736 
 737     if (got_data == -1)
 738     {
 739         mc_propagate_error (mcerror, 0, "%s", _ ("Pipe failed"));
 740         return FALSE;
 741     }
 742 
 743     if (content_string[0] != '\0')
 744     {
 745         mc_search_t *search;
 746 
 747         search = mc_search_new (ptr, NULL);
 748         if (search != NULL)
 749         {
 750             search->search_type = MC_SEARCH_T_REGEX;
 751             search->is_case_sensitive = !case_insense;
 752             found = mc_search_run (search, content_string + content_shift, 0,
 753                                    sizeof (content_string) - 1, NULL);
 754             mc_search_free (search);
 755         }
 756         else
 757         {
 758             mc_propagate_error (mcerror, 0, "%s", _ ("Regular expression error"));
 759         }
 760     }
 761 
 762     return found;
 763 }
 764 #endif
 765 
 766 /* --------------------------------------------------------------------------------------------- */
 767 
 768 static void
 769 check_old_extension_file (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 770 {
 771     char *extension_old_file;
 772 
 773     extension_old_file = mc_config_get_full_path (MC_EXT_OLD_FILE);
 774     if (exist_file (extension_old_file))
 775         message (D_ERROR, _ ("Warning"),
 776                  _ ("You have an outdated %s file.\nMidnight Commander now uses %s file.\n"
 777                     "Please copy your modifications of the old file to the new one."),
 778                  extension_old_file, MC_EXT_FILE);
 779     g_free (extension_old_file);
 780 }
 781 
 782 /* --------------------------------------------------------------------------------------------- */
 783 
 784 static gboolean
 785 load_extension_file (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 786 {
 787     char *extension_file;
 788     gboolean mc_user_ext = TRUE;
 789     gboolean home_error = FALSE;
 790 
 791     extension_file = mc_config_get_full_path (MC_EXT_FILE);
 792     if (!exist_file (extension_file))
 793     {
 794         g_free (extension_file);
 795 
 796         check_old_extension_file ();
 797 
 798     check_stock_mc_ext:
 799         extension_file = mc_build_filename (mc_global.sysconfig_dir, MC_EXT_FILE, (char *) NULL);
 800         if (!exist_file (extension_file))
 801         {
 802             g_free (extension_file);
 803             extension_file =
 804                 mc_build_filename (mc_global.share_data_dir, MC_EXT_FILE, (char *) NULL);
 805             if (!exist_file (extension_file))
 806                 MC_PTR_FREE (extension_file);
 807         }
 808         mc_user_ext = FALSE;
 809     }
 810 
 811     if (extension_file != NULL)
 812     {
 813         ext_ini = mc_config_init (extension_file, TRUE);
 814         g_free (extension_file);
 815     }
 816     if (ext_ini == NULL)
 817         return FALSE;
 818 
 819     // Check version
 820     if (!mc_config_has_group (ext_ini, descr_group))
 821     {
 822         flush_extension_file ();
 823 
 824         if (!mc_user_ext)
 825         {
 826             message (D_ERROR, MSG_ERROR,
 827                      _ ("The format of the\n%s%s\nfile has changed with version 4.0.\n"
 828                         "It seems that the installation has failed.\nPlease fetch a fresh copy "
 829                         "from the Midnight Commander package."),
 830                      mc_global.sysconfig_dir, MC_EXT_FILE);
 831             return FALSE;
 832         }
 833 
 834         home_error = TRUE;
 835         goto check_stock_mc_ext;
 836     }
 837 
 838     if (home_error)
 839     {
 840         extension_file = mc_config_get_full_path (MC_EXT_FILE);
 841         message (
 842             D_ERROR, MSG_ERROR,
 843             _ ("The format of the\n%s\nfile has changed with version 4.0.\nYou may either want "
 844                "to copy it from\n%s%s\nor use that file as an example of how to write it."),
 845             extension_file, mc_global.sysconfig_dir, MC_EXT_FILE);
 846         g_free (extension_file);
 847     }
 848 
 849     return TRUE;
 850 }
 851 
 852 /* --------------------------------------------------------------------------------------------- */
 853 /*** public functions ****************************************************************************/
 854 /* --------------------------------------------------------------------------------------------- */
 855 
 856 void
 857 flush_extension_file (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 858 {
 859     g_strfreev (ext_ini_groups);
 860     ext_ini_groups = NULL;
 861 
 862     mc_config_deinit (ext_ini);
 863     ext_ini = NULL;
 864 }
 865 
 866 /* --------------------------------------------------------------------------------------------- */
 867 /**
 868  * The second argument is action, i.e. Open, View or Edit
 869  * Use target object to open file in.
 870  *
 871  * This function returns:
 872  *
 873  * -1 for a failure or user interrupt
 874  * 0 if no command was run
 875  * 1 if some command was run
 876  *
 877  * If action == "View" then a parameter is checked in the form of "View:%d",
 878  * if the value for %d exists, then the viewer is started up at that line number.
 879  */
 880 
 881 int
 882 regex_command_for (void *target, const vfs_path_t *filename_vpath, const char *action,
     /* [previous][next][first][last][top][bottom][index][help]  */
 883                    vfs_path_t **script_vpath)
 884 {
 885     const char *filename;
 886     size_t filename_len;
 887     gboolean found = FALSE;
 888     gboolean error_flag = FALSE;
 889     int ret = 0;
 890     struct stat mystat;
 891     int view_at_line_number = 0;
 892 #ifdef USE_FILE_CMD
 893     gboolean have_type = FALSE;  // Flag used by regex_check_type()
 894 #endif
 895     char **group_iter;
 896     char *include_group = NULL;
 897     const char *current_group;
 898 
 899     if (filename_vpath == NULL)
 900         return 0;
 901 
 902     if (script_vpath != NULL)
 903         *script_vpath = NULL;
 904 
 905     // Check for the special View:%d parameter
 906     if (strncmp (action, "View:", 5) == 0)
 907     {
 908         view_at_line_number = atoi (action + 5);
 909         action = "View";
 910     }
 911 
 912     if (ext_ini == NULL && !load_extension_file ())
 913         return 0;
 914 
 915     mc_stat (filename_vpath, &mystat);
 916 
 917     filename = vfs_path_get_last_path_str (filename_vpath);
 918     filename = x_basename (filename);
 919     filename_len = strlen (filename);
 920 
 921     if (ext_ini_groups == NULL)
 922         ext_ini_groups = mc_config_get_groups (ext_ini, NULL);
 923 
 924     // find matched type, regex or shell pattern
 925     for (group_iter = ext_ini_groups; *group_iter != NULL && !found; group_iter++)
 926     {
 927         enum
 928         {
 929             TYPE_UNUSED,
 930             TYPE_NOT_FOUND,
 931             TYPE_FOUND
 932         } type_state = TYPE_UNUSED;
 933 
 934         const gchar *g = *group_iter;
 935         gchar *pattern;
 936         gboolean ignore_case;
 937 
 938         if (strcmp (g, descr_group) == 0 || strncmp (g, "Include/", 8) == 0
 939             || strcmp (g, default_group) == 0)
 940             continue;
 941 
 942         /* The "Directory" parameter is a special case: if it's present then
 943            "Type", "Regex", and "Shell" parameters are ignored */
 944         pattern = mc_config_get_string_raw (ext_ini, g, "Directory", NULL);
 945         if (pattern != NULL)
 946         {
 947             found = S_ISDIR (mystat.st_mode)
 948                 && mc_search (pattern, NULL, vfs_path_as_str (filename_vpath), MC_SEARCH_T_REGEX);
 949             g_free (pattern);
 950 
 951             continue;  // stop if found
 952         }
 953 
 954 #ifdef USE_FILE_CMD
 955         if (use_file_to_check_type)
 956         {
 957             pattern = mc_config_get_string_raw (ext_ini, g, "Type", NULL);
 958             if (pattern != NULL)
 959             {
 960                 GError *mcerror = NULL;
 961 
 962                 ignore_case = mc_config_get_bool (ext_ini, g, "TypeIgnoreCase", FALSE);
 963                 type_state =
 964                     regex_check_type (filename_vpath, pattern, ignore_case, &have_type, &mcerror)
 965                     ? TYPE_FOUND
 966                     : TYPE_NOT_FOUND;
 967                 g_free (pattern);
 968 
 969                 if (mc_error_message (&mcerror, NULL))
 970                     error_flag = TRUE;  // leave it if file cannot be opened
 971 
 972                 if (type_state == TYPE_NOT_FOUND)
 973                     continue;
 974             }
 975         }
 976 #endif
 977 
 978         pattern = mc_config_get_string_raw (ext_ini, g, "Regex", NULL);
 979         if (pattern != NULL)
 980         {
 981             mc_search_t *search;
 982 
 983             ignore_case = mc_config_get_bool (ext_ini, g, "RegexIgnoreCase", FALSE);
 984             search = mc_search_new (pattern, NULL);
 985             g_free (pattern);
 986 
 987             if (search != NULL)
 988             {
 989                 search->search_type = MC_SEARCH_T_REGEX;
 990                 search->is_case_sensitive = !ignore_case;
 991                 found = mc_search_run (search, filename, 0, filename_len, NULL);
 992                 mc_search_free (search);
 993             }
 994 
 995             found = found && (type_state == TYPE_UNUSED || type_state == TYPE_FOUND);
 996         }
 997         else
 998         {
 999             pattern = mc_config_get_string_raw (ext_ini, g, "Shell", NULL);
1000             if (pattern != NULL)
1001             {
1002                 int (*cmp_func) (const char *s1, const char *s2, size_t n);
1003                 size_t pattern_len;
1004 
1005                 ignore_case = mc_config_get_bool (ext_ini, g, "ShellIgnoreCase", FALSE);
1006                 cmp_func = ignore_case ? strncasecmp : strncmp;
1007                 pattern_len = strlen (pattern);
1008 
1009                 if (*pattern == '.' && filename_len >= pattern_len)
1010                     found =
1011                         cmp_func (pattern, filename + filename_len - pattern_len, pattern_len) == 0;
1012                 else
1013                     found = pattern_len == filename_len
1014                         && cmp_func (pattern, filename, filename_len) == 0;
1015 
1016                 g_free (pattern);
1017 
1018                 found = found && (type_state == TYPE_UNUSED || type_state == TYPE_FOUND);
1019             }
1020             else
1021                 found = type_state == TYPE_FOUND;
1022         }
1023     }
1024 
1025     // group is found, process actions
1026     if (found)
1027     {
1028         char *include_value;
1029 
1030         group_iter--;
1031 
1032         // "Include" parameter has the highest priority over any actions
1033         include_value = mc_config_get_string_raw (ext_ini, *group_iter, "Include", NULL);
1034         if (include_value != NULL)
1035         {
1036             // find "Include/include_value" group
1037             include_group = g_strconcat ("Include/", include_value, (char *) NULL);
1038             g_free (include_value);
1039             found = mc_config_has_group (ext_ini, include_group);
1040         }
1041     }
1042 
1043     if (found)
1044         current_group = include_group != NULL ? include_group : *group_iter;
1045     else
1046     {
1047         current_group = default_group;
1048         found = mc_config_has_group (ext_ini, current_group);
1049     }
1050 
1051     if (found && !error_flag)
1052     {
1053         gchar *action_value;
1054 
1055         action_value = mc_config_get_string_raw (ext_ini, current_group, action, NULL);
1056         if (action_value == NULL)
1057         {
1058             // Not found, try the action from default section
1059             action_value = mc_config_get_string_raw (ext_ini, default_group, action, NULL);
1060             found = (action_value != NULL && *action_value != '\0');
1061         }
1062         else
1063         {
1064             // If action's value is empty, ignore action from default section
1065             found = (*action_value != '\0');
1066         }
1067 
1068         if (found)
1069         {
1070             vfs_path_t *sv;
1071 
1072             sv = exec_extension (current_panel, target, filename_vpath, action_value,
1073                                  view_at_line_number);
1074             if (script_vpath != NULL)
1075                 *script_vpath = sv;
1076             else
1077                 exec_cleanup_script (sv);
1078 
1079             ret = 1;
1080         }
1081 
1082         g_free (action_value);
1083     }
1084 
1085     g_free (include_group);
1086 
1087     return (error_flag ? -1 : ret);
1088 }
1089 
1090 /* --------------------------------------------------------------------------------------------- */

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