mcview: allow set start and end of search result...
[midnight-commander.git] / src / filemanager / usermenu.c
blobe63564fe14e84515b0d7e1498452149551a9a120
1 /*
2 User Menu implementation
4 Copyright (C) 1994-2015
5 Free Software Foundation, Inc.
7 Written by:
8 Slava Zanko <slavazanko@gmail.com>, 2013
9 Andrew Borodin <aborodin@vmail.ru>, 2013
11 This file is part of the Midnight Commander.
13 The Midnight Commander is free software: you can redistribute it
14 and/or modify it under the terms of the GNU General Public License as
15 published by the Free Software Foundation, either version 3 of the License,
16 or (at your option) any later version.
18 The Midnight Commander is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
23 You should have received a copy of the GNU General Public License
24 along with this program. If not, see <http://www.gnu.org/licenses/>.
27 /** \file usermenu.c
28 * \brief Source: user menu implementation
31 #include <config.h>
33 #include <ctype.h>
34 #include <errno.h>
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <string.h>
39 #include "lib/global.h"
40 #include "lib/tty/tty.h"
41 #include "lib/skin.h"
42 #include "lib/search.h"
43 #include "lib/vfs/vfs.h"
44 #include "lib/strutil.h"
45 #include "lib/util.h"
46 #include "lib/widget.h"
48 #include "src/editor/edit.h" /* WEdit, BLOCK_FILE */
49 #include "src/viewer/mcviewer.h" /* for default_* externs */
51 #include "src/execute.h"
52 #include "src/setup.h"
53 #include "src/history.h"
55 #include "dir.h"
56 #include "midnight.h"
57 #include "layout.h"
59 #include "usermenu.h"
61 /*** global variables ****************************************************************************/
63 /*** file scope macro definitions ****************************************************************/
65 #define MAX_ENTRIES 16
66 #define MAX_ENTRY_LEN 60
68 /*** file scope type declarations ****************************************************************/
70 /*** file scope variables ************************************************************************/
72 static int debug_flag = 0;
73 static int debug_error = 0;
74 static char *menu = NULL;
76 /*** file scope functions ************************************************************************/
77 /* --------------------------------------------------------------------------------------------- */
79 /** strip file's extension */
80 static char *
81 strip_ext (char *ss)
83 char *s = ss;
84 char *e = NULL;
85 while (*s)
87 if (*s == '.')
88 e = s;
89 if (IS_PATH_SEP (*s) && e != NULL)
90 e = NULL; /* '.' in *directory* name */
91 s++;
93 if (e)
94 *e = 0;
95 return ss;
98 /* --------------------------------------------------------------------------------------------- */
99 /**
100 * Check for the "shell_patterns" directive. If it's found and valid,
101 * interpret it and move the pointer past the directive. Return the
102 * current pointer.
105 static char *
106 check_patterns (char *p)
108 static const char def_name[] = "shell_patterns=";
109 char *p0 = p;
111 if (strncmp (p, def_name, sizeof (def_name) - 1) != 0)
112 return p0;
114 p += sizeof (def_name) - 1;
115 if (*p == '1')
116 easy_patterns = 1;
117 else if (*p == '0')
118 easy_patterns = 0;
119 else
120 return p0;
122 /* Skip spaces */
123 p++;
124 while (*p == '\n' || *p == '\t' || *p == ' ')
125 p++;
126 return p;
129 /* --------------------------------------------------------------------------------------------- */
130 /** Copies a whitespace separated argument from p to arg. Returns the
131 point after argument. */
133 static char *
134 extract_arg (char *p, char *arg, int size)
136 char *np;
138 while (*p && (*p == ' ' || *p == '\t' || *p == '\n'))
139 p++;
140 /* support quote space .mnu */
141 while (*p && (*p != ' ' || *(p - 1) == '\\') && *p != '\t' && *p != '\n')
143 np = str_get_next_char (p);
144 if (np - p >= size)
145 break;
146 memcpy (arg, p, np - p);
147 arg += np - p;
148 size -= np - p;
149 p = np;
151 *arg = 0;
152 if (!*p || *p == '\n')
153 str_prev_char (&p);
154 return p;
157 /* --------------------------------------------------------------------------------------------- */
158 /* Tests whether the selected file in the panel is of any of the types
159 specified in argument. */
161 static int
162 test_type (WPanel * panel, char *arg)
164 int result = 0; /* False by default */
165 int st_mode = panel->dir.list[panel->selected].st.st_mode;
167 for (; *arg != 0; arg++)
169 switch (*arg)
171 case 'n': /* Not a directory */
172 result |= !S_ISDIR (st_mode);
173 break;
174 case 'r': /* Regular file */
175 result |= S_ISREG (st_mode);
176 break;
177 case 'd': /* Directory */
178 result |= S_ISDIR (st_mode);
179 break;
180 case 'l': /* Link */
181 result |= S_ISLNK (st_mode);
182 break;
183 case 'c': /* Character special */
184 result |= S_ISCHR (st_mode);
185 break;
186 case 'b': /* Block special */
187 result |= S_ISBLK (st_mode);
188 break;
189 case 'f': /* Fifo (named pipe) */
190 result |= S_ISFIFO (st_mode);
191 break;
192 case 's': /* Socket */
193 result |= S_ISSOCK (st_mode);
194 break;
195 case 'x': /* Executable */
196 result |= (st_mode & 0111) ? 1 : 0;
197 break;
198 case 't':
199 result |= panel->marked ? 1 : 0;
200 break;
201 default:
202 debug_error = 1;
203 break;
206 return result;
209 /* --------------------------------------------------------------------------------------------- */
210 /** Calculates the truth value of the next condition starting from
211 p. Returns the point after condition. */
213 static char *
214 test_condition (WEdit * edit_widget, char *p, int *condition)
216 char arg[256];
217 const mc_search_type_t search_type = easy_patterns ? MC_SEARCH_T_GLOB : MC_SEARCH_T_REGEX;
219 /* Handle one condition */
220 for (; *p != '\n' && *p != '&' && *p != '|'; p++)
222 WPanel *panel = NULL;
224 /* support quote space .mnu */
225 if ((*p == ' ' && *(p - 1) != '\\') || *p == '\t')
226 continue;
227 if (*p >= 'a')
228 panel = current_panel;
229 else if (get_other_type () == view_listing)
230 panel = other_panel;
232 *p |= 0x20;
234 switch (*p++)
236 case '!':
237 p = test_condition (edit_widget, p, condition);
238 *condition = !*condition;
239 str_prev_char (&p);
240 break;
241 case 'f': /* file name pattern */
242 p = extract_arg (p, arg, sizeof (arg));
243 #ifdef USE_INTERNAL_EDIT
244 if (edit_widget != NULL)
246 char *edit_filename;
248 edit_filename = edit_get_file_name (edit_widget);
249 *condition = mc_search (arg, DEFAULT_CHARSET, edit_filename, search_type) ? 1 : 0;
250 g_free (edit_filename);
252 else
253 #endif
254 *condition = panel != NULL &&
255 mc_search (arg, DEFAULT_CHARSET, panel->dir.list[panel->selected].fname,
256 search_type) ? 1 : 0;
257 break;
258 case 'y': /* syntax pattern */
259 #ifdef USE_INTERNAL_EDIT
260 if (edit_widget != NULL)
262 const char *syntax_type = edit_get_syntax_type (edit_widget);
263 if (syntax_type != NULL)
265 p = extract_arg (p, arg, sizeof (arg));
266 *condition =
267 mc_search (arg, DEFAULT_CHARSET, syntax_type, MC_SEARCH_T_NORMAL) ? 1 : 0;
270 #endif
271 break;
272 case 'd':
273 p = extract_arg (p, arg, sizeof (arg));
274 *condition = panel != NULL
275 && mc_search (arg, DEFAULT_CHARSET, vfs_path_as_str (panel->cwd_vpath),
276 search_type) ? 1 : 0;
277 break;
278 case 't':
279 p = extract_arg (p, arg, sizeof (arg));
280 *condition = panel != NULL && test_type (panel, arg) ? 1 : 0;
281 break;
282 case 'x': /* executable */
284 struct stat status;
286 p = extract_arg (p, arg, sizeof (arg));
287 if (stat (arg, &status) == 0)
288 *condition = is_exe (status.st_mode) ? 1 : 0;
289 else
290 *condition = 0;
291 break;
293 default:
294 debug_error = 1;
295 break;
296 } /* switch */
298 } /* while */
299 return p;
302 /* --------------------------------------------------------------------------------------------- */
303 /** General purpose condition debug output handler */
305 static void
306 debug_out (char *start, char *end, int cond)
308 static char *msg;
310 if (start == NULL && end == NULL)
312 /* Show output */
313 if (debug_flag && msg)
315 size_t len;
317 len = strlen (msg);
318 if (len != 0)
319 msg[len - 1] = 0;
320 message (D_NORMAL, _("Debug"), "%s", msg);
323 debug_flag = 0;
324 MC_PTR_FREE (msg);
326 else
328 const char *type;
329 char *p;
331 /* Save debug info for later output */
332 if (!debug_flag)
333 return;
334 /* Save the result of the condition */
335 if (debug_error)
337 type = _("ERROR:");
338 debug_error = 0;
340 else if (cond)
341 type = _("True:");
342 else
343 type = _("False:");
344 /* This is for debugging, don't need to be super efficient. */
345 if (end == NULL)
346 p = g_strdup_printf ("%s %s %c \n", msg ? msg : "", type, *start);
347 else
348 p = g_strdup_printf ("%s %s %.*s \n", msg ? msg : "", type, (int) (end - start), start);
349 g_free (msg);
350 msg = p;
354 /* --------------------------------------------------------------------------------------------- */
355 /** Calculates the truth value of one lineful of conditions. Returns
356 the point just before the end of line. */
358 static char *
359 test_line (WEdit * edit_widget, char *p, int *result)
361 int condition;
362 char operator;
364 /* Repeat till end of line */
365 while (*p && *p != '\n')
367 char *debug_start, *debug_end;
369 /* support quote space .mnu */
370 while ((*p == ' ' && *(p - 1) != '\\') || *p == '\t')
371 p++;
372 if (!*p || *p == '\n')
373 break;
374 operator = *p++;
375 if (*p == '?')
377 debug_flag = 1;
378 p++;
380 /* support quote space .mnu */
381 while ((*p == ' ' && *(p - 1) != '\\') || *p == '\t')
382 p++;
383 if (!*p || *p == '\n')
384 break;
385 condition = 1; /* True by default */
387 debug_start = p;
388 p = test_condition (edit_widget, p, &condition);
389 debug_end = p;
390 /* Add one debug statement */
391 debug_out (debug_start, debug_end, condition);
393 switch (operator)
395 case '+':
396 case '=':
397 /* Assignment */
398 *result = condition;
399 break;
400 case '&': /* Logical and */
401 *result &= condition;
402 break;
403 case '|': /* Logical or */
404 *result |= condition;
405 break;
406 default:
407 debug_error = 1;
408 break;
409 } /* switch */
410 /* Add one debug statement */
411 debug_out (&operator, NULL, *result);
413 } /* while (*p != '\n') */
414 /* Report debug message */
415 debug_out (NULL, NULL, 1);
417 if (!*p || *p == '\n')
418 str_prev_char (&p);
419 return p;
422 /* --------------------------------------------------------------------------------------------- */
423 /** FIXME: recode this routine on version 3.0, it could be cleaner */
425 static void
426 execute_menu_command (WEdit * edit_widget, const char *commands, gboolean show_prompt)
428 FILE *cmd_file;
429 int cmd_file_fd;
430 int expand_prefix_found = 0;
431 char *parameter = 0;
432 gboolean do_quote = FALSE;
433 char lc_prompt[80];
434 int col;
435 vfs_path_t *file_name_vpath;
436 int run_view = 0;
438 /* Skip menu entry title line */
439 commands = strchr (commands, '\n');
440 if (!commands)
442 return;
445 cmd_file_fd = mc_mkstemps (&file_name_vpath, "mcusr", SCRIPT_SUFFIX);
447 if (cmd_file_fd == -1)
449 message (D_ERROR, MSG_ERROR, _("Cannot create temporary command file\n%s"),
450 unix_error_string (errno));
451 vfs_path_free (file_name_vpath);
452 return;
454 cmd_file = fdopen (cmd_file_fd, "w");
455 fputs ("#! /bin/sh\n", cmd_file);
456 commands++;
458 for (col = 0; *commands; commands++)
460 if (col == 0)
462 if (*commands != ' ' && *commands != '\t')
463 break;
464 while (*commands == ' ' || *commands == '\t')
465 commands++;
466 if (*commands == 0)
467 break;
469 col++;
470 if (*commands == '\n')
471 col = 0;
472 if (parameter)
474 if (*commands == '}')
476 *parameter = 0;
477 parameter =
478 input_dialog (_("Parameter"), lc_prompt, MC_HISTORY_FM_MENU_EXEC_PARAM, "",
479 INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_CD |
480 INPUT_COMPLETE_HOSTNAMES | INPUT_COMPLETE_VARIABLES |
481 INPUT_COMPLETE_USERNAMES);
482 if (!parameter || !*parameter)
484 /* User canceled */
485 fclose (cmd_file);
486 mc_unlink (file_name_vpath);
487 vfs_path_free (file_name_vpath);
488 return;
490 if (do_quote)
492 char *tmp;
494 tmp = name_quote (parameter, FALSE);
495 fputs (tmp, cmd_file);
496 g_free (tmp);
498 else
499 fputs (parameter, cmd_file);
500 g_free (parameter);
501 parameter = 0;
503 else
505 if (parameter < &lc_prompt[sizeof (lc_prompt) - 1])
507 *parameter++ = *commands;
511 else if (expand_prefix_found)
513 expand_prefix_found = 0;
514 if (g_ascii_isdigit ((gchar) * commands))
516 do_quote = (atoi (commands) != 0);
517 while (g_ascii_isdigit ((gchar) * commands))
518 commands++;
520 if (*commands == '{')
521 parameter = lc_prompt;
522 else
524 char *text = expand_format (edit_widget, *commands, do_quote);
525 fputs (text, cmd_file);
526 g_free (text);
529 else
531 if (*commands == '%')
533 int i = check_format_view (commands + 1);
534 if (i)
536 commands += i;
537 run_view = 1;
539 else
541 do_quote = TRUE; /* Default: Quote expanded macro */
542 expand_prefix_found = 1;
545 else
546 fputc (*commands, cmd_file);
549 fclose (cmd_file);
550 mc_chmod (file_name_vpath, S_IRWXU);
551 if (run_view)
553 mcview_viewer (vfs_path_as_str (file_name_vpath), NULL, 0, 0, 0);
554 dialog_switch_process_pending ();
556 else
558 /* execute the command indirectly to allow execution even
559 * on no-exec filesystems. */
560 char *cmd;
562 cmd = g_strconcat ("/bin/sh ", vfs_path_as_str (file_name_vpath), (char *) NULL);
563 if (!show_prompt)
565 if (system (cmd) == -1)
566 message (D_ERROR, MSG_ERROR, "%s", _("Error calling program"));
568 else
570 shell_execute (cmd, EXECUTE_HIDE);
572 g_free (cmd);
574 mc_unlink (file_name_vpath);
575 vfs_path_free (file_name_vpath);
578 /* --------------------------------------------------------------------------------------------- */
580 ** Check owner of the menu file. Using menu file is allowed, if
581 ** owner of the menu is root or the actual user. In either case
582 ** file should not be group and word-writable.
584 ** Q. Should we apply this routine to system and home menu (and .ext files)?
587 static int
588 menu_file_own (char *path)
590 struct stat st;
592 if (stat (path, &st) == 0
593 && (!st.st_uid || (st.st_uid == geteuid ())) && ((st.st_mode & (S_IWGRP | S_IWOTH)) == 0))
595 return 1;
597 if (verbose)
599 message (D_NORMAL, _("Warning -- ignoring file"),
600 _("File %s is not owned by root or you or is world writable.\n"
601 "Using it may compromise your security"), path);
603 return 0;
606 /* --------------------------------------------------------------------------------------------- */
607 /*** public functions ****************************************************************************/
608 /* --------------------------------------------------------------------------------------------- */
610 /* Formats defined:
611 %% The % character
612 %f The current file (if non-local vfs, file will be copied locally and
613 %f will be full path to it).
614 %p The current file
615 %d The current working directory
616 %s "Selected files"; the tagged files if any, otherwise the current file
617 %t Tagged files
618 %u Tagged files (and they are untagged on return from expand_format)
619 %view Runs the commands and pipes standard output to the view command.
620 If %view is immediately followed by '{', recognize keywords
621 ascii, hex, nroff and unform
623 If the format letter is in uppercase, it refers to the other panel.
625 With a number followed the % character you can turn quoting on (default)
626 and off. For example:
627 %f quote expanded macro
628 %1f ditto
629 %0f don't quote expanded macro
631 expand_format returns a memory block that must be free()d.
634 /* Returns how many characters we should advance if %view was found */
636 check_format_view (const char *p)
638 const char *q = p;
639 if (!strncmp (p, "view", 4))
641 q += 4;
642 if (*q == '{')
644 for (q++; *q && *q != '}'; q++)
646 if (!strncmp (q, DEFAULT_CHARSET, 5))
648 mcview_default_hex_mode = 0;
649 q += 4;
651 else if (!strncmp (q, "hex", 3))
653 mcview_default_hex_mode = 1;
654 q += 2;
656 else if (!strncmp (q, "nroff", 5))
658 mcview_default_nroff_flag = 1;
659 q += 4;
661 else if (!strncmp (q, "unform", 6))
663 mcview_default_nroff_flag = 0;
664 q += 5;
667 if (*q == '}')
668 q++;
670 return q - p;
672 return 0;
675 /* --------------------------------------------------------------------------------------------- */
678 check_format_cd (const char *p)
680 return (strncmp (p, "cd", 2)) ? 0 : 3;
683 /* --------------------------------------------------------------------------------------------- */
684 /* Check if p has a "^var\{var-name\}" */
685 /* Returns the number of skipped characters (zero on not found) */
686 /* V will be set to the expanded variable name */
689 check_format_var (const char *p, char **v)
691 const char *q = p;
692 char *var_name;
694 *v = NULL;
695 if (!strncmp (p, "var{", 4))
697 const char *dots = NULL;
698 const char *value;
700 for (q += 4; *q && *q != '}'; q++)
702 if (*q == ':')
703 dots = q + 1;
705 if (!*q)
706 return 0;
708 if (!dots || dots == q + 5)
710 message (D_ERROR,
711 _("Format error on file Extensions File"),
712 !dots ? _("The %%var macro has no default")
713 : _("The %%var macro has no variable"));
714 return 0;
717 /* Copy the variable name */
718 var_name = g_strndup (p + 4, dots - 2 - (p + 3));
720 value = getenv (var_name);
721 g_free (var_name);
722 if (value)
724 *v = g_strdup (value);
725 return q - p;
727 var_name = g_strndup (dots, q - dots);
728 *v = var_name;
729 return q - p;
731 return 0;
734 /* --------------------------------------------------------------------------------------------- */
736 char *
737 expand_format (struct WEdit *edit_widget, char c, gboolean do_quote)
739 WPanel *panel = NULL;
740 char *(*quote_func) (const char *, gboolean);
741 char *fname = NULL;
742 char *result;
743 char c_lc;
745 #ifndef USE_INTERNAL_EDIT
746 (void) edit_widget;
747 #endif
749 if (c == '%')
750 return g_strdup ("%");
752 switch (mc_global.mc_run_mode)
754 case MC_RUN_FULL:
755 if (g_ascii_islower ((gchar) c))
756 panel = current_panel;
757 else
759 if (get_other_type () != view_listing)
760 return g_strdup ("");
761 panel = other_panel;
763 fname = g_strdup (panel->dir.list[panel->selected].fname);
764 break;
766 #ifdef USE_INTERNAL_EDIT
767 case MC_RUN_EDITOR:
768 fname = edit_get_file_name (edit_widget);
769 break;
770 #endif
772 default:
773 /* other modes don't use formats */
774 return g_strdup ("");
777 if (do_quote)
778 quote_func = name_quote;
779 else
780 quote_func = fake_name_quote;
782 c_lc = g_ascii_tolower ((gchar) c);
784 switch (c_lc)
786 case 'f':
787 case 'p':
788 result = quote_func (fname, FALSE);
789 goto ret;
790 case 'x':
791 result = quote_func (extension (fname), FALSE);
792 goto ret;
793 case 'd':
795 char *cwd;
796 char *qstr;
798 if (panel)
799 cwd = g_strdup (vfs_path_as_str (panel->cwd_vpath));
800 else
801 cwd = vfs_get_current_dir ();
803 qstr = quote_func (cwd, FALSE);
805 g_free (cwd);
807 result = qstr;
808 goto ret;
810 case 'i': /* indent equal number cursor position in line */
811 #ifdef USE_INTERNAL_EDIT
812 if (edit_widget)
814 result = g_strnfill (edit_get_curs_col (edit_widget), ' ');
815 goto ret;
817 #endif
818 break;
819 case 'y': /* syntax type */
820 #ifdef USE_INTERNAL_EDIT
821 if (edit_widget)
823 const char *syntax_type = edit_get_syntax_type (edit_widget);
824 if (syntax_type != NULL)
826 result = g_strdup (syntax_type);
827 goto ret;
830 #endif
831 break;
832 case 'k': /* block file name */
833 case 'b': /* block file name / strip extension */
835 #ifdef USE_INTERNAL_EDIT
836 if (edit_widget)
838 char *file;
840 file = mc_config_get_full_path (EDIT_BLOCK_FILE);
841 result = quote_func (file, FALSE);
842 g_free (file);
843 goto ret;
845 #endif
846 if (c_lc == 'b')
848 result = strip_ext (quote_func (fname, FALSE));
849 goto ret;
851 break;
853 case 'n': /* strip extension in editor */
854 #ifdef USE_INTERNAL_EDIT
855 if (edit_widget)
857 result = strip_ext (quote_func (fname, FALSE));
858 goto ret;
860 #endif
861 break;
862 case 'm': /* menu file name */
863 if (menu)
865 result = quote_func (menu, FALSE);
866 goto ret;
868 break;
869 case 's':
870 if (!panel || !panel->marked)
872 result = quote_func (fname, FALSE);
873 goto ret;
876 /* Fall through */
878 case 't':
879 case 'u':
881 GString *block;
882 int i;
884 if (panel == NULL)
886 result = g_strdup ("");
887 goto ret;
890 block = g_string_sized_new (16);
892 for (i = 0; i < panel->dir.len; i++)
893 if (panel->dir.list[i].f.marked)
895 char *tmp;
897 tmp = quote_func (panel->dir.list[i].fname, FALSE);
898 g_string_append (block, tmp);
899 g_string_append_c (block, ' ');
900 g_free (tmp);
902 if (c_lc == 'u')
903 do_file_mark (panel, i, 0);
905 result = g_string_free (block, FALSE);
906 goto ret;
907 } /* sub case block */
908 default:
909 break;
910 } /* switch */
912 result = g_strdup ("% ");
913 result[1] = c;
914 ret:
915 g_free (fname);
916 return result;
919 /* --------------------------------------------------------------------------------------------- */
921 * If edit_widget is NULL then we are called from the mc menu,
922 * otherwise we are called from the mcedit menu.
925 gboolean
926 user_menu_cmd (struct WEdit * edit_widget, const char *menu_file, int selected_entry)
928 char *p;
929 char *data, **entries;
930 int max_cols, menu_lines, menu_limit;
931 int col, i, accept_entry = 1;
932 int selected, old_patterns;
933 gboolean res = FALSE;
934 gboolean interactive = TRUE;
936 if (!vfs_current_is_local ())
938 message (D_ERROR, MSG_ERROR, "%s", _("Cannot execute commands on non-local filesystems"));
939 return FALSE;
941 if (menu_file != NULL)
942 menu = g_strdup (menu_file);
943 else
944 menu = g_strdup (edit_widget ? EDIT_LOCAL_MENU : MC_LOCAL_MENU);
945 if (!exist_file (menu) || !menu_file_own (menu))
947 if (menu_file != NULL)
949 message (D_ERROR, MSG_ERROR, _("Cannot open file %s\n%s"), menu,
950 unix_error_string (errno));
951 MC_PTR_FREE (menu);
952 return FALSE;
955 g_free (menu);
956 if (edit_widget)
957 menu = mc_config_get_full_path (EDIT_HOME_MENU);
958 else
959 menu = mc_config_get_full_path (MC_USERMENU_FILE);
962 if (!exist_file (menu))
964 g_free (menu);
965 menu =
966 mc_build_filename (mc_config_get_home_dir (),
967 edit_widget ? EDIT_GLOBAL_MENU : MC_GLOBAL_MENU, NULL);
968 if (!exist_file (menu))
970 g_free (menu);
971 menu =
972 mc_build_filename (mc_global.sysconfig_dir,
973 edit_widget ? EDIT_GLOBAL_MENU : MC_GLOBAL_MENU, NULL);
974 if (!exist_file (menu))
976 g_free (menu);
977 menu = mc_build_filename
978 (mc_global.share_data_dir, edit_widget ? EDIT_GLOBAL_MENU : MC_GLOBAL_MENU,
979 NULL);
985 if (!g_file_get_contents (menu, &data, NULL, NULL))
987 message (D_ERROR, MSG_ERROR, _("Cannot open file %s\n%s"), menu, unix_error_string (errno));
988 MC_PTR_FREE (menu);
989 return FALSE;
992 max_cols = 0;
993 selected = 0;
994 menu_limit = 0;
995 entries = 0;
997 /* Parse the menu file */
998 old_patterns = easy_patterns;
999 p = check_patterns (data);
1000 for (menu_lines = col = 0; *p; str_next_char (&p))
1002 if (menu_lines >= menu_limit)
1004 char **new_entries;
1006 menu_limit += MAX_ENTRIES;
1007 new_entries = g_try_realloc (entries, sizeof (new_entries[0]) * menu_limit);
1009 if (new_entries == NULL)
1010 break;
1012 entries = new_entries;
1013 new_entries += menu_limit;
1014 while (--new_entries >= &entries[menu_lines])
1015 *new_entries = NULL;
1017 if (col == 0 && !entries[menu_lines])
1019 if (*p == '#')
1021 /* show prompt if first line of external script is #interactive */
1022 if (selected_entry >= 0 && strncmp (p, "#silent", 7) == 0)
1023 interactive = FALSE;
1024 /* A commented menu entry */
1025 accept_entry = 1;
1027 else if (*p == '+')
1029 if (*(p + 1) == '=')
1031 /* Combined adding and default */
1032 p = test_line (edit_widget, p + 1, &accept_entry);
1033 if (selected == 0 && accept_entry)
1034 selected = menu_lines;
1036 else
1038 /* A condition for adding the entry */
1039 p = test_line (edit_widget, p, &accept_entry);
1042 else if (*p == '=')
1044 if (*(p + 1) == '+')
1046 /* Combined adding and default */
1047 p = test_line (edit_widget, p + 1, &accept_entry);
1048 if (selected == 0 && accept_entry)
1049 selected = menu_lines;
1051 else
1053 /* A condition for making the entry default */
1054 i = 1;
1055 p = test_line (edit_widget, p, &i);
1056 if (selected == 0 && i)
1057 selected = menu_lines;
1060 else if (*p != ' ' && *p != '\t' && str_isprint (p))
1062 /* A menu entry title line */
1063 if (accept_entry)
1064 entries[menu_lines] = p;
1065 else
1066 accept_entry = 1;
1069 if (*p == '\n')
1071 if (entries[menu_lines])
1073 menu_lines++;
1074 accept_entry = 1;
1076 max_cols = max (max_cols, col);
1077 col = 0;
1079 else
1081 if (*p == '\t')
1082 *p = ' ';
1083 col++;
1087 if (menu_lines == 0)
1089 message (D_ERROR, MSG_ERROR, _("No suitable entries found in %s"), menu);
1090 res = FALSE;
1092 else
1094 if (selected_entry >= 0)
1095 selected = selected_entry;
1096 else
1098 Listbox *listbox;
1100 max_cols = min (max (max_cols, col), MAX_ENTRY_LEN);
1102 /* Create listbox */
1103 listbox = create_listbox_window (menu_lines, max_cols + 2, _("User menu"),
1104 "[Edit Menu File]");
1105 /* insert all the items found */
1106 for (i = 0; i < menu_lines; i++)
1108 p = entries[i];
1109 LISTBOX_APPEND_TEXT (listbox, (unsigned char) p[0],
1110 extract_line (p, p + MAX_ENTRY_LEN), p, FALSE);
1112 /* Select the default entry */
1113 listbox_select_entry (listbox->list, selected);
1115 selected = run_listbox (listbox);
1117 if (selected >= 0)
1119 execute_menu_command (edit_widget, entries[selected], interactive);
1120 res = TRUE;
1123 do_refresh ();
1126 easy_patterns = old_patterns;
1127 MC_PTR_FREE (menu);
1128 g_free (entries);
1129 g_free (data);
1130 return res;
1133 /* --------------------------------------------------------------------------------------------- */