Clean menu -- made to use standart GNOME menu entries
[midnight-commander.git] / src / ext.c
blobf7e7c8b3212d57d96d87dda982c9ee18988c86a3
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., 675 Mass Ave, Cambridge, MA 02139, USA. */
21 #include <config.h>
22 #include <stdio.h>
23 #include <ctype.h>
24 #ifdef NEEDS_IO_H
25 # include <io.h>
26 #endif
28 #include "tty.h"
29 #ifdef HAVE_UNISTD_H
30 # include <unistd.h>
31 #endif
32 #include <fcntl.h>
33 #include <string.h>
34 #include <errno.h>
36 #include "global.h"
37 #include "user.h"
38 #include "main.h"
39 #include "dialog.h"
40 #include "ext.h"
41 #include "view.h"
42 #include "main.h"
43 #include "../vfs/vfs.h"
44 #include "x.h"
46 #include "cons.saver.h"
47 #include "layout.h"
48 #ifdef SCO_FLAVOR
49 #include <sys/wait.h>
50 #endif /* SCO_FLAVOR */
52 /* If set, we execute the file command to check the file type */
53 int use_file_to_check_type = 1;
55 /* This variable points to a copy of the mc.ext file in memory
56 * With this we avoid loading/parsing the file each time we
57 * need it
59 static char *data = NULL;
61 void
62 flush_extension_file (void)
64 if (data){
65 g_free (data);
66 data = NULL;
71 typedef char *(*quote_func_t)(const char *name, int i);
73 static char *
74 quote_block (quote_func_t quote_func, char **quoting_block)
76 char **p = quoting_block;
77 char *result = 0;
78 char *tail = 0;
79 int current_len = 0;
81 for (p = quoting_block; *p; p++){
82 int temp_len;
83 char *temp = quote_func (*p, 0);
85 temp_len = strlen (temp);
86 current_len += temp_len + 2;
87 result = g_realloc (result, current_len);
88 if (!tail)
89 tail = result;
90 strcpy (tail, temp);
91 strcat (tail, " ");
92 tail += temp_len + 1;
93 g_free (temp);
96 return result;
99 void
100 exec_extension (char *filename, char *data, char **drops, int *move_dir, int start_line)
102 char *file_name;
103 int cmd_file_fd;
104 FILE *cmd_file;
105 int expand_prefix_found = 0;
106 int parameter_found = 0;
107 char prompt [80];
108 int run_view = 0;
109 int def_hex_mode = default_hex_mode, changed_hex_mode = 0;
110 int def_nroff_flag = default_nroff_flag, changed_nroff_flag = 0;
111 int written_nonspace = 0;
112 int is_cd = 0;
113 char buffer [1024];
114 char *p = 0;
115 char *localcopy = NULL;
116 int do_local_copy;
117 time_t localmtime = 0;
118 struct stat mystat;
119 quote_func_t quote_func = name_quote;
121 g_return_if_fail (filename != NULL);
122 g_return_if_fail (data != NULL);
124 /* Avoid making a local copy if we are doing a cd */
125 if (!vfs_file_is_local(filename))
126 do_local_copy = 1;
127 else
128 do_local_copy = 0;
130 /* Note: this has to be done after the getlocalcopy call,
131 * since it uses tmpnam as well
133 file_name = g_strdup (tmpnam (NULL));
135 /* #warning FIXME: this is ugly */
136 if ((cmd_file_fd = open (file_name, O_RDWR | O_CREAT | O_TRUNC | O_EXCL, 0600)) == -1){
137 message (1, MSG_ERROR, _(" Can't create temporary command file \n %s "),
138 unix_error_string (errno));
139 return;
141 cmd_file = fdopen (cmd_file_fd, "w");
142 /* FIXME: This is a hack, that makes us sure, we are using the right syntax */
143 fprintf (cmd_file, "#!/bin/sh\n");
145 prompt [0] = 0;
146 for (;*data && *data != '\n'; data++){
147 if (parameter_found){
148 if (*data == '}'){
149 char *parameter;
150 parameter_found = 0;
151 parameter = input_dialog (_(" Parameter "), prompt, "");
152 if (!parameter){
153 /* User canceled */
154 fclose (cmd_file);
155 unlink (file_name);
156 if (localcopy) {
157 mc_ungetlocalcopy (filename, localcopy, 0);
159 g_free (file_name);
160 return;
162 fputs (parameter, cmd_file);
163 written_nonspace = 1;
164 g_free (parameter);
165 } else {
166 int len = strlen (prompt);
168 if (len < sizeof (prompt) - 1){
169 prompt [len] = *data;
170 prompt [len+1] = 0;
173 } else if (expand_prefix_found){
174 expand_prefix_found = 0;
175 if (*data == '{')
176 parameter_found = 1;
177 else {
178 int i = check_format_view (data);
179 char *v;
181 if (i){
182 data += i - 1;
183 run_view = 1;
184 } else if ((i = check_format_cd (data)) > 0) {
185 is_cd = 1;
186 quote_func = fake_name_quote;
187 do_local_copy = 0;
188 p = buffer;
189 data += i - 1;
190 } else if ((i = check_format_var (data, &v)) > 0 && v){
191 fputs (v, cmd_file);
192 g_free (v);
193 data += i;
194 } else {
195 char *text;
197 if (*data == 'f'){
198 if (do_local_copy){
199 localcopy = mc_getlocalcopy (filename);
200 if (localcopy == NULL) {
201 fclose(cmd_file);
202 unlink(file_name);
203 g_free (file_name);
204 return;
206 mc_stat (localcopy, &mystat);
207 localmtime = mystat.st_mtime;
208 text = (*quote_func) (localcopy, 0);
209 } else {
210 text = (*quote_func) (filename, 0);
212 } else if (*data == 'q') {
213 text = quote_block (quote_func, drops);
214 } else
215 text = expand_format (*data, !is_cd);
216 if (!is_cd)
217 fputs (text, cmd_file);
218 else {
219 strcpy (p, text);
220 p = strchr (p, 0);
222 g_free (text);
223 written_nonspace = 1;
226 } else {
227 if (*data == '%')
228 expand_prefix_found = 1;
229 else {
230 if (*data != ' ' && *data != '\t')
231 written_nonspace = 1;
232 if (is_cd)
233 *(p++) = *data;
234 else
235 fputc (*data, cmd_file);
238 } /* for */
239 fputc ('\n', cmd_file);
240 fclose (cmd_file);
241 chmod (file_name, S_IRWXU);
242 if (run_view){
243 altered_hex_mode = 0;
244 altered_nroff_flag = 0;
245 if (def_hex_mode != default_hex_mode)
246 changed_hex_mode = 1;
247 if (def_nroff_flag != default_nroff_flag)
248 changed_nroff_flag = 1;
250 /* If we've written whitespace only, then just load filename
251 * into view
253 if (written_nonspace)
254 view (file_name, filename, move_dir, start_line);
255 else
256 view (0, filename, move_dir, start_line);
257 if (changed_hex_mode && !altered_hex_mode)
258 default_hex_mode = def_hex_mode;
259 if (changed_nroff_flag && !altered_nroff_flag)
260 default_nroff_flag = def_nroff_flag;
261 repaint_screen ();
262 } else if (is_cd) {
263 char *q;
264 *p = 0;
265 p = buffer;
266 while (*p == ' ' && *p == '\t')
267 p++;
269 /* Search last non-space character. Start search at the end in order
270 not to short filenames containing spaces. */
271 q = p + strlen (p) - 1;
272 while (q >= p && (*q == ' ' || *q == '\t'))
273 q--;
274 q[1] = 0;
275 do_cd (p, cd_parse_command);
276 } else {
277 shell_execute (file_name, EXECUTE_INTERNAL | EXECUTE_TEMPFILE);
278 if (console_flag)
280 handle_console (CONSOLE_SAVE);
281 if (output_lines && keybar_visible)
283 show_console_contents (output_start_y,
284 LINES-keybar_visible-output_lines-1,
285 LINES-keybar_visible-1);
290 #ifdef OLD_CODE
291 if (vfs_current_is_local ())
292 shell_execute (file_name, EXECUTE_INTERNAL);
293 else
294 message (1, _(" Warning "), _(" Can't execute commands on a Virtual File System directory "));
295 #endif
297 #ifndef PORT_DOES_BACKGROUND_EXEC
298 unlink (file_name);
299 #endif
300 if (localcopy) {
301 mc_stat (localcopy, &mystat);
302 mc_ungetlocalcopy (filename, localcopy, localmtime != mystat.st_mtime);
304 g_free (file_name);
307 #ifdef FILE_L
308 # define FILE_CMD "file -L "
309 #else
310 # define FILE_CMD "file "
311 #endif
313 /* The second argument is action, i.e. Open, View, Edit, Drop, or NULL if
314 * we want regex_command to return a list of all user defined actions.
315 * Third argument is space separated list of dropped files (for actions
316 * other then Drop it should be NULL);
318 * This function returns:
320 * If action != NULL, then it returns "Success" (not allocated) if it ran
321 * some command or NULL if not.
323 * If action == NULL, it returns NULL if there are no user defined commands
324 * or an allocated space separated list of user defined Actions.
326 * If action == "Icon", we are doing again something special. We return
327 * icon name and we set the variable regex_command_title to Title for
328 * that icon.
330 * If action == "View" then a parameter is checked in the form of "View:%d",
331 * if the value for %d exists, then the viewer is started up at that line number.
333 char *regex_command_title = NULL;
334 char *regex_command (char *filename, char *action, char **drops, int *move_dir)
336 char *extension_file;
337 char *p, *q, *r, c;
338 char *buffer;
339 int file_len = strlen (filename);
340 int found = 0;
341 char content_string [2048];
342 int content_shift = 0;
343 char *to_return = NULL;
344 int old_patterns;
345 struct stat mystat;
346 int asked_file;
347 int view_at_line_number;
348 char *include_target;
349 int include_target_len;
351 #ifdef FILE_STDIN
352 int file_supports_stdin = 1;
353 #else
354 int file_supports_stdin = 0;
355 #endif
357 /* Check for the special View:%d parameter */
358 if (action && strncmp (action, "View:", 5) == 0){
359 view_at_line_number = atoi (action + 5);
360 action [4] = 0;
361 } else {
362 view_at_line_number = 0;
364 /* Have we asked file for the file contents? */
365 asked_file = 0;
367 if (data == NULL) {
368 int home_error = 0;
370 buffer = concat_dir_and_file (home_dir, MC_USER_EXT);
371 if (exist_file (buffer))
372 extension_file = buffer;
373 else
374 check_stock_mc_ext:
375 extension_file = concat_dir_and_file (mc_home, MC_LIB_EXT);
376 if ((data = load_file (extension_file)) == NULL) {
377 g_free (buffer);
378 return 0;
380 if (!strstr (data, "default/")) {
381 if (!strstr (data, "regex/") && !strstr (data, "shell/") &&
382 !strstr (data, "type/")) {
383 g_free (data);
384 data = NULL;
385 if (extension_file == buffer) {
386 home_error = 1;
387 goto check_stock_mc_ext;
388 } else {
389 char *msg;
390 char *msg2;
391 msg = g_strconcat (" ", mc_home, MC_LIB_EXT, _(" file error"), NULL);
392 msg2 = g_strconcat (_("Format of the "),
393 mc_home,
394 ("mc.ext file has changed\n\
395 with version 3.0. It seems that installation\n\
396 failed. Please fetch a fresh new copy from the\n\
397 Midnight Commander package or in case you don't\n\
398 have any, get it from ftp://ftp.nuclecu.unam.mx."), NULL);
399 message (1, msg, msg2);
400 g_free (msg);
401 g_free (msg2);
402 g_free (buffer);
403 return 0;
407 if (home_error) {
408 char *msg;
409 char *msg2;
410 msg = g_strconcat (" ~/", MC_USER_EXT, _(" file error "), NULL);
411 msg2 = g_strconcat (_("Format of the ~/"), MC_USER_EXT, _(" file has changed\n\
412 with version 3.0. You may want either to\n\
413 copy it from "), mc_home, _("mc.ext or use that\n\
414 file as an example of how to write it.\n\
415 "), mc_home, _("mc.ext will be used for this moment."), NULL);
416 message (1, msg, msg2);
417 g_free (msg);
418 g_free (msg2);
420 g_free (buffer);
422 mc_stat (filename, &mystat);
424 if (regex_command_title){
425 g_free (regex_command_title);
426 regex_command_title = NULL;
428 old_patterns = easy_patterns;
429 easy_patterns = 0; /* Real regular expressions are needed :) */
430 include_target = NULL;
431 for (p = data; *p; p++) {
432 for (q = p; *q == ' ' || *q == '\t'; q++)
434 if (*q == '\n' || !*q)
435 p = q; /* empty line */
436 if (*p == '#') /* comment */
437 while (*p && *p != '\n')
438 p++;
439 if (*p == '\n')
440 continue;
441 if (!*p)
442 break;
443 if (p == q) { /* i.e. starts in the first column, should be
444 * keyword/descNL
446 if (found && action == NULL) /* We have already accumulated all
447 * the user actions
449 break;
450 found = 0;
451 q = strchr (p, '\n');
452 if (q == NULL)
453 q = strchr (p, 0);
454 c = *q;
455 *q = 0;
456 if (include_target){
457 if ((strncmp (p, "include/", 8) == 0) &&
458 (strncmp (p+8, include_target, include_target_len) == 0))
459 found = 1;
460 } else if (!strncmp (p, "regex/", 6)) {
461 p += 6;
462 /* Do not transform shell patterns, you can use shell/ for
463 * that
465 if (regexp_match (p, filename, match_normal))
466 found = 1;
467 } else if (!strncmp (p, "directory/", 10)) {
468 if (S_ISDIR (mystat.st_mode) && regexp_match (p+10, filename, match_normal))
469 found = 1;
470 } else if (!strncmp (p, "shell/", 6)) {
471 p += 6;
472 if (*p == '.') {
473 if (!strncmp (p, filename + file_len - (q - p),
474 q - p))
475 found = 1;
476 } else {
477 if (q - p == file_len && !strncmp (p, filename, q - p))
478 found = 1;
480 } else if (!strncmp (p, "type/", 5)) {
481 int islocal = vfs_file_is_local (filename);
482 p += 5;
484 if (islocal || file_supports_stdin) {
485 char *pp;
486 int hasread = use_file_to_check_type;
488 if (asked_file || !use_file_to_check_type)
489 goto match_file_output;
491 hasread = 0;
492 if (islocal) {
493 char *tmp = name_quote (filename, 0);
494 char *command =
495 g_strconcat (FILE_CMD, tmp, NULL);
496 FILE *f = popen (command, "r");
498 g_free (tmp);
499 g_free (command);
500 if (f != NULL) {
501 hasread = (fgets (content_string, 2047, f)
502 != NULL);
503 if (!hasread)
504 content_string [0] = 0;
505 pclose (f);
506 #ifdef SCO_FLAVOR
508 ** SCO 3.2 does has a buggy pclose(), so
509 ** <command> become zombie (alex)
511 waitpid(-1,NULL,WNOHANG);
512 #endif /* SCO_FLAVOR */
514 } else {
515 #ifdef _OS_NT
516 message (1, " Win32 ", " Unimplemented file prediction ");
517 #else
518 int pipehandle, remotehandle;
519 pid_t p;
521 remotehandle = mc_open (filename, O_RDONLY);
522 if (remotehandle != -1) {
523 /* 8192 is HOWMANY hardcoded value in the file-3.14
524 * sources. Tell me if any other file uses larger
525 * chunk from beginning
527 pipehandle = mc_doublepopen
528 (remotehandle, 8192, &p,"file", "file", "-", NULL);
529 if (pipehandle != -1) {
530 int i;
531 while ((i = read (pipehandle, content_string
532 + hasread, 2047 - hasread)) > 0)
533 hasread += i;
534 mc_doublepclose (pipehandle, p);
535 content_string [hasread] = 0;
537 mc_close (remotehandle);
539 #endif /* _OS_NT */
541 asked_file = 1;
542 match_file_output:
543 if (hasread) {
544 if ((pp = strchr (content_string, '\n')) != 0)
545 *pp = 0;
546 if (islocal && !strncmp (content_string,
547 filename, file_len)) {
548 content_shift = file_len;
549 if (content_string [content_shift] == ':')
550 for (content_shift++;
551 content_string [content_shift] == ' ';
552 content_shift++);
553 } else if (!islocal
554 && !strncmp (content_string,
555 "standard input:", 15)) {
556 for (content_shift = 15;
557 content_string [content_shift] == ' ';
558 content_shift++);
560 if (content_string &&
561 regexp_match (p, content_string +
562 content_shift, match_normal)){
563 found = 1;
567 } else if (!strncmp (p, "default/", 8)) {
568 p += 8;
569 found = 1;
571 *q = c;
572 p = q;
573 if (!*p)
574 break;
575 } else { /* List of actions */
576 p = q;
577 q = strchr (p, '\n');
578 if (q == NULL)
579 q = strchr (p, 0);
580 if (found) {
581 r = strchr (p, '=');
582 if (r != NULL) {
583 c = *r;
584 *r = 0;
585 if (strcmp (p, "Include") == 0){
586 char *t;
588 include_target = p + 8;
589 t = strchr (include_target, '\n');
590 if (t) *t = 0;
591 include_target_len = strlen (include_target);
592 if (t) *t = '\n';
594 *r = c;
595 p = q;
596 found = 0;
598 if (!*p)
599 break;
600 continue;
602 if (action == NULL) {
603 if (strcmp (p, "Open") &&
604 strcmp (p, "View") &&
605 strcmp (p, "Edit") &&
606 strcmp (p, "Drop") &&
607 strcmp (p, "Icon") &&
608 strcmp (p, "Include") &&
609 strcmp (p, "Title")) {
610 /* I.e. this is a name of a user defined action */
611 static char *q;
613 if (to_return == NULL) {
614 to_return = g_malloc (512);
615 q = to_return;
616 } else
617 *(q++) = '='; /* Mark separator */
618 strcpy (q, p);
619 q = strchr (q, 0);
621 *r = c;
622 } else if (!strcmp (action, "Icon")) {
623 if (!strcmp (p, "Icon") && to_return == NULL) {
624 *r = c;
625 c = *q;
626 *q = 0;
627 to_return = g_strdup (r + 1);
628 } else if (!strcmp (p, "Title") && regex_command_title == NULL) {
629 *r = c;
630 c = *q;
631 *q = 0;
632 regex_command_title = g_strdup (r + 1);
633 } else {
634 *r = c;
635 c = *q;
637 *q = c;
638 if (to_return != NULL && regex_command_title != NULL)
639 break;
640 } else if (!strcmp (action, p)) {
641 *r = c;
642 for (p = r + 1; *p == ' ' || *p == '\t'; p++)
645 /* Empty commands just stop searching
646 * through, they don't do anything
648 * We need to copy the filename because exec_extension
649 * may end up invoking update_panels thus making the
650 * filename parameter invalid (ie, most of the time,
651 * we get filename as a pointer from cpanel->dir).
653 if (p < q) {
654 char *filename_copy = g_strdup (filename);
656 exec_extension (filename_copy, r + 1, drops, move_dir, view_at_line_number);
657 g_free (filename_copy);
659 to_return = "Success";
661 break;
662 } else
663 *r = c;
666 p = q;
667 if (!*p)
668 break;
671 easy_patterns = old_patterns;
672 return to_return;