1 /* User Menu implementation
2 Copyright (C) 1994 Miguel de Icaza, Janne Kukonlehto
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
24 #include <stdlib.h> /* For free() */
40 #include "../vfs/vfs.h"
42 /* For the simple listbox manager */
47 #include "view.h" /* for default_* externs */
51 #define MAX_ENTRIES 40
52 #define MAX_ENTRY_LEN 60
55 static char *entries
[MAX_ENTRIES
];
57 static int menu_lines
;
58 static int debug_flag
= 0;
59 static int debug_error
= 0;
60 extern char *search_string (char *, char *);
64 %f The current file (if non-local vfs, file will be copied locally and
65 %f will be full path to it).
67 %d The current working directory
68 %s "Selected files"; the tagged files if any, otherwise the current file
70 %u Tagged files (and they are untagged on return from expand_format)
71 %view Runs the commands and pipes standard output to the view command
72 if %view is directly followed by '{', a list of keywords
73 ascii, hex, nroff, unformated and
75 If the format letter is in uppercase, it refers to the other panel.
77 With a number followed the % character you can turn quoting on (default)
79 %f quote expanded macro
81 %0f don't quote expanded macro
83 expand_format returns a memory block that must be free()d.
86 /* Returns how many characters we should advance if %view was found */
87 int check_format_view (char *p
)
90 if (!strncmp (p
, "view", 4)){
93 for (q
++;*q
&& *q
!= '}';q
++){
94 if (!strncmp (q
, "ascii", 5)){
97 } else if (!strncmp (q
, "hex", 3)){
100 } else if (!strncmp (q
, "nroff", 5)){
101 default_nroff_flag
= 1;
103 } else if (!strncmp (q
, "unformated", 10)){
104 default_nroff_flag
= 0;
116 int check_format_cd (char *p
)
118 if (!strncmp (p
, "cd", 2))
124 /* Check if p has a "^var\{var-name\}" */
125 /* Returns the number of skipped characters (zero on not found) */
126 /* V will be set to the expanded variable name */
127 int check_format_var (char *p
, char **v
)
136 if (!strncmp (p
, "var{", 4)){
137 for (q
+= 4; *q
&& *q
!= '}'; q
++){
145 if (!dots
|| dots
== q
+5){
147 " Format error on file Extensions File ",
148 !dots
? " The %%var macro does not have a default "
149 : " The %%var macros does not have a variable " );
153 /* Copy the variable name */
154 var_name
= xmalloc (dots
- p
, "check_format_var");
155 strncpy (var_name
, p
+4, dots
-2 - (p
+3));
156 var_name
[dots
-2 - (p
+3)] = 0;
158 value
= getenv (var_name
);
164 var_name
= xmalloc (q
- dots
+ 1, "check_format_var_2");
165 strncpy (var_name
, dots
, q
- dots
+ 1);
166 var_name
[q
-dots
] = 0;
173 /* strip file's extension */
174 char *strip_ext(char *ss
)
176 register char *s
= ss
;
180 if(*s
== PATH_SEP
&& e
) e
=NULL
; /* '.' in *directory* name */
187 char *expand_format (char c
, int quote
)
190 char *(*quote_func
)(const char *, int);
193 quote_func
= name_quote
;
195 quote_func
= fake_name_quote
;
203 if (get_other_type () == view_listing
){
215 case 'p': return (*quote_func
) (panel
->dir
.list
[panel
->selected
].fname
, 0);
217 return strip_ext((*quote_func
) (panel
->dir
.list
[panel
->selected
].fname
, 0));
218 case 'd': return (*quote_func
) (panel
->cwd
, 0);
221 return (*quote_func
) (panel
->dir
.list
[panel
->selected
].fname
, 0);
231 for (i
= 0; i
< panel
->count
; i
++)
232 if (panel
->dir
.list
[i
].f
.marked
)
233 length
+= strlen (panel
->dir
.list
[i
].fname
) + 1;
235 block
= xmalloc (length
*2+1, "expand_format");
237 for (i
= 0; i
< panel
->count
; i
++)
238 if (panel
->dir
.list
[i
].f
.marked
){
239 strcat (block
, tmp
= (*quote_func
) (panel
->dir
.list
[i
].fname
, 0));
243 do_file_mark (panel
, i
, 0);
246 } /* sub case block */
251 /* Checks for shell patterns defination */
252 char *check_patterns (char *p
)
254 const char *def_name
= "shell_patterns=";
257 if (strncmp (p
, def_name
, sizeof (def_name
)) == 0){
258 p
+= strlen (def_name
);
260 if (value
== 0 || value
== 1)
261 easy_patterns
= value
;
263 message (1, MSG_ERROR
, _(" Invalid shell pattern defination \"%c\". "), value
+ '0');
265 while (*p
== '\n' || *p
== '\t' || *p
== ' ') p
++;
269 /* Copies a whitespace separated argument from p to arg. Returns the
270 point after argument. */
271 static char *extract_arg (char *p
, char *arg
)
273 while (*p
&& (*p
== ' ' || *p
== '\t' || *p
== '\n'))
275 while (*p
&& *p
!= ' ' && *p
!= '\t' && *p
!= '\n')
278 if (!*p
|| *p
== '\n')
283 /* Tests whether the selected file in the panel is of any of the types
284 specified in argument. */
285 static int test_type (WPanel
*panel
, char *arg
)
287 int result
= 0; /* False by default */
288 int st_mode
= panel
->dir
.list
[panel
->selected
].buf
.st_mode
;
290 for (;*arg
!= 0; arg
++){
292 case 'n': /* Not a directory */
293 result
|= !S_ISDIR (st_mode
);
295 case 'r': /* Regular file */
296 result
|= S_ISREG (st_mode
);
298 case 'd': /* Directory */
299 result
|= S_ISDIR (st_mode
);
302 result
|= S_ISLNK (st_mode
);
304 case 'c': /* Character special */
305 result
|= S_ISCHR (st_mode
);
307 case 'b': /* Block special */
308 result
|= S_ISBLK (st_mode
);
310 case 'f': /* Fifo (named pipe) */
311 result
|= S_ISFIFO (st_mode
);
313 case 's': /* Socket */
314 result
|= S_ISSOCK (st_mode
);
316 case 'x': /* Executable */
317 result
|= (st_mode
& 0111) ? 1 : 0;
320 result
|= panel
->marked
? 1 : 0;
330 /* Calculates the truth value of the next condition starting from
331 p. Returns the point after condition. */
332 static char *test_condition (char *p
, int *condition
)
337 /* Handle one condition */
338 for (;*p
!= '\n' && *p
!= '&' && *p
!= '|'; p
++){
339 if (*p
== ' ' || *p
== '\t')
344 if (get_other_type () == view_listing
)
353 p
= test_condition (p
, condition
);
354 *condition
= ! *condition
;
358 p
= extract_arg (p
, arg
);
359 *condition
= panel
&& regexp_match (arg
, panel
->dir
.list
[panel
->selected
].fname
, match_file
);
362 p
= extract_arg (p
, arg
);
363 *condition
= panel
&& regexp_match (arg
, panel
->cwd
, match_file
);
366 p
= extract_arg (p
, arg
);
367 *condition
= panel
&& test_type (panel
, arg
);
378 /* General purpose condition debug output handler */
379 void debug_out (char *start
, char *end
, int cond
)
381 static char msg
[256];
384 if (start
== NULL
&& end
== NULL
){
395 message (0, _(" Debug "), msg
);
399 /* Save debug info for later output */
402 /* Save the result of the condition */
404 strcat (msg
, _(" ERROR: "));
408 strcat (msg
, _(" True: "));
410 strcat (msg
, _(" False: "));
411 /* Copy condition statement */
414 /* Copy one character */
418 /* Copy many characters */
420 msg
[len
++] = *start
++;
428 /* Calculates the truth value of one lineful of conditions. Returns
429 the point just before the end of line. */
430 static char *test_line (char *p
, int *result
)
434 char *debug_start
, *debug_end
;
437 debug_out (NULL
, NULL
, 0);
438 /* Repeat till end of line */
439 while (*p
&& *p
!= '\n'){
440 while (*p
== ' ' || *p
== '\t')
442 if (!*p
|| *p
== '\n')
449 while (*p
== ' ' || *p
== '\t')
451 if (!*p
|| *p
== '\n')
453 condition
= 1; /* True by default */
456 p
= test_condition (p
, &condition
);
458 /* Add one debug statement */
459 debug_out (debug_start
, debug_end
, condition
);
467 case '&': /* Logical and */
468 *result
&= condition
;
470 case '|': /* Logical or */
471 *result
|= condition
;
477 /* Add one debug statement */
478 debug_out (&operator, NULL
, *result
);
480 } /* while (*p != '\n') */
481 /* Report debug message */
482 debug_out (NULL
, NULL
, 1);
484 if (!*p
|| *p
== '\n')
489 /* FIXME: recode this routine on version 3.0, it could be cleaner */
490 void execute_menu_command (char *s
)
495 int expand_prefix_found
= 0;
496 int parameter_found
= 0;
498 char prompt
[80] = "";
500 char *file_name
= tmpnam (0);
503 /* OS/2 and NT requires the command to end in .cmd */
504 file_name
= copy_strings (file_name
, ".cmd", NULL
);
506 if ((cmd_file_fd
= open (file_name
, O_RDWR
| O_CREAT
| O_TRUNC
| O_EXCL
, 0600)) == -1){
507 message (1, MSG_ERROR
, _(" Can't create temporary command file \n %s "),
508 unix_error_string (errno
));
511 cmd_file
= fdopen (cmd_file_fd
, "w");
512 commands
= strchr (s
, '\n');
520 for (col
= 0; *commands
; commands
++){
521 if (col
== 0 && (*commands
!= ' ' && *commands
!= '\t'))
524 while (*commands
== ' ' || *commands
== '\t')
527 if (*commands
== '\n')
529 if (parameter_found
){
530 if (*commands
== '}'){
534 parameter
= input_dialog (_(" Parameter "), prompt
, "");
535 if (!parameter
|| !*parameter
){
542 fputs (tmp
= name_quote (parameter
, 0), cmd_file
);
545 fputs (parameter
, cmd_file
);
548 int len
= strlen (prompt
);
550 if (len
+1 < sizeof (prompt
)){
551 prompt
[len
] = *commands
;
554 prompt
[sizeof (prompt
)-1] = 0;
556 } else if (expand_prefix_found
){
557 expand_prefix_found
= 0;
558 if (isdigit (*commands
)) {
559 do_quote
= atoi (commands
);
560 for ( ; isdigit (*commands
); commands
++)
563 if (*commands
== '{')
566 char *text
= expand_format (*commands
, do_quote
);
567 fputs (text
, cmd_file
);
571 if (*commands
== '%') {
572 do_quote
= 1; /* Default: Quote expanded macro */
573 expand_prefix_found
= 1;
575 fputc (*commands
, cmd_file
);
579 chmod (file_name
, S_IRWXU
);
585 ** Check owner of the menu file. Using menu file is allowed, if
586 ** owner of the menu is root or the actual user. In either case
587 ** file should not be group and word-writable.
589 ** Q. Should we apply this routine to system and home menu (and .ext files)?
592 menu_file_own(char* path
)
596 if (stat (path
, &st
) == 0
597 && (!st
.st_uid
|| (st
.st_uid
== geteuid ()))
598 && ((st
.st_mode
& (S_IWGRP
| S_IWOTH
)) == 0)
607 message (0, _(" Warning -- ignoring file "),
608 _("File %s is not owned by root or you or is world writable.\n"
609 "Using it may compromise your security"),
617 void user_menu_cmd (void)
620 int col
, i
, accept_entry
= 1;
621 int selected
, old_patterns
;
624 if (!vfs_current_is_local ()){
625 message (1, _(" Oops... "),
626 _(" I can't run programs while logged on a non local directory "));
630 menu
= strdup (MC_LOCAL_MENU
);
631 if (!exist_file (menu
) || !menu_file_own (menu
)){
633 menu
= concat_dir_and_file (home_dir
, MC_HOME_MENU
);
634 if (!exist_file (menu
)){
636 menu
= concat_dir_and_file (mc_home
, MC_GLOBAL_MENU
);
640 if ((data
= load_file (menu
)) == NULL
){
641 message (1, MSG_ERROR
, _(" Can't open file %s \n %s "),
642 menu
, unix_error_string (errno
));
649 for (i
= 0; i
< MAX_ENTRIES
; i
++)
653 /* Parse the menu file */
654 old_patterns
= easy_patterns
;
655 p
= check_patterns (data
);
656 for (menu_lines
= col
= 0; *p
; p
++){
657 if (col
== 0 && !entries
[menu_lines
]){
659 /* A commented menu entry */
661 } else if (*p
== '+'){
663 /* Combined adding and default */
666 p
= test_line (q
, &accept_entry
);
667 if (selected
== 0 && accept_entry
)
668 selected
= menu_lines
;
670 /* A condition for adding the entry */
671 p
= test_line (p
, &accept_entry
);
673 } else if (*p
== '='){
676 /* Combined adding and default */
677 p
= test_line (q
, &accept_entry
);
678 if (selected
== 0 && accept_entry
)
679 selected
= menu_lines
;
681 /* A condition for making the entry default */
683 p
= test_line (p
, &i
);
684 if (selected
== 0 && i
)
685 selected
= menu_lines
;
688 else if (*p
> ' ' && *p
< 127){
689 /* A menu entry title line */
691 entries
[menu_lines
] = p
;
696 if (menu_lines
== MAX_ENTRIES
)
702 if (entries
[menu_lines
]){
706 max_cols
= max (max_cols
, col
);
710 max_cols
= min (max (max_cols
, col
), MAX_ENTRY_LEN
);
713 listbox
= create_listbox_window (max_cols
+2, menu_lines
, _(" User menu "),
716 /* insert all the items found */
717 for (i
= 0; i
< menu_lines
; i
++)
718 LISTBOX_APPEND_TEXT (listbox
, entries
[i
][0],
719 extract_line (entries
[i
],
720 entries
[i
]+MAX_ENTRY_LEN
),
723 /* Select the default entry */
724 listbox_select_by_number (listbox
->list
, selected
);
726 selected
= run_listbox (listbox
);
728 execute_menu_command (entries
[selected
]);
730 easy_patterns
= old_patterns
;