Moved charsets.[ch] from src to lib directory
[midnight-commander.git] / src / user.c
blob20df0d7a9d36559c23102a3ab62d36b03edb36a7
1 /* User Menu implementation
2 Copyright (C) 1994, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
3 2006, 2007 Free Software Foundation, Inc.
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
19 /** \file user.c
20 * \brief Source: user menu implementation
23 #include <config.h>
25 #include <ctype.h>
26 #include <errno.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <string.h>
31 #include "lib/global.h"
32 #include "lib/tty/tty.h"
33 #include "lib/skin.h"
34 #include "lib/search.h"
35 #include "lib/vfs/mc-vfs/vfs.h"
36 #include "lib/strutil.h"
37 #include "lib/util.h"
38 #include "lib/widget.h"
40 #include "src/editor/edit.h" /* WEdit, BLOCK_FILE */
41 #include "src/viewer/mcviewer.h" /* for default_* externs */
43 #include "dir.h"
44 #include "panel.h"
45 #include "main.h"
46 #include "user.h"
47 #include "layout.h"
48 #include "execute.h"
49 #include "setup.h"
50 #include "history.h"
52 /*** global variables ****************************************************************************/
54 /*** file scope macro definitions ****************************************************************/
56 #define MAX_ENTRIES 16
57 #define MAX_ENTRY_LEN 60
59 /*** file scope type declarations ****************************************************************/
61 /*** file scope variables ************************************************************************/
63 static int debug_flag = 0;
64 static int debug_error = 0;
65 static char *menu = NULL;
67 /*** file scope functions ************************************************************************/
68 /* --------------------------------------------------------------------------------------------- */
70 /** strip file's extension */
71 static char *
72 strip_ext (char *ss)
74 register char *s = ss;
75 char *e = NULL;
76 while (*s)
78 if (*s == '.')
79 e = s;
80 if (*s == PATH_SEP && e)
81 e = NULL; /* '.' in *directory* name */
82 s++;
84 if (e)
85 *e = 0;
86 return ss;
89 /* --------------------------------------------------------------------------------------------- */
90 /**
91 * Check for the "shell_patterns" directive. If it's found and valid,
92 * interpret it and move the pointer past the directive. Return the
93 * current pointer.
96 static char *
97 check_patterns (char *p)
99 static const char def_name[] = "shell_patterns=";
100 char *p0 = p;
102 if (strncmp (p, def_name, sizeof (def_name) - 1) != 0)
103 return p0;
105 p += sizeof (def_name) - 1;
106 if (*p == '1')
107 easy_patterns = 1;
108 else if (*p == '0')
109 easy_patterns = 0;
110 else
111 return p0;
113 /* Skip spaces */
114 p++;
115 while (*p == '\n' || *p == '\t' || *p == ' ')
116 p++;
117 return p;
120 /* --------------------------------------------------------------------------------------------- */
121 /** Copies a whitespace separated argument from p to arg. Returns the
122 point after argument. */
124 static char *
125 extract_arg (char *p, char *arg, int size)
127 char *np;
129 while (*p && (*p == ' ' || *p == '\t' || *p == '\n'))
130 p++;
131 /* support quote space .mnu */
132 while (*p && (*p != ' ' || *(p - 1) == '\\') && *p != '\t' && *p != '\n')
134 np = str_get_next_char (p);
135 if (np - p >= size)
136 break;
137 memcpy (arg, p, np - p);
138 arg += np - p;
139 size -= np - p;
140 p = np;
142 *arg = 0;
143 if (!*p || *p == '\n')
144 str_prev_char (&p);
145 return p;
148 /* --------------------------------------------------------------------------------------------- */
149 /* Tests whether the selected file in the panel is of any of the types
150 specified in argument. */
152 static int
153 test_type (WPanel * panel, char *arg)
155 int result = 0; /* False by default */
156 int st_mode = panel->dir.list[panel->selected].st.st_mode;
158 for (; *arg != 0; arg++)
160 switch (*arg)
162 case 'n': /* Not a directory */
163 result |= !S_ISDIR (st_mode);
164 break;
165 case 'r': /* Regular file */
166 result |= S_ISREG (st_mode);
167 break;
168 case 'd': /* Directory */
169 result |= S_ISDIR (st_mode);
170 break;
171 case 'l': /* Link */
172 result |= S_ISLNK (st_mode);
173 break;
174 case 'c': /* Character special */
175 result |= S_ISCHR (st_mode);
176 break;
177 case 'b': /* Block special */
178 result |= S_ISBLK (st_mode);
179 break;
180 case 'f': /* Fifo (named pipe) */
181 result |= S_ISFIFO (st_mode);
182 break;
183 case 's': /* Socket */
184 result |= S_ISSOCK (st_mode);
185 break;
186 case 'x': /* Executable */
187 result |= (st_mode & 0111) ? 1 : 0;
188 break;
189 case 't':
190 result |= panel->marked ? 1 : 0;
191 break;
192 default:
193 debug_error = 1;
194 break;
197 return result;
200 /* --------------------------------------------------------------------------------------------- */
201 /** Calculates the truth value of the next condition starting from
202 p. Returns the point after condition. */
204 static char *
205 test_condition (WEdit * edit_widget, char *p, int *condition)
207 WPanel *panel;
208 char arg[256];
209 mc_search_type_t search_type;
211 if (easy_patterns)
213 search_type = MC_SEARCH_T_GLOB;
215 else
217 search_type = MC_SEARCH_T_REGEX;
220 /* Handle one condition */
221 for (; *p != '\n' && *p != '&' && *p != '|'; p++)
223 /* support quote space .mnu */
224 if ((*p == ' ' && *(p - 1) != '\\') || *p == '\t')
225 continue;
226 if (*p >= 'a')
227 panel = current_panel;
228 else
230 if (get_other_type () == view_listing)
231 panel = other_panel;
232 else
233 panel = NULL;
235 *p |= 0x20;
237 switch (*p++)
239 case '!':
240 p = test_condition (edit_widget, p, condition);
241 *condition = !*condition;
242 str_prev_char (&p);
243 break;
244 case 'f': /* file name pattern */
245 p = extract_arg (p, arg, sizeof (arg));
246 *condition = panel
247 && mc_search (arg, panel->dir.list[panel->selected].fname, search_type);
248 break;
249 case 'y': /* syntax pattern */
250 #ifdef USE_INTERNAL_EDIT
251 if (edit_widget)
253 const char *syntax_type = edit_get_syntax_type (edit_widget);
254 if (syntax_type != NULL)
256 p = extract_arg (p, arg, sizeof (arg));
257 *condition = panel && mc_search (arg, syntax_type, MC_SEARCH_T_NORMAL);
260 #endif
261 break;
262 case 'd':
263 p = extract_arg (p, arg, sizeof (arg));
264 *condition = panel && mc_search (arg, panel->cwd, search_type);
265 break;
266 case 't':
267 p = extract_arg (p, arg, sizeof (arg));
268 *condition = panel && test_type (panel, arg);
269 break;
270 case 'x': /* executable */
272 struct stat status;
274 p = extract_arg (p, arg, sizeof (arg));
275 if (stat (arg, &status) == 0)
276 *condition = is_exe (status.st_mode);
277 else
278 *condition = 0;
279 break;
281 default:
282 debug_error = 1;
283 break;
284 } /* switch */
286 } /* while */
287 return p;
290 /* --------------------------------------------------------------------------------------------- */
291 /** General purpose condition debug output handler */
293 static void
294 debug_out (char *start, char *end, int cond)
296 static char *msg;
297 int len;
299 if (start == NULL && end == NULL)
301 /* Show output */
302 if (debug_flag && msg)
304 len = strlen (msg);
305 if (len)
306 msg[len - 1] = 0;
307 message (D_NORMAL, _("Debug"), "%s", msg);
310 debug_flag = 0;
311 g_free (msg);
312 msg = NULL;
314 else
316 const char *type;
317 char *p;
319 /* Save debug info for later output */
320 if (!debug_flag)
321 return;
322 /* Save the result of the condition */
323 if (debug_error)
325 type = _("ERROR:");
326 debug_error = 0;
328 else if (cond)
329 type = _("True:");
330 else
331 type = _("False:");
332 /* This is for debugging, don't need to be super efficient. */
333 if (end == NULL)
334 p = g_strdup_printf ("%s %s %c \n", msg ? msg : "", type, *start);
335 else
336 p = g_strdup_printf ("%s %s %.*s \n", msg ? msg : "", type, (int) (end - start), start);
337 g_free (msg);
338 msg = p;
342 /* --------------------------------------------------------------------------------------------- */
343 /** Calculates the truth value of one lineful of conditions. Returns
344 the point just before the end of line. */
346 static char *
347 test_line (WEdit * edit_widget, char *p, int *result)
349 int condition;
350 char operator;
351 char *debug_start, *debug_end;
353 /* Repeat till end of line */
354 while (*p && *p != '\n')
356 /* support quote space .mnu */
357 while ((*p == ' ' && *(p - 1) != '\\') || *p == '\t')
358 p++;
359 if (!*p || *p == '\n')
360 break;
361 operator = *p++;
362 if (*p == '?')
364 debug_flag = 1;
365 p++;
367 /* support quote space .mnu */
368 while ((*p == ' ' && *(p - 1) != '\\') || *p == '\t')
369 p++;
370 if (!*p || *p == '\n')
371 break;
372 condition = 1; /* True by default */
374 debug_start = p;
375 p = test_condition (edit_widget, p, &condition);
376 debug_end = p;
377 /* Add one debug statement */
378 debug_out (debug_start, debug_end, condition);
380 switch (operator)
382 case '+':
383 case '=':
384 /* Assignment */
385 *result = condition;
386 break;
387 case '&': /* Logical and */
388 *result &= condition;
389 break;
390 case '|': /* Logical or */
391 *result |= condition;
392 break;
393 default:
394 debug_error = 1;
395 break;
396 } /* switch */
397 /* Add one debug statement */
398 debug_out (&operator, NULL, *result);
400 } /* while (*p != '\n') */
401 /* Report debug message */
402 debug_out (NULL, NULL, 1);
404 if (!*p || *p == '\n')
405 str_prev_char (&p);
406 return p;
409 /* --------------------------------------------------------------------------------------------- */
410 /** FIXME: recode this routine on version 3.0, it could be cleaner */
412 static void
413 execute_menu_command (WEdit * edit_widget, const char *commands)
415 FILE *cmd_file;
416 int cmd_file_fd;
417 int expand_prefix_found = 0;
418 char *parameter = 0;
419 gboolean do_quote = FALSE;
420 char lc_prompt[80];
421 int col;
422 char *file_name;
423 int run_view = 0;
425 /* Skip menu entry title line */
426 commands = strchr (commands, '\n');
427 if (!commands)
429 return;
432 cmd_file_fd = mc_mkstemps (&file_name, "mcusr", SCRIPT_SUFFIX);
434 if (cmd_file_fd == -1)
436 message (D_ERROR, MSG_ERROR, _("Cannot create temporary command file\n%s"),
437 unix_error_string (errno));
438 return;
440 cmd_file = fdopen (cmd_file_fd, "w");
441 fputs ("#! /bin/sh\n", cmd_file);
442 commands++;
444 for (col = 0; *commands; commands++)
446 if (col == 0)
448 if (*commands != ' ' && *commands != '\t')
449 break;
450 while (*commands == ' ' || *commands == '\t')
451 commands++;
452 if (*commands == 0)
453 break;
455 col++;
456 if (*commands == '\n')
457 col = 0;
458 if (parameter)
460 if (*commands == '}')
462 char *tmp;
463 *parameter = 0;
464 parameter =
465 input_dialog (_("Parameter"), lc_prompt, MC_HISTORY_FM_MENU_EXEC_PARAM, "");
466 if (!parameter || !*parameter)
468 /* User canceled */
469 fclose (cmd_file);
470 unlink (file_name);
471 g_free (file_name);
472 return;
474 if (do_quote)
476 tmp = name_quote (parameter, 0);
477 fputs (tmp, cmd_file);
478 g_free (tmp);
480 else
481 fputs (parameter, cmd_file);
482 g_free (parameter);
483 parameter = 0;
485 else
487 if (parameter < &lc_prompt[sizeof (lc_prompt) - 1])
489 *parameter++ = *commands;
493 else if (expand_prefix_found)
495 expand_prefix_found = 0;
496 if (g_ascii_isdigit ((gchar) * commands))
498 do_quote = (atoi (commands) != 0);
499 while (g_ascii_isdigit ((gchar) * commands))
500 commands++;
502 if (*commands == '{')
503 parameter = lc_prompt;
504 else
506 char *text = expand_format (edit_widget, *commands, do_quote);
507 fputs (text, cmd_file);
508 g_free (text);
511 else
513 if (*commands == '%')
515 int i = check_format_view (commands + 1);
516 if (i)
518 commands += i;
519 run_view = 1;
521 else
523 do_quote = TRUE; /* Default: Quote expanded macro */
524 expand_prefix_found = 1;
527 else
528 fputc (*commands, cmd_file);
531 fclose (cmd_file);
532 chmod (file_name, S_IRWXU);
533 if (run_view)
535 mcview_viewer (file_name, NULL, 0);
536 dialog_switch_process_pending ();
538 else
540 /* execute the command indirectly to allow execution even
541 * on no-exec filesystems. */
542 char *cmd = g_strconcat ("/bin/sh ", file_name, (char *) NULL);
543 shell_execute (cmd, EXECUTE_HIDE);
544 g_free (cmd);
546 unlink (file_name);
547 g_free (file_name);
550 /* --------------------------------------------------------------------------------------------- */
552 ** Check owner of the menu file. Using menu file is allowed, if
553 ** owner of the menu is root or the actual user. In either case
554 ** file should not be group and word-writable.
556 ** Q. Should we apply this routine to system and home menu (and .ext files)?
559 static int
560 menu_file_own (char *path)
562 struct stat st;
564 if (stat (path, &st) == 0
565 && (!st.st_uid || (st.st_uid == geteuid ())) && ((st.st_mode & (S_IWGRP | S_IWOTH)) == 0))
567 return 1;
569 if (verbose)
571 message (D_NORMAL, _("Warning -- ignoring file"),
572 _("File %s is not owned by root or you or is world writable.\n"
573 "Using it may compromise your security"), path);
575 return 0;
578 /* --------------------------------------------------------------------------------------------- */
579 /*** public functions ****************************************************************************/
580 /* --------------------------------------------------------------------------------------------- */
582 /* Formats defined:
583 %% The % character
584 %f The current file (if non-local vfs, file will be copied locally and
585 %f will be full path to it).
586 %p The current file
587 %d The current working directory
588 %s "Selected files"; the tagged files if any, otherwise the current file
589 %t Tagged files
590 %u Tagged files (and they are untagged on return from expand_format)
591 %view Runs the commands and pipes standard output to the view command.
592 If %view is immediately followed by '{', recognize keywords
593 ascii, hex, nroff and unform
595 If the format letter is in uppercase, it refers to the other panel.
597 With a number followed the % character you can turn quoting on (default)
598 and off. For example:
599 %f quote expanded macro
600 %1f ditto
601 %0f don't quote expanded macro
603 expand_format returns a memory block that must be free()d.
606 /* Returns how many characters we should advance if %view was found */
608 check_format_view (const char *p)
610 const char *q = p;
611 if (!strncmp (p, "view", 4))
613 q += 4;
614 if (*q == '{')
616 for (q++; *q && *q != '}'; q++)
618 if (!strncmp (q, "ascii", 5))
620 mcview_default_hex_mode = 0;
621 q += 4;
623 else if (!strncmp (q, "hex", 3))
625 mcview_default_hex_mode = 1;
626 q += 2;
628 else if (!strncmp (q, "nroff", 5))
630 mcview_default_nroff_flag = 1;
631 q += 4;
633 else if (!strncmp (q, "unform", 6))
635 mcview_default_nroff_flag = 0;
636 q += 5;
639 if (*q == '}')
640 q++;
642 return q - p;
644 return 0;
647 /* --------------------------------------------------------------------------------------------- */
650 check_format_cd (const char *p)
652 return (strncmp (p, "cd", 2)) ? 0 : 3;
655 /* --------------------------------------------------------------------------------------------- */
656 /* Check if p has a "^var\{var-name\}" */
657 /* Returns the number of skipped characters (zero on not found) */
658 /* V will be set to the expanded variable name */
661 check_format_var (const char *p, char **v)
663 const char *q = p;
664 char *var_name;
665 const char *value;
666 const char *dots = 0;
668 *v = 0;
669 if (!strncmp (p, "var{", 4))
671 for (q += 4; *q && *q != '}'; q++)
673 if (*q == ':')
674 dots = q + 1;
676 if (!*q)
677 return 0;
679 if (!dots || dots == q + 5)
681 message (D_ERROR,
682 _("Format error on file Extensions File"),
683 !dots ? _("The %%var macro has no default")
684 : _("The %%var macro has no variable"));
685 return 0;
688 /* Copy the variable name */
689 var_name = g_strndup (p + 4, dots - 2 - (p + 3));
691 value = getenv (var_name);
692 g_free (var_name);
693 if (value)
695 *v = g_strdup (value);
696 return q - p;
698 var_name = g_strndup (dots, q - dots);
699 *v = var_name;
700 return q - p;
702 return 0;
705 /* --------------------------------------------------------------------------------------------- */
707 char *
708 expand_format (struct WEdit *edit_widget, char c, gboolean do_quote)
710 WPanel *panel = NULL;
711 char *(*quote_func) (const char *, int);
712 char *fname = NULL;
713 char *result;
714 char c_lc;
716 #ifndef USE_INTERNAL_EDIT
717 (void) edit_widget;
718 #endif
720 if (c == '%')
721 return g_strdup ("%");
723 if (mc_run_mode == MC_RUN_FULL)
725 if (g_ascii_islower ((gchar) c))
726 panel = current_panel;
727 else
729 if (get_other_type () != view_listing)
730 return g_strdup ("");
731 panel = other_panel;
733 fname = panel->dir.list[panel->selected].fname;
735 #ifdef USE_INTERNAL_EDIT
736 else if (mc_run_mode == MC_RUN_EDITOR)
737 fname = (char *) edit_get_file_name (edit_widget);
738 #endif
740 if (do_quote)
741 quote_func = name_quote;
742 else
743 quote_func = fake_name_quote;
745 c_lc = g_ascii_tolower ((gchar) c);
747 switch (c_lc)
749 case 'f':
750 case 'p':
751 return (*quote_func) (fname, 0);
752 case 'x':
753 return (*quote_func) (extension (fname), 0);
754 case 'd':
756 char *cwd;
757 char *qstr;
759 cwd = g_malloc (MC_MAXPATHLEN + 1);
761 if (panel)
762 g_strlcpy (cwd, panel->cwd, MC_MAXPATHLEN + 1);
763 else
764 mc_get_current_wd (cwd, MC_MAXPATHLEN + 1);
766 qstr = (*quote_func) (cwd, 0);
768 g_free (cwd);
770 return qstr;
772 case 'i': /* indent equal number cursor position in line */
773 #ifdef USE_INTERNAL_EDIT
774 if (edit_widget)
775 return g_strnfill (edit_get_curs_col (edit_widget), ' ');
776 #endif
777 break;
778 case 'y': /* syntax type */
779 #ifdef USE_INTERNAL_EDIT
780 if (edit_widget)
782 const char *syntax_type = edit_get_syntax_type (edit_widget);
783 if (syntax_type != NULL)
784 return g_strdup (syntax_type);
786 #endif
787 break;
788 case 'k': /* block file name */
789 case 'b': /* block file name / strip extension */
791 #ifdef USE_INTERNAL_EDIT
792 if (edit_widget)
794 char *file = concat_dir_and_file (home_dir, EDIT_BLOCK_FILE);
795 fname = (*quote_func) (file, 0);
796 g_free (file);
797 return fname;
799 #endif
800 if (c_lc == 'b')
801 return strip_ext ((*quote_func) (fname, 0));
802 break;
804 case 'n': /* strip extension in editor */
805 #ifdef USE_INTERNAL_EDIT
806 if (edit_widget)
807 return strip_ext ((*quote_func) (fname, 0));
808 #endif
809 break;
810 case 'm': /* menu file name */
811 if (menu)
812 return (*quote_func) (menu, 0);
813 break;
814 case 's':
815 if (!panel || !panel->marked)
816 return (*quote_func) (fname, 0);
818 /* Fall through */
820 case 't':
821 case 'u':
823 int length = 2, i;
824 char *block, *tmp;
826 if (!panel)
827 return g_strdup ("");
829 for (i = 0; i < panel->count; i++)
830 if (panel->dir.list[i].f.marked)
831 length += strlen (panel->dir.list[i].fname) + 1; /* for space */
833 block = g_malloc (length * 2 + 1);
834 *block = 0;
835 for (i = 0; i < panel->count; i++)
836 if (panel->dir.list[i].f.marked)
838 tmp = (*quote_func) (panel->dir.list[i].fname, 0);
839 strcat (block, tmp);
840 g_free (tmp);
841 strcat (block, " ");
842 if (c_lc == 'u')
843 do_file_mark (panel, i, 0);
845 return block;
846 } /* sub case block */
847 } /* switch */
848 result = g_strdup ("% ");
849 result[1] = c;
850 return result;
853 /* --------------------------------------------------------------------------------------------- */
855 * If edit_widget is NULL then we are called from the mc menu,
856 * otherwise we are called from the mcedit menu.
859 void
860 user_menu_cmd (struct WEdit *edit_widget)
862 char *p;
863 char *data, **entries;
864 int max_cols, menu_lines, menu_limit;
865 int col, i, accept_entry = 1;
866 int selected, old_patterns;
867 Listbox *listbox;
869 if (!vfs_current_is_local ())
871 message (D_ERROR, MSG_ERROR, "%s", _("Cannot execute commands on non-local filesystems"));
872 return;
875 menu = g_strdup (edit_widget ? EDIT_LOCAL_MENU : MC_LOCAL_MENU);
876 if (!exist_file (menu) || !menu_file_own (menu))
878 g_free (menu);
879 if (edit_widget)
880 menu = concat_dir_and_file (home_dir, EDIT_HOME_MENU);
881 else
882 menu = g_build_filename (home_dir, MC_USERCONF_DIR, MC_USERMENU_FILE, NULL);
885 if (!exist_file (menu))
887 g_free (menu);
888 menu = concat_dir_and_file (mc_home, edit_widget ? EDIT_GLOBAL_MENU : MC_GLOBAL_MENU);
889 if (!exist_file (menu))
891 g_free (menu);
892 menu = concat_dir_and_file
893 (mc_home_alt, edit_widget ? EDIT_GLOBAL_MENU : MC_GLOBAL_MENU);
898 if (!g_file_get_contents (menu, &data, NULL, NULL))
900 message (D_ERROR, MSG_ERROR, _("Cannot open file%s\n%s"), menu, unix_error_string (errno));
901 g_free (menu);
902 menu = NULL;
903 return;
906 max_cols = 0;
907 selected = 0;
908 menu_limit = 0;
909 entries = 0;
911 /* Parse the menu file */
912 old_patterns = easy_patterns;
913 p = check_patterns (data);
914 for (menu_lines = col = 0; *p; str_next_char (&p))
916 if (menu_lines >= menu_limit)
918 char **new_entries;
920 menu_limit += MAX_ENTRIES;
921 new_entries = g_try_realloc (entries, sizeof (new_entries[0]) * menu_limit);
923 if (new_entries == NULL)
924 break;
926 entries = new_entries;
927 new_entries += menu_limit;
928 while (--new_entries >= &entries[menu_lines])
929 *new_entries = NULL;
931 if (col == 0 && !entries[menu_lines])
933 if (*p == '#')
935 /* A commented menu entry */
936 accept_entry = 1;
938 else if (*p == '+')
940 if (*(p + 1) == '=')
942 /* Combined adding and default */
943 p = test_line (edit_widget, p + 1, &accept_entry);
944 if (selected == 0 && accept_entry)
945 selected = menu_lines;
947 else
949 /* A condition for adding the entry */
950 p = test_line (edit_widget, p, &accept_entry);
953 else if (*p == '=')
955 if (*(p + 1) == '+')
957 /* Combined adding and default */
958 p = test_line (edit_widget, p + 1, &accept_entry);
959 if (selected == 0 && accept_entry)
960 selected = menu_lines;
962 else
964 /* A condition for making the entry default */
965 i = 1;
966 p = test_line (edit_widget, p, &i);
967 if (selected == 0 && i)
968 selected = menu_lines;
971 else if (*p != ' ' && *p != '\t' && str_isprint (p))
973 /* A menu entry title line */
974 if (accept_entry)
975 entries[menu_lines] = p;
976 else
977 accept_entry = 1;
980 if (*p == '\n')
982 if (entries[menu_lines])
984 menu_lines++;
985 accept_entry = 1;
987 max_cols = max (max_cols, col);
988 col = 0;
990 else
992 if (*p == '\t')
993 *p = ' ';
994 col++;
998 if (menu_lines == 0)
999 message (D_ERROR, MSG_ERROR, _("No suitable entries found in %s"), menu);
1000 else
1002 max_cols = min (max (max_cols, col), MAX_ENTRY_LEN);
1004 /* Create listbox */
1005 listbox = create_listbox_window (menu_lines, max_cols + 2, _("User menu"),
1006 "[Menu File Edit]");
1007 /* insert all the items found */
1008 for (i = 0; i < menu_lines; i++)
1010 p = entries[i];
1011 LISTBOX_APPEND_TEXT (listbox, (unsigned char) p[0],
1012 extract_line (p, p + MAX_ENTRY_LEN), p);
1014 /* Select the default entry */
1015 listbox_select_entry (listbox->list, selected);
1017 selected = run_listbox (listbox);
1018 if (selected >= 0)
1019 execute_menu_command (edit_widget, entries[selected]);
1021 do_refresh ();
1024 easy_patterns = old_patterns;
1025 g_free (menu);
1026 menu = NULL;
1027 g_free (entries);
1028 g_free (data);
1031 /* --------------------------------------------------------------------------------------------- */