Don't include lib/util.h via lib/global.h.
[midnight-commander/osp/fridrivaxxx.git] / src / ext.c
blobaf84cec63c082fcf4d85b0a5226617bf58471415
1 /* Extension dependent execution.
2 Copyright (C) 1994, 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
3 2005, 2007 Free Software Foundation, Inc.
5 Written by: 1995 Jakub Jelinek
6 1994 Miguel de Icaza
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
22 /** \file ext.c
23 * \brief Source: extension dependent execution
26 #include <config.h>
28 #include <ctype.h>
29 #include <errno.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <unistd.h>
35 #include "lib/global.h"
36 #include "lib/tty/tty.h"
37 #include "lib/search.h"
38 #include "lib/fileloc.h"
39 #include "lib/util.h"
40 #include "lib/vfs/mc-vfs/vfs.h"
41 #include "lib/widget/wtools.h"
42 #include "lib/widget/dialog-switch.h"
44 #include "consaver/cons.saver.h"
45 #include "viewer/mcviewer.h"
47 #include "user.h"
48 #include "main.h"
49 #include "execute.h"
50 #include "history.h"
51 #include "layout.h"
52 #ifdef HAVE_CHARSET
53 #include "charsets.h" /* get_codepage_index */
54 #include "selcodepage.h" /* do_set_codepage */
55 #endif
56 #include "ext.h"
58 /*** global variables ****************************************************************************/
60 /* If set, we execute the file command to check the file type */
61 int use_file_to_check_type = 1;
63 /*** file scope macro definitions ****************************************************************/
65 #ifdef FILE_L
66 #define FILE_CMD "file -L "
67 #else
68 #define FILE_CMD "file "
69 #endif
71 /*** file scope type declarations ****************************************************************/
73 typedef char *(*quote_func_t) (const char *name, int quote_percent);
75 /*** file scope variables ************************************************************************/
77 /* This variable points to a copy of the mc.ext file in memory
78 * With this we avoid loading/parsing the file each time we
79 * need it
81 static char *data = NULL;
83 /*** file scope functions ************************************************************************/
84 /* --------------------------------------------------------------------------------------------- */
86 static void
87 exec_extension (const char *filename, const char *lc_data, int *move_dir, int start_line)
89 char *fn;
90 char *file_name;
91 int cmd_file_fd;
92 FILE *cmd_file;
93 char *cmd = NULL;
94 int expand_prefix_found = 0;
95 int parameter_found = 0;
96 char lc_prompt[80];
97 int run_view = 0;
98 int def_hex_mode = mcview_default_hex_mode, changed_hex_mode = 0;
99 int def_nroff_flag = mcview_default_nroff_flag, changed_nroff_flag = 0;
100 int written_nonspace = 0;
101 int is_cd = 0;
102 char buffer[1024];
103 char *p = 0;
104 char *localcopy = NULL;
105 int do_local_copy;
106 time_t localmtime = 0;
107 struct stat mystat;
108 quote_func_t quote_func = name_quote;
110 g_return_if_fail (filename != NULL);
111 g_return_if_fail (lc_data != NULL);
113 /* Avoid making a local copy if we are doing a cd */
114 if (!vfs_file_is_local (filename))
115 do_local_copy = 1;
116 else
117 do_local_copy = 0;
120 * All commands should be run in /bin/sh regardless of user shell.
121 * To do that, create temporary shell script and run it.
122 * Sometimes it's not needed (e.g. for %cd and %view commands),
123 * but it's easier to create it anyway.
125 cmd_file_fd = mc_mkstemps (&file_name, "mcext", SCRIPT_SUFFIX);
127 if (cmd_file_fd == -1)
129 message (D_ERROR, MSG_ERROR,
130 _("Cannot create temporary command file\n%s"), unix_error_string (errno));
131 return;
134 cmd_file = fdopen (cmd_file_fd, "w");
135 fputs ("#! /bin/sh\n", cmd_file);
137 lc_prompt[0] = '\0';
138 for (; *lc_data != '\0' && *lc_data != '\n'; lc_data++)
140 if (parameter_found)
142 if (*lc_data == '}')
144 char *parameter;
146 parameter_found = 0;
147 parameter = input_dialog (_("Parameter"), lc_prompt, MC_HISTORY_EXT_PARAMETER, "");
148 if (parameter == NULL)
150 /* User canceled */
151 fclose (cmd_file);
152 unlink (file_name);
153 if (localcopy)
155 mc_ungetlocalcopy (filename, localcopy, 0);
156 g_free (localcopy);
158 g_free (file_name);
159 return;
161 fputs (parameter, cmd_file);
162 written_nonspace = 1;
163 g_free (parameter);
165 else
167 size_t len = strlen (lc_prompt);
169 if (len < sizeof (lc_prompt) - 1)
171 lc_prompt[len] = *lc_data;
172 lc_prompt[len + 1] = '\0';
176 else if (expand_prefix_found)
178 expand_prefix_found = 0;
179 if (*lc_data == '{')
180 parameter_found = 1;
181 else
183 int i;
184 char *v;
186 i = check_format_view (lc_data);
187 if (i != 0)
189 lc_data += i - 1;
190 run_view = 1;
192 else
194 i = check_format_cd (lc_data);
195 if (i > 0)
197 is_cd = 1;
198 quote_func = fake_name_quote;
199 do_local_copy = 0;
200 p = buffer;
201 lc_data += i - 1;
203 else
205 i = check_format_var (lc_data, &v);
206 if (i > 0 && v != NULL)
208 fputs (v, cmd_file);
209 g_free (v);
210 lc_data += i;
212 else
214 char *text;
216 if (*lc_data != 'f')
217 text = expand_format (NULL, *lc_data, !is_cd);
218 else
220 if (do_local_copy)
222 localcopy = mc_getlocalcopy (filename);
223 if (localcopy == NULL)
225 fclose (cmd_file);
226 unlink (file_name);
227 g_free (file_name);
228 return;
230 mc_stat (localcopy, &mystat);
231 localmtime = mystat.st_mtime;
232 text = quote_func (localcopy, 0);
234 else
236 fn = vfs_canon_and_translate (filename);
237 text = quote_func (fn, 0);
238 g_free (fn);
242 if (!is_cd)
243 fputs (text, cmd_file);
244 else
246 strcpy (p, text);
247 p = strchr (p, 0);
250 g_free (text);
251 written_nonspace = 1;
257 else if (*lc_data == '%')
258 expand_prefix_found = 1;
259 else
261 if (*lc_data != ' ' && *lc_data != '\t')
262 written_nonspace = 1;
263 if (is_cd)
264 *(p++) = *lc_data;
265 else
266 fputc (*lc_data, cmd_file);
268 } /* for */
271 * Make the script remove itself when it finishes.
272 * Don't do it for the viewer - it may need to rerun the script,
273 * so we clean up after calling view().
275 if (!run_view)
276 fprintf (cmd_file, "\n/bin/rm -f %s\n", file_name);
278 fclose (cmd_file);
280 if ((run_view && !written_nonspace) || is_cd)
282 unlink (file_name);
283 g_free (file_name);
284 file_name = NULL;
286 else
288 /* Set executable flag on the command file ... */
289 chmod (file_name, S_IRWXU);
290 /* ... but don't rely on it - run /bin/sh explicitly */
291 cmd = g_strconcat ("/bin/sh ", file_name, (char *) NULL);
294 if (run_view)
296 mcview_ret_t ret;
298 mcview_altered_hex_mode = 0;
299 mcview_altered_nroff_flag = 0;
300 if (def_hex_mode != mcview_default_hex_mode)
301 changed_hex_mode = 1;
302 if (def_nroff_flag != mcview_default_nroff_flag)
303 changed_nroff_flag = 1;
305 /* If we've written whitespace only, then just load filename
306 * into view
308 if (written_nonspace)
310 ret = mcview_viewer (cmd, filename, start_line);
311 unlink (file_name);
313 else
314 ret = mcview_viewer (NULL, filename, start_line);
316 if (move_dir != NULL)
317 switch (ret)
319 case MCVIEW_WANT_NEXT:
320 *move_dir = 1;
321 break;
322 case MCVIEW_WANT_PREV:
323 *move_dir = -1;
324 break;
325 default:
326 *move_dir = 0;
329 if (changed_hex_mode && !mcview_altered_hex_mode)
330 mcview_default_hex_mode = def_hex_mode;
331 if (changed_nroff_flag && !mcview_altered_nroff_flag)
332 mcview_default_nroff_flag = def_nroff_flag;
334 dialog_switch_process_pending ();
336 else if (is_cd)
338 char *q;
339 *p = 0;
340 p = buffer;
341 /* while (*p == ' ' && *p == '\t')
342 * p++;
344 /* Search last non-space character. Start search at the end in order
345 not to short filenames containing spaces. */
346 q = p + strlen (p) - 1;
347 while (q >= p && (*q == ' ' || *q == '\t'))
348 q--;
349 q[1] = 0;
350 do_cd (p, cd_parse_command);
352 else
354 shell_execute (cmd, EXECUTE_INTERNAL);
355 if (console_flag)
357 handle_console (CONSOLE_SAVE);
358 if (output_lines && keybar_visible)
359 show_console_contents (output_start_y,
360 LINES - keybar_visible -
361 output_lines - 1, LINES - keybar_visible - 1);
365 g_free (file_name);
366 g_free (cmd);
368 if (localcopy)
370 mc_stat (localcopy, &mystat);
371 mc_ungetlocalcopy (filename, localcopy, localmtime != mystat.st_mtime);
372 g_free (localcopy);
376 /* --------------------------------------------------------------------------------------------- */
378 * Run cmd_file with args, put result into buf.
379 * If error, put '\0' into buf[0]
380 * Return 1 if the data is valid, 0 otherwise, -1 for fatal errors.
382 * NOTES: buf is null-terminated string.
385 static int
386 get_popen_information (const char *cmd_file, const char *args, char *buf, int buflen)
388 gboolean read_bytes = FALSE;
389 char *command;
390 FILE *f;
392 command = g_strconcat (cmd_file, args, " 2>/dev/null", (char *) NULL);
393 f = popen (command, "r");
394 g_free (command);
396 if (f != NULL)
398 #ifdef __QNXNTO__
399 if (setvbuf (f, NULL, _IOFBF, 0) != 0)
401 (void) pclose (f);
402 return -1;
404 #endif
405 read_bytes = (fgets (buf, buflen, f) != NULL);
406 if (!read_bytes)
407 buf[0] = '\0'; /* Paranoid termination */
408 pclose (f);
410 else
412 buf[0] = '\0'; /* Paranoid termination */
413 return -1;
416 buf[buflen - 1] = '\0';
418 return read_bytes ? 1 : 0;
421 /* --------------------------------------------------------------------------------------------- */
423 * Run the "file" command on the local file.
424 * Return 1 if the data is valid, 0 otherwise, -1 for fatal errors.
427 static int
428 get_file_type_local (const char *filename, char *buf, int buflen)
430 char *tmp;
431 int ret;
433 tmp = name_quote (filename, 0);
434 ret = get_popen_information (FILE_CMD, tmp, buf, buflen);
435 g_free (tmp);
437 return ret;
440 /* --------------------------------------------------------------------------------------------- */
442 * Run the "enca" command on the local file.
443 * Return 1 if the data is valid, 0 otherwise, -1 for fatal errors.
446 #ifdef HAVE_CHARSET
447 static int
448 get_file_encoding_local (const char *filename, char *buf, int buflen)
450 char *tmp, *lang, *args;
451 int ret;
453 tmp = name_quote (filename, 0);
454 lang = name_quote (autodetect_codeset, 0);
455 args = g_strconcat (" -L", lang, " -i ", tmp, (char *) NULL);
457 ret = get_popen_information ("enca", args, buf, buflen);
459 g_free (args);
460 g_free (lang);
461 g_free (tmp);
463 return ret;
465 #endif /* HAVE_CHARSET */
467 /* --------------------------------------------------------------------------------------------- */
469 * Invoke the "file" command on the file and match its output against PTR.
470 * have_type is a flag that is set if we already have tried to determine
471 * the type of that file.
472 * Return 1 for match, 0 for no match, -1 errors.
475 static int
476 regex_check_type (const char *filename, const char *ptr, int *have_type)
478 int found = 0;
480 /* Following variables are valid if *have_type is 1 */
481 static char content_string[2048];
482 static char encoding_id[21]; /* CSISO51INISCYRILLIC -- 20 */
483 static size_t content_shift = 0;
484 static int got_data = 0;
486 if (!use_file_to_check_type)
487 return 0;
489 if (*have_type == 0)
491 char *realname; /* name used with "file" */
492 char *localfile;
494 #ifdef HAVE_CHARSET
495 int got_encoding_data;
496 #endif /* HAVE_CHARSET */
498 /* Don't repeate even unsuccessful checks */
499 *have_type = 1;
501 localfile = mc_getlocalcopy (filename);
502 if (localfile == NULL)
503 return -1;
505 realname = localfile;
507 #ifdef HAVE_CHARSET
508 got_encoding_data = is_autodetect_codeset_enabled
509 ? get_file_encoding_local (localfile, encoding_id, sizeof (encoding_id)) : 0;
511 if (got_encoding_data > 0)
513 char *pp;
514 int cp_id;
516 pp = strchr (encoding_id, '\n');
517 if (pp != NULL)
518 *pp = '\0';
520 cp_id = get_codepage_index (encoding_id);
521 if (cp_id == -1)
522 cp_id = default_source_codepage;
524 do_set_codepage (cp_id);
526 #endif /* HAVE_CHARSET */
528 mc_ungetlocalcopy (filename, localfile, 0);
530 got_data = get_file_type_local (localfile, content_string, sizeof (content_string));
532 if (got_data > 0)
534 char *pp;
535 size_t real_len;
537 pp = strchr (content_string, '\n');
538 if (pp != NULL)
539 *pp = '\0';
541 real_len = strlen (realname);
543 if (strncmp (content_string, realname, real_len) == 0)
545 /* Skip "realname: " */
546 content_shift = real_len;
547 if (content_string[content_shift] == ':')
549 /* Solaris' file prints tab(s) after ':' */
550 for (content_shift++;
551 content_string[content_shift] == ' '
552 || content_string[content_shift] == '\t'; content_shift++)
557 else
559 /* No data */
560 content_string[0] = '\0';
562 g_free (realname);
565 if (got_data == -1)
566 return -1;
568 if (content_string[0] != '\0'
569 && mc_search (ptr, content_string + content_shift, MC_SEARCH_T_REGEX))
571 found = 1;
574 return found;
577 /* --------------------------------------------------------------------------------------------- */
578 /*** public functions ****************************************************************************/
579 /* --------------------------------------------------------------------------------------------- */
581 void
582 flush_extension_file (void)
584 g_free (data);
585 data = NULL;
588 /* --------------------------------------------------------------------------------------------- */
590 * The second argument is action, i.e. Open, View or Edit
592 * This function returns:
594 * -1 for a failure or user interrupt
595 * 0 if no command was run
596 * 1 if some command was run
598 * If action == "View" then a parameter is checked in the form of "View:%d",
599 * if the value for %d exists, then the viewer is started up at that line number.
603 regex_command (const char *filename, const char *action, int *move_dir)
605 char *p, *q, *r, c;
606 int file_len = strlen (filename);
607 int found = 0;
608 int error_flag = 0;
609 int ret = 0;
610 struct stat mystat;
611 int view_at_line_number;
612 char *include_target;
613 int include_target_len;
614 int have_type = 0; /* Flag used by regex_check_type() */
616 /* Check for the special View:%d parameter */
617 if (strncmp (action, "View:", 5) == 0)
619 view_at_line_number = atoi (action + 5);
620 action = "View";
622 else
624 view_at_line_number = 0;
627 if (data == NULL)
629 char *extension_file;
630 int mc_user_ext = 1;
631 int home_error = 0;
633 extension_file = g_build_filename (home_dir, MC_USERCONF_DIR, MC_FILEBIND_FILE, NULL);
634 if (!exist_file (extension_file))
636 g_free (extension_file);
637 check_stock_mc_ext:
638 extension_file = concat_dir_and_file (mc_home, MC_LIB_EXT);
639 if (!exist_file (extension_file))
641 g_free (extension_file);
642 extension_file = concat_dir_and_file (mc_home_alt, MC_LIB_EXT);
644 mc_user_ext = 0;
647 g_file_get_contents (extension_file, &data, NULL, NULL);
648 g_free (extension_file);
649 if (data == NULL)
650 return 0;
652 if (!strstr (data, "default/"))
654 if (!strstr (data, "regex/") && !strstr (data, "shell/") && !strstr (data, "type/"))
656 g_free (data);
657 data = NULL;
658 if (mc_user_ext)
660 home_error = 1;
661 goto check_stock_mc_ext;
663 else
665 char *title = g_strdup_printf (_(" %s%s file error"),
666 mc_home, MC_LIB_EXT);
667 message (D_ERROR, title, _("The format of the %smc.ext "
668 "file has changed with version 3.0. It seems that "
669 "the installation failed. Please fetch a fresh "
670 "copy from the Midnight Commander package."),
671 mc_home);
672 g_free (title);
673 return 0;
677 if (home_error)
679 char *title = g_strdup_printf (_("~/%s file error"),
680 MC_USERCONF_DIR PATH_SEP_STR MC_FILEBIND_FILE);
681 message (D_ERROR, title,
682 _("The format of the ~/%s file has "
683 "changed with version 3.0. You may either want to copy "
684 "it from %smc.ext or use that file as an example of how to write it."),
685 MC_USERCONF_DIR PATH_SEP_STR MC_FILEBIND_FILE, mc_home);
686 g_free (title);
689 mc_stat (filename, &mystat);
691 include_target = NULL;
692 include_target_len = 0;
693 for (p = data; *p; p++)
695 for (q = p; *q == ' ' || *q == '\t'; q++);
696 if (*q == '\n' || !*q)
697 p = q; /* empty line */
698 if (*p == '#') /* comment */
699 while (*p && *p != '\n')
700 p++;
701 if (*p == '\n')
702 continue;
703 if (!*p)
704 break;
705 if (p == q)
706 { /* i.e. starts in the first column, should be
707 * keyword/descNL
709 found = 0;
710 q = strchr (p, '\n');
711 if (q == NULL)
712 q = strchr (p, 0);
713 c = *q;
714 *q = 0;
715 if (include_target)
717 if ((strncmp (p, "include/", 8) == 0)
718 && (strncmp (p + 8, include_target, include_target_len) == 0))
719 found = 1;
721 else if (!strncmp (p, "regex/", 6))
723 p += 6;
724 /* Do not transform shell patterns, you can use shell/ for
725 * that
727 if (mc_search (p, filename, MC_SEARCH_T_REGEX))
728 found = 1;
730 else if (!strncmp (p, "directory/", 10))
732 if (S_ISDIR (mystat.st_mode) && mc_search (p + 10, filename, MC_SEARCH_T_REGEX))
733 found = 1;
735 else if (!strncmp (p, "shell/", 6))
737 p += 6;
738 if (*p == '.' && file_len >= (q - p))
740 if (!strncmp (p, filename + file_len - (q - p), q - p))
741 found = 1;
743 else
745 if (q - p == file_len && !strncmp (p, filename, q - p))
746 found = 1;
749 else if (!strncmp (p, "type/", 5))
751 int res;
752 p += 5;
753 res = regex_check_type (filename, p, &have_type);
754 if (res == 1)
755 found = 1;
756 if (res == -1)
757 error_flag = 1; /* leave it if file cannot be opened */
759 else if (!strncmp (p, "default/", 8))
761 found = 1;
763 *q = c;
764 p = q;
765 if (!*p)
766 break;
768 else
769 { /* List of actions */
770 p = q;
771 q = strchr (p, '\n');
772 if (q == NULL)
773 q = strchr (p, 0);
774 if (found && !error_flag)
776 r = strchr (p, '=');
777 if (r != NULL)
779 c = *r;
780 *r = 0;
781 if (strcmp (p, "Include") == 0)
783 char *t;
785 include_target = p + 8;
786 t = strchr (include_target, '\n');
787 if (t)
788 *t = 0;
789 include_target_len = strlen (include_target);
790 if (t)
791 *t = '\n';
793 *r = c;
794 p = q;
795 found = 0;
797 if (!*p)
798 break;
799 continue;
801 if (!strcmp (action, p))
803 *r = c;
804 for (p = r + 1; *p == ' ' || *p == '\t'; p++);
806 /* Empty commands just stop searching
807 * through, they don't do anything
809 * We need to copy the filename because exec_extension
810 * may end up invoking update_panels thus making the
811 * filename parameter invalid (ie, most of the time,
812 * we get filename as a pointer from current_panel->dir).
814 if (p < q)
816 char *filename_copy = g_strdup (filename);
818 exec_extension (filename_copy, r + 1, move_dir, view_at_line_number);
819 g_free (filename_copy);
821 ret = 1;
823 break;
825 else
826 *r = c;
829 p = q;
830 if (!*p)
831 break;
834 if (error_flag)
835 return -1;
836 return ret;
839 /* --------------------------------------------------------------------------------------------- */