Concretize the usage of autocompliting in different input fields.
[midnight-commander.git] / src / filemanager / usermenu.c
blobea8ede44e3ae800b757e6c774838225a656687d2
1 /*
2 User Menu implementation
4 Copyright (C) 1994, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
5 2006, 2007, 2011
6 The Free Software Foundation, Inc.
8 This file is part of the Midnight Commander.
10 The Midnight Commander is free software: you can redistribute it
11 and/or modify it under the terms of the GNU General Public License as
12 published by the Free Software Foundation, either version 3 of the License,
13 or (at your option) any later version.
15 The Midnight Commander is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 /** \file usermenu.c
25 * \brief Source: user menu implementation
28 #include <config.h>
30 #include <ctype.h>
31 #include <errno.h>
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <string.h>
36 #include "lib/global.h"
37 #include "lib/tty/tty.h"
38 #include "lib/skin.h"
39 #include "lib/search.h"
40 #include "lib/vfs/vfs.h"
41 #include "lib/strutil.h"
42 #include "lib/util.h"
43 #include "lib/widget.h"
45 #include "src/editor/edit.h" /* WEdit, BLOCK_FILE */
46 #include "src/viewer/mcviewer.h" /* for default_* externs */
48 #include "src/execute.h"
49 #include "src/setup.h"
50 #include "src/history.h"
52 #include "dir.h"
53 #include "midnight.h"
54 #include "layout.h"
56 #include "usermenu.h"
58 /*** global variables ****************************************************************************/
60 /*** file scope macro definitions ****************************************************************/
62 #define MAX_ENTRIES 16
63 #define MAX_ENTRY_LEN 60
65 /*** file scope type declarations ****************************************************************/
67 /*** file scope variables ************************************************************************/
69 static int debug_flag = 0;
70 static int debug_error = 0;
71 static char *menu = NULL;
73 /*** file scope functions ************************************************************************/
74 /* --------------------------------------------------------------------------------------------- */
76 /** strip file's extension */
77 static char *
78 strip_ext (char *ss)
80 register char *s = ss;
81 char *e = NULL;
82 while (*s)
84 if (*s == '.')
85 e = s;
86 if (*s == PATH_SEP && e)
87 e = NULL; /* '.' in *directory* name */
88 s++;
90 if (e)
91 *e = 0;
92 return ss;
95 /* --------------------------------------------------------------------------------------------- */
96 /**
97 * Check for the "shell_patterns" directive. If it's found and valid,
98 * interpret it and move the pointer past the directive. Return the
99 * current pointer.
102 static char *
103 check_patterns (char *p)
105 static const char def_name[] = "shell_patterns=";
106 char *p0 = p;
108 if (strncmp (p, def_name, sizeof (def_name) - 1) != 0)
109 return p0;
111 p += sizeof (def_name) - 1;
112 if (*p == '1')
113 easy_patterns = 1;
114 else if (*p == '0')
115 easy_patterns = 0;
116 else
117 return p0;
119 /* Skip spaces */
120 p++;
121 while (*p == '\n' || *p == '\t' || *p == ' ')
122 p++;
123 return p;
126 /* --------------------------------------------------------------------------------------------- */
127 /** Copies a whitespace separated argument from p to arg. Returns the
128 point after argument. */
130 static char *
131 extract_arg (char *p, char *arg, int size)
133 char *np;
135 while (*p && (*p == ' ' || *p == '\t' || *p == '\n'))
136 p++;
137 /* support quote space .mnu */
138 while (*p && (*p != ' ' || *(p - 1) == '\\') && *p != '\t' && *p != '\n')
140 np = str_get_next_char (p);
141 if (np - p >= size)
142 break;
143 memcpy (arg, p, np - p);
144 arg += np - p;
145 size -= np - p;
146 p = np;
148 *arg = 0;
149 if (!*p || *p == '\n')
150 str_prev_char (&p);
151 return p;
154 /* --------------------------------------------------------------------------------------------- */
155 /* Tests whether the selected file in the panel is of any of the types
156 specified in argument. */
158 static int
159 test_type (WPanel * panel, char *arg)
161 int result = 0; /* False by default */
162 int st_mode = panel->dir.list[panel->selected].st.st_mode;
164 for (; *arg != 0; arg++)
166 switch (*arg)
168 case 'n': /* Not a directory */
169 result |= !S_ISDIR (st_mode);
170 break;
171 case 'r': /* Regular file */
172 result |= S_ISREG (st_mode);
173 break;
174 case 'd': /* Directory */
175 result |= S_ISDIR (st_mode);
176 break;
177 case 'l': /* Link */
178 result |= S_ISLNK (st_mode);
179 break;
180 case 'c': /* Character special */
181 result |= S_ISCHR (st_mode);
182 break;
183 case 'b': /* Block special */
184 result |= S_ISBLK (st_mode);
185 break;
186 case 'f': /* Fifo (named pipe) */
187 result |= S_ISFIFO (st_mode);
188 break;
189 case 's': /* Socket */
190 result |= S_ISSOCK (st_mode);
191 break;
192 case 'x': /* Executable */
193 result |= (st_mode & 0111) ? 1 : 0;
194 break;
195 case 't':
196 result |= panel->marked ? 1 : 0;
197 break;
198 default:
199 debug_error = 1;
200 break;
203 return result;
206 /* --------------------------------------------------------------------------------------------- */
207 /** Calculates the truth value of the next condition starting from
208 p. Returns the point after condition. */
210 static char *
211 test_condition (WEdit * edit_widget, char *p, int *condition)
213 char arg[256];
214 const mc_search_type_t search_type = easy_patterns ? MC_SEARCH_T_GLOB : MC_SEARCH_T_REGEX;
216 /* Handle one condition */
217 for (; *p != '\n' && *p != '&' && *p != '|'; p++)
219 WPanel *panel = NULL;
221 /* support quote space .mnu */
222 if ((*p == ' ' && *(p - 1) != '\\') || *p == '\t')
223 continue;
224 if (*p >= 'a')
225 panel = current_panel;
226 else if (get_other_type () == view_listing)
227 panel = other_panel;
229 *p |= 0x20;
231 switch (*p++)
233 case '!':
234 p = test_condition (edit_widget, p, condition);
235 *condition = !*condition;
236 str_prev_char (&p);
237 break;
238 case 'f': /* file name pattern */
239 p = extract_arg (p, arg, sizeof (arg));
240 #ifdef USE_INTERNAL_EDIT
241 if (edit_widget != NULL)
243 char *edit_filename;
245 edit_filename = edit_get_file_name (edit_widget);
246 *condition = mc_search (arg, edit_filename, search_type) ? 1 : 0;
247 g_free (edit_filename);
249 else
250 #endif
251 *condition = panel != NULL &&
252 mc_search (arg, panel->dir.list[panel->selected].fname, search_type) ? 1 : 0;
253 break;
254 case 'y': /* syntax pattern */
255 #ifdef USE_INTERNAL_EDIT
256 if (edit_widget != NULL)
258 const char *syntax_type = edit_get_syntax_type (edit_widget);
259 if (syntax_type != NULL)
261 p = extract_arg (p, arg, sizeof (arg));
262 *condition = mc_search (arg, syntax_type, MC_SEARCH_T_NORMAL) ? 1 : 0;
265 #endif
266 break;
267 case 'd':
268 p = extract_arg (p, arg, sizeof (arg));
270 char *cwd_str;
272 cwd_str = vfs_path_to_str (panel->cwd_vpath);
273 *condition = panel != NULL && mc_search (arg, cwd_str, search_type) ? 1 : 0;
274 g_free (cwd_str);
276 break;
277 case 't':
278 p = extract_arg (p, arg, sizeof (arg));
279 *condition = panel != NULL && test_type (panel, arg) ? 1 : 0;
280 break;
281 case 'x': /* executable */
283 struct stat status;
285 p = extract_arg (p, arg, sizeof (arg));
286 if (stat (arg, &status) == 0)
287 *condition = is_exe (status.st_mode) ? 1 : 0;
288 else
289 *condition = 0;
290 break;
292 default:
293 debug_error = 1;
294 break;
295 } /* switch */
297 } /* while */
298 return p;
301 /* --------------------------------------------------------------------------------------------- */
302 /** General purpose condition debug output handler */
304 static void
305 debug_out (char *start, char *end, int cond)
307 static char *msg;
308 int len;
310 if (start == NULL && end == NULL)
312 /* Show output */
313 if (debug_flag && msg)
315 len = strlen (msg);
316 if (len)
317 msg[len - 1] = 0;
318 message (D_NORMAL, _("Debug"), "%s", msg);
321 debug_flag = 0;
322 g_free (msg);
323 msg = NULL;
325 else
327 const char *type;
328 char *p;
330 /* Save debug info for later output */
331 if (!debug_flag)
332 return;
333 /* Save the result of the condition */
334 if (debug_error)
336 type = _("ERROR:");
337 debug_error = 0;
339 else if (cond)
340 type = _("True:");
341 else
342 type = _("False:");
343 /* This is for debugging, don't need to be super efficient. */
344 if (end == NULL)
345 p = g_strdup_printf ("%s %s %c \n", msg ? msg : "", type, *start);
346 else
347 p = g_strdup_printf ("%s %s %.*s \n", msg ? msg : "", type, (int) (end - start), start);
348 g_free (msg);
349 msg = p;
353 /* --------------------------------------------------------------------------------------------- */
354 /** Calculates the truth value of one lineful of conditions. Returns
355 the point just before the end of line. */
357 static char *
358 test_line (WEdit * edit_widget, char *p, int *result)
360 int condition;
361 char operator;
362 char *debug_start, *debug_end;
364 /* Repeat till end of line */
365 while (*p && *p != '\n')
367 /* support quote space .mnu */
368 while ((*p == ' ' && *(p - 1) != '\\') || *p == '\t')
369 p++;
370 if (!*p || *p == '\n')
371 break;
372 operator = *p++;
373 if (*p == '?')
375 debug_flag = 1;
376 p++;
378 /* support quote space .mnu */
379 while ((*p == ' ' && *(p - 1) != '\\') || *p == '\t')
380 p++;
381 if (!*p || *p == '\n')
382 break;
383 condition = 1; /* True by default */
385 debug_start = p;
386 p = test_condition (edit_widget, p, &condition);
387 debug_end = p;
388 /* Add one debug statement */
389 debug_out (debug_start, debug_end, condition);
391 switch (operator)
393 case '+':
394 case '=':
395 /* Assignment */
396 *result = condition;
397 break;
398 case '&': /* Logical and */
399 *result &= condition;
400 break;
401 case '|': /* Logical or */
402 *result |= condition;
403 break;
404 default:
405 debug_error = 1;
406 break;
407 } /* switch */
408 /* Add one debug statement */
409 debug_out (&operator, NULL, *result);
411 } /* while (*p != '\n') */
412 /* Report debug message */
413 debug_out (NULL, NULL, 1);
415 if (!*p || *p == '\n')
416 str_prev_char (&p);
417 return p;
420 /* --------------------------------------------------------------------------------------------- */
421 /** FIXME: recode this routine on version 3.0, it could be cleaner */
423 static void
424 execute_menu_command (WEdit * edit_widget, const char *commands, gboolean show_prompt)
426 FILE *cmd_file;
427 int cmd_file_fd;
428 int expand_prefix_found = 0;
429 char *parameter = 0;
430 gboolean do_quote = FALSE;
431 char lc_prompt[80];
432 int col;
433 vfs_path_t *file_name_vpath;
434 int run_view = 0;
436 /* Skip menu entry title line */
437 commands = strchr (commands, '\n');
438 if (!commands)
440 return;
443 cmd_file_fd = mc_mkstemps (&file_name_vpath, "mcusr", SCRIPT_SUFFIX);
445 if (cmd_file_fd == -1)
447 message (D_ERROR, MSG_ERROR, _("Cannot create temporary command file\n%s"),
448 unix_error_string (errno));
449 vfs_path_free (file_name_vpath);
450 return;
452 cmd_file = fdopen (cmd_file_fd, "w");
453 fputs ("#! /bin/sh\n", cmd_file);
454 commands++;
456 for (col = 0; *commands; commands++)
458 if (col == 0)
460 if (*commands != ' ' && *commands != '\t')
461 break;
462 while (*commands == ' ' || *commands == '\t')
463 commands++;
464 if (*commands == 0)
465 break;
467 col++;
468 if (*commands == '\n')
469 col = 0;
470 if (parameter)
472 if (*commands == '}')
474 char *tmp;
475 *parameter = 0;
476 parameter =
477 input_dialog (_("Parameter"), lc_prompt, MC_HISTORY_FM_MENU_EXEC_PARAM, "",
478 INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_CD |
479 INPUT_COMPLETE_HOSTNAMES | INPUT_COMPLETE_VARIABLES |
480 INPUT_COMPLETE_USERNAMES);
481 if (!parameter || !*parameter)
483 /* User canceled */
484 fclose (cmd_file);
485 mc_unlink (file_name_vpath);
486 vfs_path_free (file_name_vpath);
487 return;
489 if (do_quote)
491 tmp = name_quote (parameter, 0);
492 fputs (tmp, cmd_file);
493 g_free (tmp);
495 else
496 fputs (parameter, cmd_file);
497 g_free (parameter);
498 parameter = 0;
500 else
502 if (parameter < &lc_prompt[sizeof (lc_prompt) - 1])
504 *parameter++ = *commands;
508 else if (expand_prefix_found)
510 expand_prefix_found = 0;
511 if (g_ascii_isdigit ((gchar) * commands))
513 do_quote = (atoi (commands) != 0);
514 while (g_ascii_isdigit ((gchar) * commands))
515 commands++;
517 if (*commands == '{')
518 parameter = lc_prompt;
519 else
521 char *text = expand_format (edit_widget, *commands, do_quote);
522 fputs (text, cmd_file);
523 g_free (text);
526 else
528 if (*commands == '%')
530 int i = check_format_view (commands + 1);
531 if (i)
533 commands += i;
534 run_view = 1;
536 else
538 do_quote = TRUE; /* Default: Quote expanded macro */
539 expand_prefix_found = 1;
542 else
543 fputc (*commands, cmd_file);
546 fclose (cmd_file);
547 mc_chmod (file_name_vpath, S_IRWXU);
548 if (run_view)
550 char *file_name;
552 file_name = vfs_path_to_str (file_name_vpath);
553 mcview_viewer (file_name, NULL, 0);
554 g_free (file_name);
555 dialog_switch_process_pending ();
557 else
559 /* execute the command indirectly to allow execution even
560 * on no-exec filesystems. */
561 char *file_name, *cmd;
563 file_name = vfs_path_to_str (file_name_vpath);
564 cmd = g_strconcat ("/bin/sh ", file_name, (char *) NULL);
565 g_free (file_name);
566 if (!show_prompt)
568 if (system (cmd) == -1)
569 message (D_ERROR, MSG_ERROR, "%s", _("Error calling program"));
571 else
573 shell_execute (cmd, EXECUTE_HIDE);
575 g_free (cmd);
577 mc_unlink (file_name_vpath);
578 vfs_path_free (file_name_vpath);
581 /* --------------------------------------------------------------------------------------------- */
583 ** Check owner of the menu file. Using menu file is allowed, if
584 ** owner of the menu is root or the actual user. In either case
585 ** file should not be group and word-writable.
587 ** Q. Should we apply this routine to system and home menu (and .ext files)?
590 static int
591 menu_file_own (char *path)
593 struct stat st;
595 if (stat (path, &st) == 0
596 && (!st.st_uid || (st.st_uid == geteuid ())) && ((st.st_mode & (S_IWGRP | S_IWOTH)) == 0))
598 return 1;
600 if (verbose)
602 message (D_NORMAL, _("Warning -- ignoring file"),
603 _("File %s is not owned by root or you or is world writable.\n"
604 "Using it may compromise your security"), path);
606 return 0;
609 /* --------------------------------------------------------------------------------------------- */
610 /*** public functions ****************************************************************************/
611 /* --------------------------------------------------------------------------------------------- */
613 /* Formats defined:
614 %% The % character
615 %f The current file (if non-local vfs, file will be copied locally and
616 %f will be full path to it).
617 %p The current file
618 %d The current working directory
619 %s "Selected files"; the tagged files if any, otherwise the current file
620 %t Tagged files
621 %u Tagged files (and they are untagged on return from expand_format)
622 %view Runs the commands and pipes standard output to the view command.
623 If %view is immediately followed by '{', recognize keywords
624 ascii, hex, nroff and unform
626 If the format letter is in uppercase, it refers to the other panel.
628 With a number followed the % character you can turn quoting on (default)
629 and off. For example:
630 %f quote expanded macro
631 %1f ditto
632 %0f don't quote expanded macro
634 expand_format returns a memory block that must be free()d.
637 /* Returns how many characters we should advance if %view was found */
639 check_format_view (const char *p)
641 const char *q = p;
642 if (!strncmp (p, "view", 4))
644 q += 4;
645 if (*q == '{')
647 for (q++; *q && *q != '}'; q++)
649 if (!strncmp (q, "ascii", 5))
651 mcview_default_hex_mode = 0;
652 q += 4;
654 else if (!strncmp (q, "hex", 3))
656 mcview_default_hex_mode = 1;
657 q += 2;
659 else if (!strncmp (q, "nroff", 5))
661 mcview_default_nroff_flag = 1;
662 q += 4;
664 else if (!strncmp (q, "unform", 6))
666 mcview_default_nroff_flag = 0;
667 q += 5;
670 if (*q == '}')
671 q++;
673 return q - p;
675 return 0;
678 /* --------------------------------------------------------------------------------------------- */
681 check_format_cd (const char *p)
683 return (strncmp (p, "cd", 2)) ? 0 : 3;
686 /* --------------------------------------------------------------------------------------------- */
687 /* Check if p has a "^var\{var-name\}" */
688 /* Returns the number of skipped characters (zero on not found) */
689 /* V will be set to the expanded variable name */
692 check_format_var (const char *p, char **v)
694 const char *q = p;
695 char *var_name;
696 const char *value;
697 const char *dots = 0;
699 *v = 0;
700 if (!strncmp (p, "var{", 4))
702 for (q += 4; *q && *q != '}'; q++)
704 if (*q == ':')
705 dots = q + 1;
707 if (!*q)
708 return 0;
710 if (!dots || dots == q + 5)
712 message (D_ERROR,
713 _("Format error on file Extensions File"),
714 !dots ? _("The %%var macro has no default")
715 : _("The %%var macro has no variable"));
716 return 0;
719 /* Copy the variable name */
720 var_name = g_strndup (p + 4, dots - 2 - (p + 3));
722 value = getenv (var_name);
723 g_free (var_name);
724 if (value)
726 *v = g_strdup (value);
727 return q - p;
729 var_name = g_strndup (dots, q - dots);
730 *v = var_name;
731 return q - p;
733 return 0;
736 /* --------------------------------------------------------------------------------------------- */
738 char *
739 expand_format (struct WEdit *edit_widget, char c, gboolean do_quote)
741 WPanel *panel = NULL;
742 char *(*quote_func) (const char *, int);
743 char *fname = NULL;
744 char *result;
745 char c_lc;
747 #ifndef USE_INTERNAL_EDIT
748 (void) edit_widget;
749 #endif
751 if (c == '%')
752 return g_strdup ("%");
754 switch (mc_global.mc_run_mode)
756 case MC_RUN_FULL:
757 if (g_ascii_islower ((gchar) c))
758 panel = current_panel;
759 else
761 if (get_other_type () != view_listing)
762 return g_strdup ("");
763 panel = other_panel;
765 fname = g_strdup (panel->dir.list[panel->selected].fname);
766 break;
768 #ifdef USE_INTERNAL_EDIT
769 case MC_RUN_EDITOR:
770 fname = edit_get_file_name (edit_widget);
771 break;
772 #endif
774 default:
775 /* other modes don't use formats */
776 return g_strdup ("");
779 if (do_quote)
780 quote_func = name_quote;
781 else
782 quote_func = fake_name_quote;
784 c_lc = g_ascii_tolower ((gchar) c);
786 switch (c_lc)
788 case 'f':
789 case 'p':
790 result = (*quote_func) (fname, 0);
791 goto ret;
792 case 'x':
793 result = (*quote_func) (extension (fname), 0);
794 goto ret;
795 case 'd':
797 char *cwd;
798 char *qstr;
800 if (panel)
801 cwd = vfs_path_to_str (panel->cwd_vpath);
802 else
803 cwd = vfs_get_current_dir ();
805 qstr = (*quote_func) (cwd, 0);
807 g_free (cwd);
809 result = qstr;
810 goto ret;
812 case 'i': /* indent equal number cursor position in line */
813 #ifdef USE_INTERNAL_EDIT
814 if (edit_widget)
816 result = g_strnfill (edit_get_curs_col (edit_widget), ' ');
817 goto ret;
819 #endif
820 break;
821 case 'y': /* syntax type */
822 #ifdef USE_INTERNAL_EDIT
823 if (edit_widget)
825 const char *syntax_type = edit_get_syntax_type (edit_widget);
826 if (syntax_type != NULL)
828 result = g_strdup (syntax_type);
829 goto ret;
832 #endif
833 break;
834 case 'k': /* block file name */
835 case 'b': /* block file name / strip extension */
837 #ifdef USE_INTERNAL_EDIT
838 if (edit_widget)
840 char *file;
842 file = mc_config_get_full_path (EDIT_BLOCK_FILE);
843 result = (*quote_func) (file, 0);
844 g_free (file);
845 goto ret;
847 #endif
848 if (c_lc == 'b')
850 result = strip_ext ((*quote_func) (fname, 0));
851 goto ret;
853 break;
855 case 'n': /* strip extension in editor */
856 #ifdef USE_INTERNAL_EDIT
857 if (edit_widget)
859 result = strip_ext ((*quote_func) (fname, 0));
860 goto ret;
862 #endif
863 break;
864 case 'm': /* menu file name */
865 if (menu)
867 result = (*quote_func) (menu, 0);
868 goto ret;
870 break;
871 case 's':
872 if (!panel || !panel->marked)
874 result = (*quote_func) (fname, 0);
875 goto ret;
878 /* Fall through */
880 case 't':
881 case 'u':
883 GString *block;
884 int i;
886 if (panel == NULL)
888 result = g_strdup ("");
889 goto ret;
892 block = g_string_sized_new (16);
894 for (i = 0; i < panel->count; i++)
895 if (panel->dir.list[i].f.marked)
897 char *tmp;
899 tmp = (*quote_func) (panel->dir.list[i].fname, 0);
900 g_string_append (block, tmp);
901 g_string_append_c (block, ' ');
902 g_free (tmp);
904 if (c_lc == 'u')
905 do_file_mark (panel, i, 0);
907 result = g_string_free (block, FALSE);
908 goto ret;
909 } /* sub case block */
910 } /* switch */
911 result = g_strdup ("% ");
912 result[1] = c;
913 ret:
914 g_free (fname);
915 return result;
918 /* --------------------------------------------------------------------------------------------- */
920 * If edit_widget is NULL then we are called from the mc menu,
921 * otherwise we are called from the mcedit menu.
924 gboolean
925 user_menu_cmd (struct WEdit * edit_widget, const char *menu_file, int selected_entry)
927 char *p;
928 char *data, **entries;
929 int max_cols, menu_lines, menu_limit;
930 int col, i, accept_entry = 1;
931 int selected, old_patterns;
932 Listbox *listbox;
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 g_free (menu);
952 menu = NULL;
953 return FALSE;
956 g_free (menu);
957 if (edit_widget)
958 menu = mc_config_get_full_path (EDIT_HOME_MENU);
959 else
960 menu = mc_config_get_full_path (MC_USERMENU_FILE);
963 if (!exist_file (menu))
965 g_free (menu);
966 menu =
967 mc_build_filename (mc_config_get_home_dir (),
968 edit_widget ? EDIT_GLOBAL_MENU : MC_GLOBAL_MENU, NULL);
969 if (!exist_file (menu))
971 g_free (menu);
972 menu =
973 mc_build_filename (mc_global.sysconfig_dir,
974 edit_widget ? EDIT_GLOBAL_MENU : MC_GLOBAL_MENU, NULL);
975 if (!exist_file (menu))
977 g_free (menu);
978 menu = mc_build_filename
979 (mc_global.share_data_dir, edit_widget ? EDIT_GLOBAL_MENU : MC_GLOBAL_MENU,
980 NULL);
986 if (!g_file_get_contents (menu, &data, NULL, NULL))
988 message (D_ERROR, MSG_ERROR, _("Cannot open file%s\n%s"), menu, unix_error_string (errno));
989 g_free (menu);
990 menu = NULL;
991 return FALSE;
994 max_cols = 0;
995 selected = 0;
996 menu_limit = 0;
997 entries = 0;
999 /* Parse the menu file */
1000 old_patterns = easy_patterns;
1001 p = check_patterns (data);
1002 for (menu_lines = col = 0; *p; str_next_char (&p))
1004 if (menu_lines >= menu_limit)
1006 char **new_entries;
1008 menu_limit += MAX_ENTRIES;
1009 new_entries = g_try_realloc (entries, sizeof (new_entries[0]) * menu_limit);
1011 if (new_entries == NULL)
1012 break;
1014 entries = new_entries;
1015 new_entries += menu_limit;
1016 while (--new_entries >= &entries[menu_lines])
1017 *new_entries = NULL;
1019 if (col == 0 && !entries[menu_lines])
1021 if (*p == '#')
1023 /* show prompt if first line of external script is #interactive */
1024 if (selected_entry >= 0 && strncmp (p, "#silent", 7) == 0)
1025 interactive = FALSE;
1026 /* A commented menu entry */
1027 accept_entry = 1;
1029 else if (*p == '+')
1031 if (*(p + 1) == '=')
1033 /* Combined adding and default */
1034 p = test_line (edit_widget, p + 1, &accept_entry);
1035 if (selected == 0 && accept_entry)
1036 selected = menu_lines;
1038 else
1040 /* A condition for adding the entry */
1041 p = test_line (edit_widget, p, &accept_entry);
1044 else if (*p == '=')
1046 if (*(p + 1) == '+')
1048 /* Combined adding and default */
1049 p = test_line (edit_widget, p + 1, &accept_entry);
1050 if (selected == 0 && accept_entry)
1051 selected = menu_lines;
1053 else
1055 /* A condition for making the entry default */
1056 i = 1;
1057 p = test_line (edit_widget, p, &i);
1058 if (selected == 0 && i)
1059 selected = menu_lines;
1062 else if (*p != ' ' && *p != '\t' && str_isprint (p))
1064 /* A menu entry title line */
1065 if (accept_entry)
1066 entries[menu_lines] = p;
1067 else
1068 accept_entry = 1;
1071 if (*p == '\n')
1073 if (entries[menu_lines])
1075 menu_lines++;
1076 accept_entry = 1;
1078 max_cols = max (max_cols, col);
1079 col = 0;
1081 else
1083 if (*p == '\t')
1084 *p = ' ';
1085 col++;
1089 if (menu_lines == 0)
1091 message (D_ERROR, MSG_ERROR, _("No suitable entries found in %s"), menu);
1092 res = FALSE;
1094 else
1096 if (selected_entry >= 0)
1097 selected = selected_entry;
1098 else
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 "[Menu File Edit]");
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);
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 g_free (menu);
1128 menu = NULL;
1129 g_free (entries);
1130 g_free (data);
1131 return res;
1134 /* --------------------------------------------------------------------------------------------- */