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

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