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

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