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. flush_extension_file
  15. regex_command_for

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

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