Moved charsets.[ch] from src to lib directory
[midnight-commander.git] / src / ext.c
blob5daea843cdca2068b3458f090fc8f6c419f9c062
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.h"
42 #include "lib/charsets.h" /* get_codepage_index */
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 "selcodepage.h" /* do_set_codepage */
54 #endif
55 #include "ext.h"
57 /*** global variables ****************************************************************************/
59 /* If set, we execute the file command to check the file type */
60 int use_file_to_check_type = 1;
62 /*** file scope macro definitions ****************************************************************/
64 #ifdef FILE_L
65 #define FILE_CMD "file -L "
66 #else
67 #define FILE_CMD "file "
68 #endif
70 /*** file scope type declarations ****************************************************************/
72 typedef char *(*quote_func_t) (const char *name, int quote_percent);
74 /*** file scope variables ************************************************************************/
76 /* This variable points to a copy of the mc.ext file in memory
77 * With this we avoid loading/parsing the file each time we
78 * need it
80 static char *data = NULL;
82 /*** file scope functions ************************************************************************/
83 /* --------------------------------------------------------------------------------------------- */
85 static void
86 exec_extension (const char *filename, const char *lc_data, int *move_dir, int start_line)
88 char *fn;
89 char *file_name;
90 int cmd_file_fd;
91 FILE *cmd_file;
92 char *cmd = NULL;
93 int expand_prefix_found = 0;
94 int parameter_found = 0;
95 char lc_prompt[80];
96 int run_view = 0;
97 int def_hex_mode = mcview_default_hex_mode, changed_hex_mode = 0;
98 int def_nroff_flag = mcview_default_nroff_flag, changed_nroff_flag = 0;
99 int written_nonspace = 0;
100 int is_cd = 0;
101 char buffer[1024];
102 char *p = 0;
103 char *localcopy = NULL;
104 int do_local_copy;
105 time_t localmtime = 0;
106 struct stat mystat;
107 quote_func_t quote_func = name_quote;
109 g_return_if_fail (filename != NULL);
110 g_return_if_fail (lc_data != NULL);
112 /* Avoid making a local copy if we are doing a cd */
113 if (!vfs_file_is_local (filename))
114 do_local_copy = 1;
115 else
116 do_local_copy = 0;
119 * All commands should be run in /bin/sh regardless of user shell.
120 * To do that, create temporary shell script and run it.
121 * Sometimes it's not needed (e.g. for %cd and %view commands),
122 * but it's easier to create it anyway.
124 cmd_file_fd = mc_mkstemps (&file_name, "mcext", SCRIPT_SUFFIX);
126 if (cmd_file_fd == -1)
128 message (D_ERROR, MSG_ERROR,
129 _("Cannot create temporary command file\n%s"), unix_error_string (errno));
130 return;
133 cmd_file = fdopen (cmd_file_fd, "w");
134 fputs ("#! /bin/sh\n", cmd_file);
136 lc_prompt[0] = '\0';
137 for (; *lc_data != '\0' && *lc_data != '\n'; lc_data++)
139 if (parameter_found)
141 if (*lc_data == '}')
143 char *parameter;
145 parameter_found = 0;
146 parameter = input_dialog (_("Parameter"), lc_prompt, MC_HISTORY_EXT_PARAMETER, "");
147 if (parameter == NULL)
149 /* User canceled */
150 fclose (cmd_file);
151 unlink (file_name);
152 if (localcopy)
154 mc_ungetlocalcopy (filename, localcopy, 0);
155 g_free (localcopy);
157 g_free (file_name);
158 return;
160 fputs (parameter, cmd_file);
161 written_nonspace = 1;
162 g_free (parameter);
164 else
166 size_t len = strlen (lc_prompt);
168 if (len < sizeof (lc_prompt) - 1)
170 lc_prompt[len] = *lc_data;
171 lc_prompt[len + 1] = '\0';
175 else if (expand_prefix_found)
177 expand_prefix_found = 0;
178 if (*lc_data == '{')
179 parameter_found = 1;
180 else
182 int i;
183 char *v;
185 i = check_format_view (lc_data);
186 if (i != 0)
188 lc_data += i - 1;
189 run_view = 1;
191 else
193 i = check_format_cd (lc_data);
194 if (i > 0)
196 is_cd = 1;
197 quote_func = fake_name_quote;
198 do_local_copy = 0;
199 p = buffer;
200 lc_data += i - 1;
202 else
204 i = check_format_var (lc_data, &v);
205 if (i > 0 && v != NULL)
207 fputs (v, cmd_file);
208 g_free (v);
209 lc_data += i;
211 else
213 char *text;
215 if (*lc_data != 'f')
216 text = expand_format (NULL, *lc_data, !is_cd);
217 else
219 if (do_local_copy)
221 localcopy = mc_getlocalcopy (filename);
222 if (localcopy == NULL)
224 fclose (cmd_file);
225 unlink (file_name);
226 g_free (file_name);
227 return;
229 mc_stat (localcopy, &mystat);
230 localmtime = mystat.st_mtime;
231 text = quote_func (localcopy, 0);
233 else
235 fn = vfs_canon_and_translate (filename);
236 text = quote_func (fn, 0);
237 g_free (fn);
241 if (!is_cd)
242 fputs (text, cmd_file);
243 else
245 strcpy (p, text);
246 p = strchr (p, 0);
249 g_free (text);
250 written_nonspace = 1;
256 else if (*lc_data == '%')
257 expand_prefix_found = 1;
258 else
260 if (*lc_data != ' ' && *lc_data != '\t')
261 written_nonspace = 1;
262 if (is_cd)
263 *(p++) = *lc_data;
264 else
265 fputc (*lc_data, cmd_file);
267 } /* for */
270 * Make the script remove itself when it finishes.
271 * Don't do it for the viewer - it may need to rerun the script,
272 * so we clean up after calling view().
274 if (!run_view)
275 fprintf (cmd_file, "\n/bin/rm -f %s\n", file_name);
277 fclose (cmd_file);
279 if ((run_view && !written_nonspace) || is_cd)
281 unlink (file_name);
282 g_free (file_name);
283 file_name = NULL;
285 else
287 /* Set executable flag on the command file ... */
288 chmod (file_name, S_IRWXU);
289 /* ... but don't rely on it - run /bin/sh explicitly */
290 cmd = g_strconcat ("/bin/sh ", file_name, (char *) NULL);
293 if (run_view)
295 mcview_ret_t ret;
297 mcview_altered_hex_mode = 0;
298 mcview_altered_nroff_flag = 0;
299 if (def_hex_mode != mcview_default_hex_mode)
300 changed_hex_mode = 1;
301 if (def_nroff_flag != mcview_default_nroff_flag)
302 changed_nroff_flag = 1;
304 /* If we've written whitespace only, then just load filename
305 * into view
307 if (written_nonspace)
309 ret = mcview_viewer (cmd, filename, start_line);
310 unlink (file_name);
312 else
313 ret = mcview_viewer (NULL, filename, start_line);
315 if (move_dir != NULL)
316 switch (ret)
318 case MCVIEW_WANT_NEXT:
319 *move_dir = 1;
320 break;
321 case MCVIEW_WANT_PREV:
322 *move_dir = -1;
323 break;
324 default:
325 *move_dir = 0;
328 if (changed_hex_mode && !mcview_altered_hex_mode)
329 mcview_default_hex_mode = def_hex_mode;
330 if (changed_nroff_flag && !mcview_altered_nroff_flag)
331 mcview_default_nroff_flag = def_nroff_flag;
333 dialog_switch_process_pending ();
335 else if (is_cd)
337 char *q;
338 *p = 0;
339 p = buffer;
340 /* while (*p == ' ' && *p == '\t')
341 * p++;
343 /* Search last non-space character. Start search at the end in order
344 not to short filenames containing spaces. */
345 q = p + strlen (p) - 1;
346 while (q >= p && (*q == ' ' || *q == '\t'))
347 q--;
348 q[1] = 0;
349 do_cd (p, cd_parse_command);
351 else
353 shell_execute (cmd, EXECUTE_INTERNAL);
354 if (console_flag)
356 handle_console (CONSOLE_SAVE);
357 if (output_lines && keybar_visible)
358 show_console_contents (output_start_y,
359 LINES - keybar_visible -
360 output_lines - 1, LINES - keybar_visible - 1);
364 g_free (file_name);
365 g_free (cmd);
367 if (localcopy)
369 mc_stat (localcopy, &mystat);
370 mc_ungetlocalcopy (filename, localcopy, localmtime != mystat.st_mtime);
371 g_free (localcopy);
375 /* --------------------------------------------------------------------------------------------- */
377 * Run cmd_file with args, put result into buf.
378 * If error, put '\0' into buf[0]
379 * Return 1 if the data is valid, 0 otherwise, -1 for fatal errors.
381 * NOTES: buf is null-terminated string.
384 static int
385 get_popen_information (const char *cmd_file, const char *args, char *buf, int buflen)
387 gboolean read_bytes = FALSE;
388 char *command;
389 FILE *f;
391 command = g_strconcat (cmd_file, args, " 2>/dev/null", (char *) NULL);
392 f = popen (command, "r");
393 g_free (command);
395 if (f != NULL)
397 #ifdef __QNXNTO__
398 if (setvbuf (f, NULL, _IOFBF, 0) != 0)
400 (void) pclose (f);
401 return -1;
403 #endif
404 read_bytes = (fgets (buf, buflen, f) != NULL);
405 if (!read_bytes)
406 buf[0] = '\0'; /* Paranoid termination */
407 pclose (f);
409 else
411 buf[0] = '\0'; /* Paranoid termination */
412 return -1;
415 buf[buflen - 1] = '\0';
417 return read_bytes ? 1 : 0;
420 /* --------------------------------------------------------------------------------------------- */
422 * Run the "file" command on the local file.
423 * Return 1 if the data is valid, 0 otherwise, -1 for fatal errors.
426 static int
427 get_file_type_local (const char *filename, char *buf, int buflen)
429 char *tmp;
430 int ret;
432 tmp = name_quote (filename, 0);
433 ret = get_popen_information (FILE_CMD, tmp, buf, buflen);
434 g_free (tmp);
436 return ret;
439 /* --------------------------------------------------------------------------------------------- */
441 * Run the "enca" command on the local file.
442 * Return 1 if the data is valid, 0 otherwise, -1 for fatal errors.
445 #ifdef HAVE_CHARSET
446 static int
447 get_file_encoding_local (const char *filename, char *buf, int buflen)
449 char *tmp, *lang, *args;
450 int ret;
452 tmp = name_quote (filename, 0);
453 lang = name_quote (autodetect_codeset, 0);
454 args = g_strconcat (" -L", lang, " -i ", tmp, (char *) NULL);
456 ret = get_popen_information ("enca", args, buf, buflen);
458 g_free (args);
459 g_free (lang);
460 g_free (tmp);
462 return ret;
464 #endif /* HAVE_CHARSET */
466 /* --------------------------------------------------------------------------------------------- */
468 * Invoke the "file" command on the file and match its output against PTR.
469 * have_type is a flag that is set if we already have tried to determine
470 * the type of that file.
471 * Return 1 for match, 0 for no match, -1 errors.
474 static int
475 regex_check_type (const char *filename, const char *ptr, int *have_type)
477 int found = 0;
479 /* Following variables are valid if *have_type is 1 */
480 static char content_string[2048];
481 static char encoding_id[21]; /* CSISO51INISCYRILLIC -- 20 */
482 static size_t content_shift = 0;
483 static int got_data = 0;
485 if (!use_file_to_check_type)
486 return 0;
488 if (*have_type == 0)
490 char *realname; /* name used with "file" */
491 char *localfile;
493 #ifdef HAVE_CHARSET
494 int got_encoding_data;
495 #endif /* HAVE_CHARSET */
497 /* Don't repeate even unsuccessful checks */
498 *have_type = 1;
500 localfile = mc_getlocalcopy (filename);
501 if (localfile == NULL)
502 return -1;
504 realname = localfile;
506 #ifdef HAVE_CHARSET
507 got_encoding_data = is_autodetect_codeset_enabled
508 ? get_file_encoding_local (localfile, encoding_id, sizeof (encoding_id)) : 0;
510 if (got_encoding_data > 0)
512 char *pp;
513 int cp_id;
515 pp = strchr (encoding_id, '\n');
516 if (pp != NULL)
517 *pp = '\0';
519 cp_id = get_codepage_index (encoding_id);
520 if (cp_id == -1)
521 cp_id = default_source_codepage;
523 do_set_codepage (cp_id);
525 #endif /* HAVE_CHARSET */
527 mc_ungetlocalcopy (filename, localfile, 0);
529 got_data = get_file_type_local (localfile, content_string, sizeof (content_string));
531 if (got_data > 0)
533 char *pp;
534 size_t real_len;
536 pp = strchr (content_string, '\n');
537 if (pp != NULL)
538 *pp = '\0';
540 real_len = strlen (realname);
542 if (strncmp (content_string, realname, real_len) == 0)
544 /* Skip "realname: " */
545 content_shift = real_len;
546 if (content_string[content_shift] == ':')
548 /* Solaris' file prints tab(s) after ':' */
549 for (content_shift++;
550 content_string[content_shift] == ' '
551 || content_string[content_shift] == '\t'; content_shift++)
556 else
558 /* No data */
559 content_string[0] = '\0';
561 g_free (realname);
564 if (got_data == -1)
565 return -1;
567 if (content_string[0] != '\0'
568 && mc_search (ptr, content_string + content_shift, MC_SEARCH_T_REGEX))
570 found = 1;
573 return found;
576 /* --------------------------------------------------------------------------------------------- */
577 /*** public functions ****************************************************************************/
578 /* --------------------------------------------------------------------------------------------- */
580 void
581 flush_extension_file (void)
583 g_free (data);
584 data = NULL;
587 /* --------------------------------------------------------------------------------------------- */
589 * The second argument is action, i.e. Open, View or Edit
591 * This function returns:
593 * -1 for a failure or user interrupt
594 * 0 if no command was run
595 * 1 if some command was run
597 * If action == "View" then a parameter is checked in the form of "View:%d",
598 * if the value for %d exists, then the viewer is started up at that line number.
602 regex_command (const char *filename, const char *action, int *move_dir)
604 char *p, *q, *r, c;
605 int file_len = strlen (filename);
606 int found = 0;
607 int error_flag = 0;
608 int ret = 0;
609 struct stat mystat;
610 int view_at_line_number;
611 char *include_target;
612 int include_target_len;
613 int have_type = 0; /* Flag used by regex_check_type() */
615 /* Check for the special View:%d parameter */
616 if (strncmp (action, "View:", 5) == 0)
618 view_at_line_number = atoi (action + 5);
619 action = "View";
621 else
623 view_at_line_number = 0;
626 if (data == NULL)
628 char *extension_file;
629 int mc_user_ext = 1;
630 int home_error = 0;
632 extension_file = g_build_filename (home_dir, MC_USERCONF_DIR, MC_FILEBIND_FILE, NULL);
633 if (!exist_file (extension_file))
635 g_free (extension_file);
636 check_stock_mc_ext:
637 extension_file = concat_dir_and_file (mc_home, MC_LIB_EXT);
638 if (!exist_file (extension_file))
640 g_free (extension_file);
641 extension_file = concat_dir_and_file (mc_home_alt, MC_LIB_EXT);
643 mc_user_ext = 0;
646 g_file_get_contents (extension_file, &data, NULL, NULL);
647 g_free (extension_file);
648 if (data == NULL)
649 return 0;
651 if (!strstr (data, "default/"))
653 if (!strstr (data, "regex/") && !strstr (data, "shell/") && !strstr (data, "type/"))
655 g_free (data);
656 data = NULL;
657 if (mc_user_ext)
659 home_error = 1;
660 goto check_stock_mc_ext;
662 else
664 char *title = g_strdup_printf (_(" %s%s file error"),
665 mc_home, MC_LIB_EXT);
666 message (D_ERROR, title, _("The format of the %smc.ext "
667 "file has changed with version 3.0. It seems that "
668 "the installation failed. Please fetch a fresh "
669 "copy from the Midnight Commander package."),
670 mc_home);
671 g_free (title);
672 return 0;
676 if (home_error)
678 char *title = g_strdup_printf (_("~/%s file error"),
679 MC_USERCONF_DIR PATH_SEP_STR MC_FILEBIND_FILE);
680 message (D_ERROR, title,
681 _("The format of the ~/%s file has "
682 "changed with version 3.0. You may either want to copy "
683 "it from %smc.ext or use that file as an example of how to write it."),
684 MC_USERCONF_DIR PATH_SEP_STR MC_FILEBIND_FILE, mc_home);
685 g_free (title);
688 mc_stat (filename, &mystat);
690 include_target = NULL;
691 include_target_len = 0;
692 for (p = data; *p; p++)
694 for (q = p; *q == ' ' || *q == '\t'; q++);
695 if (*q == '\n' || !*q)
696 p = q; /* empty line */
697 if (*p == '#') /* comment */
698 while (*p && *p != '\n')
699 p++;
700 if (*p == '\n')
701 continue;
702 if (!*p)
703 break;
704 if (p == q)
705 { /* i.e. starts in the first column, should be
706 * keyword/descNL
708 found = 0;
709 q = strchr (p, '\n');
710 if (q == NULL)
711 q = strchr (p, 0);
712 c = *q;
713 *q = 0;
714 if (include_target)
716 if ((strncmp (p, "include/", 8) == 0)
717 && (strncmp (p + 8, include_target, include_target_len) == 0))
718 found = 1;
720 else if (!strncmp (p, "regex/", 6))
722 p += 6;
723 /* Do not transform shell patterns, you can use shell/ for
724 * that
726 if (mc_search (p, filename, MC_SEARCH_T_REGEX))
727 found = 1;
729 else if (!strncmp (p, "directory/", 10))
731 if (S_ISDIR (mystat.st_mode) && mc_search (p + 10, filename, MC_SEARCH_T_REGEX))
732 found = 1;
734 else if (!strncmp (p, "shell/", 6))
736 p += 6;
737 if (*p == '.' && file_len >= (q - p))
739 if (!strncmp (p, filename + file_len - (q - p), q - p))
740 found = 1;
742 else
744 if (q - p == file_len && !strncmp (p, filename, q - p))
745 found = 1;
748 else if (!strncmp (p, "type/", 5))
750 int res;
751 p += 5;
752 res = regex_check_type (filename, p, &have_type);
753 if (res == 1)
754 found = 1;
755 if (res == -1)
756 error_flag = 1; /* leave it if file cannot be opened */
758 else if (!strncmp (p, "default/", 8))
760 found = 1;
762 *q = c;
763 p = q;
764 if (!*p)
765 break;
767 else
768 { /* List of actions */
769 p = q;
770 q = strchr (p, '\n');
771 if (q == NULL)
772 q = strchr (p, 0);
773 if (found && !error_flag)
775 r = strchr (p, '=');
776 if (r != NULL)
778 c = *r;
779 *r = 0;
780 if (strcmp (p, "Include") == 0)
782 char *t;
784 include_target = p + 8;
785 t = strchr (include_target, '\n');
786 if (t)
787 *t = 0;
788 include_target_len = strlen (include_target);
789 if (t)
790 *t = '\n';
792 *r = c;
793 p = q;
794 found = 0;
796 if (!*p)
797 break;
798 continue;
800 if (!strcmp (action, p))
802 *r = c;
803 for (p = r + 1; *p == ' ' || *p == '\t'; p++);
805 /* Empty commands just stop searching
806 * through, they don't do anything
808 * We need to copy the filename because exec_extension
809 * may end up invoking update_panels thus making the
810 * filename parameter invalid (ie, most of the time,
811 * we get filename as a pointer from current_panel->dir).
813 if (p < q)
815 char *filename_copy = g_strdup (filename);
817 exec_extension (filename_copy, r + 1, move_dir, view_at_line_number);
818 g_free (filename_copy);
820 ret = 1;
822 break;
824 else
825 *r = c;
828 p = q;
829 if (!*p)
830 break;
833 if (error_flag)
834 return -1;
835 return ret;
838 /* --------------------------------------------------------------------------------------------- */