Just a little correction at the it.po file.
[midnight-commander.git] / src / ext.c
blob14bc4efa9313b33c8ce678220096a4bf7edf6991
1 /* Extension dependent execution.
2 Copyright (C) 1994, 1995 The Free Software Foundation
4 Written by: 1995 Jakub Jelinek
5 1994 Miguel de Icaza
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
21 #include <config.h>
22 #include <stdio.h>
23 #include <ctype.h>
25 #ifdef HAVE_UNISTD_H
26 # include <unistd.h>
27 #endif
28 #include <string.h>
29 #include <errno.h>
31 #include "global.h"
32 #include "tty.h"
33 #include "user.h"
34 #include "main.h"
35 #include "wtools.h"
36 #include "ext.h"
37 #include "view.h"
39 #include "cons.saver.h"
40 #include "layout.h"
42 /* If set, we execute the file command to check the file type */
43 int use_file_to_check_type = 1;
45 /* This variable points to a copy of the mc.ext file in memory
46 * With this we avoid loading/parsing the file each time we
47 * need it
49 static char *data = NULL;
51 void
52 flush_extension_file (void)
54 if (data) {
55 g_free (data);
56 data = NULL;
61 typedef char *(*quote_func_t) (const char *name, int i);
63 static void
64 exec_extension (const char *filename, const char *data, int *move_dir,
65 int start_line)
67 char *file_name;
68 int cmd_file_fd;
69 FILE *cmd_file;
70 char *cmd = NULL;
71 int expand_prefix_found = 0;
72 int parameter_found = 0;
73 char prompt[80];
74 int run_view = 0;
75 int def_hex_mode = default_hex_mode, changed_hex_mode = 0;
76 int def_nroff_flag = default_nroff_flag, changed_nroff_flag = 0;
77 int written_nonspace = 0;
78 int is_cd = 0;
79 char buffer[1024];
80 char *p = 0;
81 char *localcopy = NULL;
82 int do_local_copy;
83 time_t localmtime = 0;
84 struct stat mystat;
85 quote_func_t quote_func = name_quote;
87 g_return_if_fail (filename != NULL);
88 g_return_if_fail (data != NULL);
90 /* Avoid making a local copy if we are doing a cd */
91 if (!vfs_file_is_local (filename))
92 do_local_copy = 1;
93 else
94 do_local_copy = 0;
97 * All commands should be run in /bin/sh regardless of user shell.
98 * To do that, create temporary shell script and run it.
99 * Sometimes it's not needed (e.g. for %cd and %view commands),
100 * but it's easier to create it anyway.
102 cmd_file_fd = mc_mkstemps (&file_name, "mcext", SCRIPT_SUFFIX);
104 if (cmd_file_fd == -1) {
105 message (1, MSG_ERROR,
106 _(" Cannot create temporary command file \n %s "),
107 unix_error_string (errno));
108 return;
110 cmd_file = fdopen (cmd_file_fd, "w");
111 fputs ("#! /bin/sh\n", cmd_file);
113 prompt[0] = 0;
114 for (; *data && *data != '\n'; data++) {
115 if (parameter_found) {
116 if (*data == '}') {
117 char *parameter;
118 parameter_found = 0;
119 parameter = input_dialog (_(" Parameter "), prompt, "");
120 if (!parameter) {
121 /* User canceled */
122 fclose (cmd_file);
123 unlink (file_name);
124 if (localcopy) {
125 mc_ungetlocalcopy (filename, localcopy, 0);
127 g_free (file_name);
128 return;
130 fputs (parameter, cmd_file);
131 written_nonspace = 1;
132 g_free (parameter);
133 } else {
134 int len = strlen (prompt);
136 if (len < sizeof (prompt) - 1) {
137 prompt[len] = *data;
138 prompt[len + 1] = 0;
141 } else if (expand_prefix_found) {
142 expand_prefix_found = 0;
143 if (*data == '{')
144 parameter_found = 1;
145 else {
146 int i = check_format_view (data);
147 char *v;
149 if (i) {
150 data += i - 1;
151 run_view = 1;
152 } else if ((i = check_format_cd (data)) > 0) {
153 is_cd = 1;
154 quote_func = fake_name_quote;
155 do_local_copy = 0;
156 p = buffer;
157 data += i - 1;
158 } else if ((i = check_format_var (data, &v)) > 0 && v) {
159 fputs (v, cmd_file);
160 g_free (v);
161 data += i;
162 } else {
163 char *text;
165 if (*data == 'f') {
166 if (do_local_copy) {
167 localcopy = mc_getlocalcopy (filename);
168 if (localcopy == NULL) {
169 fclose (cmd_file);
170 unlink (file_name);
171 g_free (file_name);
172 return;
174 mc_stat (localcopy, &mystat);
175 localmtime = mystat.st_mtime;
176 text = (*quote_func) (localcopy, 0);
177 } else {
178 text = (*quote_func) (filename, 0);
180 } else
181 text = expand_format (NULL, *data, !is_cd);
182 if (!is_cd)
183 fputs (text, cmd_file);
184 else {
185 strcpy (p, text);
186 p = strchr (p, 0);
188 g_free (text);
189 written_nonspace = 1;
192 } else {
193 if (*data == '%')
194 expand_prefix_found = 1;
195 else {
196 if (*data != ' ' && *data != '\t')
197 written_nonspace = 1;
198 if (is_cd)
199 *(p++) = *data;
200 else
201 fputc (*data, cmd_file);
204 } /* for */
207 * Make the script remove itself when it finishes.
208 * Don't do it for the viewer - it may need to rerun the script,
209 * so we clean up after calling view().
211 if (!run_view) {
212 fprintf (cmd_file, "\n/bin/rm -f %s\n", file_name);
215 fclose (cmd_file);
217 if ((run_view && !written_nonspace) || is_cd) {
218 unlink (file_name);
219 g_free (file_name);
220 file_name = NULL;
221 } else {
222 int cmd_len = strlen (file_name) + 10;
224 /* Set executable flag on the command file ... */
225 chmod (file_name, S_IRWXU);
226 /* ... but don't rely on it - run /bin/sh explicitly */
227 cmd = g_malloc (cmd_len);
228 g_snprintf (cmd, cmd_len, "/bin/sh %s", file_name);
231 if (run_view) {
232 altered_hex_mode = 0;
233 altered_nroff_flag = 0;
234 if (def_hex_mode != default_hex_mode)
235 changed_hex_mode = 1;
236 if (def_nroff_flag != default_nroff_flag)
237 changed_nroff_flag = 1;
239 /* If we've written whitespace only, then just load filename
240 * into view
242 if (written_nonspace) {
243 view (cmd, filename, move_dir, start_line);
244 unlink (file_name);
245 } else {
246 view (0, filename, move_dir, start_line);
248 if (changed_hex_mode && !altered_hex_mode)
249 default_hex_mode = def_hex_mode;
250 if (changed_nroff_flag && !altered_nroff_flag)
251 default_nroff_flag = def_nroff_flag;
252 repaint_screen ();
253 } else if (is_cd) {
254 char *q;
255 *p = 0;
256 p = buffer;
257 /* while (*p == ' ' && *p == '\t')
258 * p++;
260 /* Search last non-space character. Start search at the end in order
261 not to short filenames containing spaces. */
262 q = p + strlen (p) - 1;
263 while (q >= p && (*q == ' ' || *q == '\t'))
264 q--;
265 q[1] = 0;
266 do_cd (p, cd_parse_command);
267 } else {
268 shell_execute (cmd, EXECUTE_INTERNAL);
269 if (console_flag) {
270 handle_console (CONSOLE_SAVE);
271 if (output_lines && keybar_visible) {
272 show_console_contents (output_start_y,
273 LINES - keybar_visible -
274 output_lines - 1,
275 LINES - keybar_visible - 1);
281 g_free (file_name);
282 g_free (cmd);
284 if (localcopy) {
285 mc_stat (localcopy, &mystat);
286 mc_ungetlocalcopy (filename, localcopy,
287 localmtime != mystat.st_mtime);
291 #ifdef FILE_L
292 # define FILE_CMD "file -L "
293 #else
294 # define FILE_CMD "file "
295 #endif
298 * Run the "file" command on the local file.
299 * Return 1 if the data is valid, 0 otherwise, -1 for fatal errors.
301 static int
302 get_file_type_local (char *filename, char *buf, int buflen)
304 int read_bytes = 0;
306 char *tmp = name_quote (filename, 0);
307 char *command = g_strconcat (FILE_CMD, tmp, NULL);
308 FILE *f = popen (command, "r");
310 g_free (tmp);
311 g_free (command);
312 if (f != NULL) {
313 read_bytes = (fgets (buf, buflen - 1, f)
314 != NULL);
315 if (read_bytes == 0)
316 buf[0] = 0;
317 pclose (f);
318 #ifdef SCO_FLAVOR
320 ** SCO 3.2 does has a buggy pclose(), so
321 ** <command> become zombie (alex)
323 waitpid (-1, NULL, WNOHANG);
324 #endif /* SCO_FLAVOR */
325 } else {
326 return -1;
329 return (read_bytes > 0);
333 #ifdef FILE_STDIN
335 * Read file through VFS and feed is to the "file" command.
336 * Return 1 if the data is valid, 0 otherwise, -1 for fatal errors.
338 static int
339 get_file_type_pipe (char *filename, char *buf, int buflen)
341 int read_bytes = 0;
343 int pipehandle, remotehandle;
344 pid_t p;
346 remotehandle = mc_open (filename, O_RDONLY);
347 if (remotehandle != -1) {
348 /* 8192 is HOWMANY hardcoded value in the file-3.14
349 * sources. Tell me if any other file uses larger
350 * chunk from beginning
352 pipehandle =
353 mc_doublepopen (remotehandle, 8192, &p, "file", "file", "-",
354 NULL);
355 if (pipehandle != -1) {
356 int i;
357 while ((i =
358 read (pipehandle, buf + read_bytes,
359 buflen - 1 - read_bytes)) > 0)
360 read_bytes += i;
361 mc_doublepclose (pipehandle, p);
362 buf[read_bytes] = 0;
364 mc_close (remotehandle);
365 } else {
366 return -1;
369 return (read_bytes > 0);
371 #endif /* FILE_STDIN */
375 * Invoke the "file" command on the file and match its output against PTR.
376 * have_type is a flag that is set if we already have tried to determine
377 * the type of that file.
378 * Return 1 for match, 0 otherwise.
380 static int
381 regex_check_type (char *filename, int file_len, char *ptr, int *have_type)
383 int found = 0;
384 int islocal;
386 /* Following variables are valid if *have_type is 1 */
387 static char content_string[2048];
388 static int content_shift = 0;
389 static int got_data = 0;
391 if (!use_file_to_check_type) {
392 return 0;
395 islocal = vfs_file_is_local (filename);
397 if (!*have_type) {
398 /* Don't repeate even unsuccessful checks */
399 *have_type = 1;
401 if (islocal) {
402 got_data =
403 get_file_type_local (filename, content_string,
404 sizeof (content_string));
405 } else
406 #ifdef FILE_STDIN
408 got_data =
409 get_file_type_pipe (filename, content_string,
410 sizeof (content_string));
412 #else
413 /* Cannot use pipe, must make a local copy, not yet supported */
414 return 0;
415 #endif /* !FILE_STDIN */
417 if (got_data > 0) {
418 char *pp;
420 /* Paranoid termination */
421 content_string[sizeof (content_string) - 1] = 0;
423 if ((pp = strchr (content_string, '\n')) != 0)
424 *pp = 0;
426 if (islocal && !strncmp (content_string, filename, file_len)) {
427 /* Skip "filename: " */
428 content_shift = file_len;
429 if (content_string[content_shift] == ':')
430 for (content_shift++;
431 content_string[content_shift] == ' ';
432 content_shift++);
433 } else if (!islocal
434 && !strncmp (content_string, "standard input:",
435 15)) {
436 /* Skip "standard input: " */
437 for (content_shift = 15;
438 content_string[content_shift] == ' ';
439 content_shift++);
441 } else {
442 /* No data */
443 content_string[0] = 0;
447 if (got_data == -1) {
448 return -1;
451 if (content_string && content_string[0]
452 && regexp_match (ptr, content_string + content_shift,
453 match_normal)) {
454 found = 1;
457 return found;
461 /* The second argument is action, i.e. Open, View or Edit
463 * This function returns:
465 * -1 for a failure or user interrupt
466 * 0 if no command was run
467 * 1 if some command was run
469 * If action == "View" then a parameter is checked in the form of "View:%d",
470 * if the value for %d exists, then the viewer is started up at that line number.
473 regex_command (char *filename, char *action, int *move_dir)
475 char *p, *q, *r, c;
476 int file_len = strlen (filename);
477 int found = 0;
478 int error_flag = 0;
479 int ret = 0;
480 int old_patterns;
481 struct stat mystat;
482 int view_at_line_number;
483 char *include_target;
484 int include_target_len;
485 int have_type = 0; /* Flag used by regex_check_type() */
487 /* Check for the special View:%d parameter */
488 if (strncmp (action, "View:", 5) == 0) {
489 view_at_line_number = atoi (action + 5);
490 action[4] = 0;
491 } else {
492 view_at_line_number = 0;
495 if (data == NULL) {
496 char *extension_file;
497 int mc_user_ext = 1;
498 int home_error = 0;
500 extension_file = concat_dir_and_file (home_dir, MC_USER_EXT);
501 if (!exist_file (extension_file)) {
502 g_free (extension_file);
503 check_stock_mc_ext:
504 extension_file = concat_dir_and_file (mc_home, MC_LIB_EXT);
505 mc_user_ext = 0;
507 data = load_file (extension_file);
508 g_free (extension_file);
509 if (data == NULL)
510 return 0;
512 if (!strstr (data, "default/")) {
513 if (!strstr (data, "regex/") && !strstr (data, "shell/")
514 && !strstr (data, "type/")) {
515 g_free (data);
516 data = NULL;
517 if (mc_user_ext) {
518 home_error = 1;
519 goto check_stock_mc_ext;
520 } else {
521 char *msg;
522 char *msg2;
523 msg =
524 g_strconcat (" ", mc_home, MC_LIB_EXT,
525 _(" file error "), NULL);
526 msg2 =
527 g_strconcat (_("Format of the "), mc_home,
528 _("mc.ext file has changed\n"
529 "with version 3.0. It seems that installation\n"
530 "failed. Please fetch a fresh new copy from the\n"
531 "Midnight Commander package."),
532 NULL);
533 message (1, msg, "%s", msg2);
534 g_free (msg);
535 g_free (msg2);
536 return 0;
540 if (home_error) {
541 char *msg;
542 char *msg2;
543 msg =
544 g_strconcat (" ~/", MC_USER_EXT, _(" file error "), NULL);
545 msg2 =
546 g_strconcat (_("Format of the "), "~/", MC_USER_EXT,
547 _(" file has changed\n"
548 "with version 3.0. You may want either to\n"
549 "copy it from "), mc_home,
550 _("mc.ext or use that\n"
551 "file as an example of how to write it.\n"),
552 mc_home,
553 _("mc.ext will be used for this moment."),
554 NULL);
555 message (1, msg, "%s", msg2);
556 g_free (msg);
557 g_free (msg2);
560 mc_stat (filename, &mystat);
562 old_patterns = easy_patterns;
563 easy_patterns = 0; /* Real regular expressions are needed :) */
564 include_target = NULL;
565 include_target_len = 0;
566 for (p = data; *p; p++) {
567 for (q = p; *q == ' ' || *q == '\t'; q++);
568 if (*q == '\n' || !*q)
569 p = q; /* empty line */
570 if (*p == '#') /* comment */
571 while (*p && *p != '\n')
572 p++;
573 if (*p == '\n')
574 continue;
575 if (!*p)
576 break;
577 if (p == q) { /* i.e. starts in the first column, should be
578 * keyword/descNL
580 found = 0;
581 q = strchr (p, '\n');
582 if (q == NULL)
583 q = strchr (p, 0);
584 c = *q;
585 *q = 0;
586 if (include_target) {
587 if ((strncmp (p, "include/", 8) == 0)
588 && (strncmp (p + 8, include_target, include_target_len)
589 == 0))
590 found = 1;
591 } else if (!strncmp (p, "regex/", 6)) {
592 p += 6;
593 /* Do not transform shell patterns, you can use shell/ for
594 * that
596 if (regexp_match (p, filename, match_normal))
597 found = 1;
598 } else if (!strncmp (p, "directory/", 10)) {
599 if (S_ISDIR (mystat.st_mode)
600 && regexp_match (p + 10, filename, match_normal))
601 found = 1;
602 } else if (!strncmp (p, "shell/", 6)) {
603 p += 6;
604 if (*p == '.' && file_len >= (q - p)) {
605 if (!strncmp (p, filename + file_len - (q - p), q - p))
606 found = 1;
607 } else {
608 if (q - p == file_len && !strncmp (p, filename, q - p))
609 found = 1;
611 } else if (!strncmp (p, "type/", 5)) {
612 int res;
613 p += 5;
614 res = regex_check_type (filename, file_len, p, &have_type);
615 if (res == 1)
616 found = 1;
617 if (res == -1)
618 error_flag = 1; /* leave it if file cannot be opened */
619 } else if (!strncmp (p, "default/", 8)) {
620 found = 1;
622 *q = c;
623 p = q;
624 if (!*p)
625 break;
626 } else { /* List of actions */
627 p = q;
628 q = strchr (p, '\n');
629 if (q == NULL)
630 q = strchr (p, 0);
631 if (found && !error_flag) {
632 r = strchr (p, '=');
633 if (r != NULL) {
634 c = *r;
635 *r = 0;
636 if (strcmp (p, "Include") == 0) {
637 char *t;
639 include_target = p + 8;
640 t = strchr (include_target, '\n');
641 if (t)
642 *t = 0;
643 include_target_len = strlen (include_target);
644 if (t)
645 *t = '\n';
647 *r = c;
648 p = q;
649 found = 0;
651 if (!*p)
652 break;
653 continue;
655 if (!strcmp (action, p)) {
656 *r = c;
657 for (p = r + 1; *p == ' ' || *p == '\t'; p++);
659 /* Empty commands just stop searching
660 * through, they don't do anything
662 * We need to copy the filename because exec_extension
663 * may end up invoking update_panels thus making the
664 * filename parameter invalid (ie, most of the time,
665 * we get filename as a pointer from cpanel->dir).
667 if (p < q) {
668 char *filename_copy = g_strdup (filename);
670 exec_extension (filename_copy, r + 1, move_dir,
671 view_at_line_number);
672 g_free (filename_copy);
674 ret = 1;
676 break;
677 } else
678 *r = c;
681 p = q;
682 if (!*p)
683 break;
686 easy_patterns = old_patterns;
687 if (error_flag)
688 return -1;
689 return ret;