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