Concretize the usage of autocompliting in different input fields.
[midnight-commander.git] / src / filemanager / find.c
blob7b8b728b3ac430d2a149e56efd4e0a5685e2fecc
1 /*
2 Find file command for the Midnight Commander
4 Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
5 2006, 2007, 2011
6 The Free Software Foundation, Inc.
8 Written by:
9 Miguel de Icaza, 1995
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 find.c
28 * \brief Source: Find file command
31 #include <config.h>
33 #include <ctype.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <fcntl.h>
38 #include <sys/stat.h>
40 #include "lib/global.h"
42 #include "lib/tty/tty.h"
43 #include "lib/tty/key.h"
44 #include "lib/skin.h"
45 #include "lib/search.h"
46 #include "lib/mcconfig.h"
47 #include "lib/vfs/vfs.h"
48 #include "lib/strutil.h"
49 #include "lib/widget.h"
50 #include "lib/util.h" /* canonicalize_pathname() */
52 #include "src/setup.h" /* verbose */
53 #include "src/history.h" /* MC_HISTORY_SHARED_SEARCH */
55 #include "dir.h"
56 #include "cmd.h" /* view_file_at_line */
57 #include "midnight.h" /* current_panel */
58 #include "boxes.h"
59 #include "panelize.h"
61 #include "find.h"
63 /*** global variables ****************************************************************************/
65 /*** file scope macro definitions ****************************************************************/
67 /*** file scope type declarations ****************************************************************/
69 /* A couple of extra messages we need */
70 enum
72 B_STOP = B_USER + 1,
73 B_AGAIN,
74 B_PANELIZE,
75 B_TREE,
76 B_VIEW
79 typedef enum
81 FIND_CONT = 0,
82 FIND_SUSPEND,
83 FIND_ABORT
84 } FindProgressStatus;
86 /* find file options */
87 typedef struct
89 /* file name options */
90 gboolean file_case_sens;
91 gboolean file_pattern;
92 gboolean find_recurs;
93 gboolean skip_hidden;
94 gboolean file_all_charsets;
96 /* file content options */
97 gboolean content_use;
98 gboolean content_case_sens;
99 gboolean content_regexp;
100 gboolean content_first_hit;
101 gboolean content_whole_words;
102 gboolean content_all_charsets;
104 /* whether use ignore dirs or not */
105 gboolean ignore_dirs_enable;
106 /* list of directories to be ignored, separated by ':' */
107 char *ignore_dirs;
108 } find_file_options_t;
110 /*** file scope variables ************************************************************************/
112 /* button callbacks */
113 static int start_stop (WButton * button, int action);
114 static int find_do_view_file (WButton * button, int action);
115 static int find_do_edit_file (WButton * button, int action);
117 /* Parsed ignore dirs */
118 static char **find_ignore_dirs = NULL;
120 /* static variables to remember find parameters */
121 static WInput *in_start; /* Start path */
122 static WInput *in_name; /* Filename */
123 static WInput *in_with; /* Text */
124 static WInput *in_ignore;
125 static WLabel *content_label; /* 'Content:' label */
126 static WCheck *file_case_sens_cbox; /* "case sensitive" checkbox */
127 static WCheck *file_pattern_cbox; /* File name is glob or regexp */
128 static WCheck *recursively_cbox;
129 static WCheck *skip_hidden_cbox;
130 static WCheck *content_use_cbox; /* Take into account the Content field */
131 static WCheck *content_case_sens_cbox; /* "case sensitive" checkbox */
132 static WCheck *content_regexp_cbox; /* "find regular expression" checkbox */
133 static WCheck *content_first_hit_cbox; /* "First hit" checkbox" */
134 static WCheck *content_whole_words_cbox; /* "whole words" checkbox */
135 #ifdef HAVE_CHARSET
136 static WCheck *file_all_charsets_cbox;
137 static WCheck *content_all_charsets_cbox;
138 #endif
139 static WCheck *ignore_dirs_cbox;
141 static gboolean running = FALSE; /* nice flag */
142 static char *find_pattern = NULL; /* Pattern to search */
143 static char *content_pattern = NULL; /* pattern to search inside files; if
144 content_regexp_flag is true, it contains the
145 regex pattern, else the search string. */
146 static unsigned long matches; /* Number of matches */
147 static gboolean is_start = FALSE; /* Status of the start/stop toggle button */
148 static char *old_dir = NULL;
150 /* Where did we stop */
151 static int resuming;
152 static int last_line;
153 static int last_pos;
155 static size_t ignore_count = 0;
157 static WDialog *find_dlg; /* The dialog */
158 static WLabel *status_label; /* Finished, Searching etc. */
159 static WLabel *found_num_label; /* Number of found items */
161 /* This keeps track of the directory stack */
162 #if GLIB_CHECK_VERSION (2, 14, 0)
163 static GQueue dir_queue = G_QUEUE_INIT;
164 #else
165 typedef struct dir_stack
167 vfs_path_t *name;
168 struct dir_stack *prev;
169 } dir_stack;
171 static dir_stack *dir_stack_base = 0;
172 #endif /* GLIB_CHECK_VERSION */
174 /* *INDENT-OFF* */
175 static struct
177 int ret_cmd;
178 button_flags_t flags;
179 const char *text;
180 int len; /* length including space and brackets */
181 int x;
182 Widget *button;
183 bcback_fn callback;
184 } fbuts[] =
186 { B_ENTER, DEFPUSH_BUTTON, N_("&Chdir"), 0, 0, NULL, NULL },
187 { B_AGAIN, NORMAL_BUTTON, N_("&Again"), 0, 0, NULL, NULL },
188 { B_STOP, NORMAL_BUTTON, N_("S&uspend"), 0, 0, NULL, start_stop },
189 { B_STOP, NORMAL_BUTTON, N_("Con&tinue"), 0, 0, NULL, NULL },
190 { B_CANCEL, NORMAL_BUTTON, N_("&Quit"), 0, 0, NULL, NULL },
192 { B_PANELIZE, NORMAL_BUTTON, N_("Pane&lize"), 0, 0, NULL, NULL },
193 { B_VIEW, NORMAL_BUTTON, N_("&View - F3"), 0, 0, NULL, find_do_view_file },
194 { B_VIEW, NORMAL_BUTTON, N_("&Edit - F4"), 0, 0, NULL, find_do_edit_file }
196 /* *INDENT-ON* */
198 static const size_t fbuts_num = G_N_ELEMENTS (fbuts);
199 const size_t quit_button = 4; /* index of "Quit" button */
201 static WListbox *find_list; /* Listbox with the file list */
203 static find_file_options_t options = {
204 TRUE, TRUE, TRUE, FALSE, FALSE,
205 FALSE, TRUE, FALSE, FALSE, FALSE, FALSE
208 static char *in_start_dir = INPUT_LAST_TEXT;
210 static mc_search_t *search_file_handle = NULL;
211 static mc_search_t *search_content_handle = NULL;
213 /* --------------------------------------------------------------------------------------------- */
214 /*** file scope functions ************************************************************************/
215 /* --------------------------------------------------------------------------------------------- */
217 /* don't use max macro to avoid double str_term_width1() call in widget length caclulation */
218 #undef max
220 static int
221 max (int a, int b)
223 return (a > b ? a : b);
226 /* --------------------------------------------------------------------------------------------- */
228 static void
229 parse_ignore_dirs (const char *ignore_dirs)
231 size_t r = 0, w = 0; /* read and write iterators */
233 if (!options.ignore_dirs_enable || ignore_dirs == NULL || ignore_dirs[0] == '\0')
234 return;
236 find_ignore_dirs = g_strsplit (ignore_dirs, ":", -1);
238 /* Values like '/foo::/bar: produce holes in list.
239 * Find and remove them */
240 for (; find_ignore_dirs[r] != NULL; r++)
242 if (find_ignore_dirs[r][0] == '\0')
244 /* empty entry -- skip it */
245 g_free (find_ignore_dirs[r]);
246 find_ignore_dirs[r] = NULL;
247 continue;
250 if (r != w)
252 /* copy entry to the previous free array cell */
253 find_ignore_dirs[w] = find_ignore_dirs[r];
254 find_ignore_dirs[r] = NULL;
257 canonicalize_pathname (find_ignore_dirs[w]);
258 if (find_ignore_dirs[w][0] != '\0')
259 w++;
260 else
262 g_free (find_ignore_dirs[w]);
263 find_ignore_dirs[w] = NULL;
267 if (find_ignore_dirs[0] == NULL)
269 g_strfreev (find_ignore_dirs);
270 find_ignore_dirs = NULL;
274 /* --------------------------------------------------------------------------------------------- */
276 static void
277 find_load_options (void)
279 static gboolean loaded = FALSE;
281 if (loaded)
282 return;
284 loaded = TRUE;
286 options.file_case_sens =
287 mc_config_get_bool (mc_main_config, "FindFile", "file_case_sens", TRUE);
288 options.file_pattern =
289 mc_config_get_bool (mc_main_config, "FindFile", "file_shell_pattern", TRUE);
290 options.find_recurs = mc_config_get_bool (mc_main_config, "FindFile", "file_find_recurs", TRUE);
291 options.skip_hidden =
292 mc_config_get_bool (mc_main_config, "FindFile", "file_skip_hidden", FALSE);
293 options.file_all_charsets =
294 mc_config_get_bool (mc_main_config, "FindFile", "file_all_charsets", FALSE);
295 options.content_use = mc_config_get_bool (mc_main_config, "FindFile", "content_use", TRUE);
296 options.content_case_sens =
297 mc_config_get_bool (mc_main_config, "FindFile", "content_case_sens", TRUE);
298 options.content_regexp =
299 mc_config_get_bool (mc_main_config, "FindFile", "content_regexp", FALSE);
300 options.content_first_hit =
301 mc_config_get_bool (mc_main_config, "FindFile", "content_first_hit", FALSE);
302 options.content_whole_words =
303 mc_config_get_bool (mc_main_config, "FindFile", "content_whole_words", FALSE);
304 options.content_all_charsets =
305 mc_config_get_bool (mc_main_config, "FindFile", "content_all_charsets", FALSE);
306 options.ignore_dirs_enable =
307 mc_config_get_bool (mc_main_config, "FindFile", "ignore_dirs_enable", TRUE);
308 options.ignore_dirs = mc_config_get_string (mc_main_config, "FindFile", "ignore_dirs", "");
310 if (options.ignore_dirs[0] == '\0')
312 g_free (options.ignore_dirs);
313 options.ignore_dirs = NULL;
317 /* --------------------------------------------------------------------------------------------- */
319 static void
320 find_save_options (void)
322 mc_config_set_bool (mc_main_config, "FindFile", "file_case_sens", options.file_case_sens);
323 mc_config_set_bool (mc_main_config, "FindFile", "file_shell_pattern", options.file_pattern);
324 mc_config_set_bool (mc_main_config, "FindFile", "file_find_recurs", options.find_recurs);
325 mc_config_set_bool (mc_main_config, "FindFile", "file_skip_hidden", options.skip_hidden);
326 mc_config_set_bool (mc_main_config, "FindFile", "file_all_charsets", options.file_all_charsets);
327 mc_config_set_bool (mc_main_config, "FindFile", "content_use", options.content_use);
328 mc_config_set_bool (mc_main_config, "FindFile", "content_case_sens", options.content_case_sens);
329 mc_config_set_bool (mc_main_config, "FindFile", "content_regexp", options.content_regexp);
330 mc_config_set_bool (mc_main_config, "FindFile", "content_first_hit", options.content_first_hit);
331 mc_config_set_bool (mc_main_config, "FindFile", "content_whole_words",
332 options.content_whole_words);
333 mc_config_set_bool (mc_main_config, "FindFile", "content_all_charsets",
334 options.content_all_charsets);
335 mc_config_set_bool (mc_main_config, "FindFile", "ignore_dirs_enable",
336 options.ignore_dirs_enable);
337 mc_config_set_string (mc_main_config, "FindFile", "ignore_dirs", options.ignore_dirs);
340 /* --------------------------------------------------------------------------------------------- */
342 static inline char *
343 add_to_list (const char *text, void *data)
345 return listbox_add_item (find_list, LISTBOX_APPEND_AT_END, 0, text, data);
348 /* --------------------------------------------------------------------------------------------- */
350 static inline void
351 stop_idle (void *data)
353 widget_want_idle (WIDGET (data), FALSE);
356 /* --------------------------------------------------------------------------------------------- */
358 static inline void
359 status_update (const char *text)
361 label_set_text (status_label, text);
364 /* --------------------------------------------------------------------------------------------- */
366 static void
367 found_num_update (void)
369 char buffer[BUF_TINY];
370 g_snprintf (buffer, sizeof (buffer), _("Found: %ld"), matches);
371 label_set_text (found_num_label, buffer);
374 /* --------------------------------------------------------------------------------------------- */
376 static void
377 get_list_info (char **file, char **dir)
379 listbox_get_current (find_list, file, (void **) dir);
382 /* --------------------------------------------------------------------------------------------- */
383 /** check regular expression */
385 static gboolean
386 find_check_regexp (const char *r)
388 mc_search_t *search;
389 gboolean regexp_ok = FALSE;
391 search = mc_search_new (r, -1);
393 if (search != NULL)
395 search->search_type = MC_SEARCH_T_REGEX;
396 regexp_ok = mc_search_prepare (search);
397 mc_search_free (search);
400 return regexp_ok;
403 /* --------------------------------------------------------------------------------------------- */
405 * Callback for the parameter dialog.
406 * Validate regex, prevent closing the dialog if it's invalid.
409 static cb_ret_t
410 find_parm_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
412 WDialog *h = DIALOG (w);
414 switch (msg)
416 case MSG_ACTION:
417 if (sender == WIDGET (content_use_cbox))
419 gboolean disable = !(content_use_cbox->state & C_BOOL);
421 widget_disable (WIDGET (in_with), disable);
422 widget_disable (WIDGET (content_first_hit_cbox), disable);
423 widget_disable (WIDGET (content_regexp_cbox), disable);
424 widget_disable (WIDGET (content_case_sens_cbox), disable);
425 #ifdef HAVE_CHARSET
426 widget_disable (WIDGET (content_all_charsets_cbox), disable);
427 #endif
428 widget_disable (WIDGET (content_whole_words_cbox), disable);
430 return MSG_HANDLED;
433 if (sender == WIDGET (ignore_dirs_cbox))
435 gboolean disable = !(ignore_dirs_cbox->state & C_BOOL);
437 widget_disable (WIDGET (in_ignore), disable);
439 return MSG_HANDLED;
442 return MSG_NOT_HANDLED;
445 case MSG_VALIDATE:
446 if (h->ret_value != B_ENTER)
447 return MSG_HANDLED;
449 /* check filename regexp */
450 if (!(file_pattern_cbox->state & C_BOOL)
451 && (in_name->buffer[0] != '\0') && !find_check_regexp (in_name->buffer))
453 h->state = DLG_ACTIVE; /* Don't stop the dialog */
454 message (D_ERROR, MSG_ERROR, _("Malformed regular expression"));
455 dlg_select_widget (in_name);
456 return MSG_HANDLED;
459 /* check content regexp */
460 if ((content_use_cbox->state & C_BOOL) && (content_regexp_cbox->state & C_BOOL)
461 && (in_with->buffer[0] != '\0') && !find_check_regexp (in_with->buffer))
463 h->state = DLG_ACTIVE; /* Don't stop the dialog */
464 message (D_ERROR, MSG_ERROR, _("Malformed regular expression"));
465 dlg_select_widget (in_with);
466 return MSG_HANDLED;
469 return MSG_HANDLED;
471 default:
472 return dlg_default_callback (w, sender, msg, parm, data);
476 /* --------------------------------------------------------------------------------------------- */
478 * find_parameters: gets information from the user
480 * If the return value is TRUE, then the following holds:
482 * start_dir, ignore_dirs, pattern and content contain the information provided by the user.
483 * They are newly allocated strings and must be freed when uneeded.
485 * start_dir_len is -1 when user entered an absolute path, otherwise it is a length
486 * of start_dir (which is absolute). It is used to get a relative pats of find results.
489 static gboolean
490 find_parameters (char **start_dir, ssize_t * start_dir_len,
491 char **ignore_dirs, char **pattern, char **content)
493 /* Size of the find parameters window */
494 #ifdef HAVE_CHARSET
495 const int lines = 19;
496 #else
497 const int lines = 18;
498 #endif
499 int cols = 68;
501 gboolean return_value;
503 /* file name */
504 const char *file_name_label = N_("File name:");
505 const char *file_recurs_label = N_("&Find recursively");
506 const char *file_pattern_label = N_("&Using shell patterns");
507 #ifdef HAVE_CHARSET
508 const char *file_all_charsets_label = N_("&All charsets");
509 #endif
510 const char *file_case_label = N_("Cas&e sensitive");
511 const char *file_skip_hidden_label = N_("S&kip hidden");
513 /* file content */
514 const char *content_content_label = N_("Content:");
515 const char *content_use_label = N_("Sea&rch for content");
516 const char *content_regexp_label = N_("Re&gular expression");
517 const char *content_case_label = N_("Case sens&itive");
518 #ifdef HAVE_CHARSET
519 const char *content_all_charsets_label = N_("A&ll charsets");
520 #endif
521 const char *content_whole_words_label = N_("&Whole words");
522 const char *content_first_hit_label = N_("Fir&st hit");
524 const char *buts[] = { N_("&Tree"), N_("&OK"), N_("&Cancel") };
526 /* button lengths */
527 int b0, b1, b2, b12;
528 int y1, y2, x1, x2;
529 /* column width */
530 int cw;
532 gboolean disable;
534 #ifdef ENABLE_NLS
536 size_t i;
538 file_name_label = _(file_name_label);
539 file_recurs_label = _(file_recurs_label);
540 file_pattern_label = _(file_pattern_label);
541 #ifdef HAVE_CHARSET
542 file_all_charsets_label = _(file_all_charsets_label);
543 #endif
544 file_case_label = _(file_case_label);
545 file_skip_hidden_label = _(file_skip_hidden_label);
547 /* file content */
548 content_content_label = _(content_content_label);
549 content_use_label = _(content_use_label);
550 content_regexp_label = _(content_regexp_label);
551 content_case_label = _(content_case_label);
552 #ifdef HAVE_CHARSET
553 content_all_charsets_label = _(content_all_charsets_label);
554 #endif
555 content_whole_words_label = _(content_whole_words_label);
556 content_first_hit_label = _(content_first_hit_label);
558 for (i = 0; i < G_N_ELEMENTS (buts); i++)
559 buts[i] = _(buts[i]);
561 #endif /* ENABLE_NLS */
563 /* caclulate dialog width */
565 /* widget widths */
566 cw = str_term_width1 (file_name_label);
567 cw = max (cw, str_term_width1 (file_recurs_label) + 4);
568 cw = max (cw, str_term_width1 (file_pattern_label) + 4);
569 #ifdef HAVE_CHARSET
570 cw = max (cw, str_term_width1 (file_all_charsets_label) + 4);
571 #endif
572 cw = max (cw, str_term_width1 (file_case_label) + 4);
573 cw = max (cw, str_term_width1 (file_skip_hidden_label) + 4);
575 cw = max (cw, str_term_width1 (content_content_label) + 4);
576 cw = max (cw, str_term_width1 (content_use_label) + 4);
577 cw = max (cw, str_term_width1 (content_regexp_label) + 4);
578 cw = max (cw, str_term_width1 (content_case_label) + 4);
579 #ifdef HAVE_CHARSET
580 cw = max (cw, str_term_width1 (content_all_charsets_label) + 4);
581 #endif
582 cw = max (cw, str_term_width1 (content_whole_words_label) + 4);
583 cw = max (cw, str_term_width1 (content_first_hit_label) + 4);
585 /* button width */
586 b0 = str_term_width1 (buts[0]) + 3;
587 b1 = str_term_width1 (buts[1]) + 5; /* default button */
588 b2 = str_term_width1 (buts[2]) + 3;
589 b12 = b1 + b2 + 1;
591 cols = max (cols, max (b12, cw * 2 + 1) + 6);
593 find_load_options ();
595 if (in_start_dir == NULL)
596 in_start_dir = g_strdup (".");
598 disable = !options.content_use;
600 find_dlg =
601 create_dlg (TRUE, 0, 0, lines, cols, dialog_colors, find_parm_callback, NULL, "[Find File]",
602 _("Find File"), DLG_CENTER);
604 x1 = 3;
605 x2 = cols / 2 + 1;
606 cw = (cols - 7) / 2;
607 y1 = 2;
609 add_widget (find_dlg, label_new (y1++, x1, _("Start at:")));
610 in_start =
611 input_new (y1, x1, input_get_default_colors (), cols - b0 - 7, in_start_dir, "start",
612 INPUT_COMPLETE_CD | INPUT_COMPLETE_FILENAMES);
613 add_widget (find_dlg, in_start);
615 add_widget (find_dlg, button_new (y1++, cols - b0 - 3, B_TREE, NORMAL_BUTTON, buts[0], NULL));
617 ignore_dirs_cbox =
618 check_new (y1++, x1, options.ignore_dirs_enable, _("Ena&ble ignore directories:"));
619 add_widget (find_dlg, ignore_dirs_cbox);
621 in_ignore =
622 input_new (y1++, x1, input_get_default_colors (), cols - 6,
623 options.ignore_dirs != NULL ? options.ignore_dirs : "", "ignoredirs",
624 INPUT_COMPLETE_CD | INPUT_COMPLETE_FILENAMES);
625 widget_disable (WIDGET (in_ignore), !options.ignore_dirs_enable);
626 add_widget (find_dlg, in_ignore);
628 add_widget (find_dlg, hline_new (y1++, -1, -1));
630 y2 = y1;
632 /* Start 1st column */
633 add_widget (find_dlg, label_new (y1++, x1, file_name_label));
634 in_name =
635 input_new (y1++, x1, input_get_default_colors (), cw, INPUT_LAST_TEXT, "name",
636 INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_CD);
637 add_widget (find_dlg, in_name);
639 /* Start 2nd column */
640 content_label = label_new (y2++, x2, content_content_label);
641 add_widget (find_dlg, content_label);
642 in_with =
643 input_new (y2++, x2, input_get_default_colors (), cw, INPUT_LAST_TEXT,
644 MC_HISTORY_SHARED_SEARCH, INPUT_COMPLETE_NONE);
645 in_with->label = content_label;
646 widget_disable (WIDGET (in_with), disable);
647 add_widget (find_dlg, in_with);
649 content_use_cbox = check_new (y2++, x2, options.content_use, content_use_label);
650 add_widget (find_dlg, content_use_cbox);
652 /* Continue 1st column */
653 recursively_cbox = check_new (y1++, x1, options.find_recurs, file_recurs_label);
654 add_widget (find_dlg, recursively_cbox);
656 file_pattern_cbox = check_new (y1++, x1, options.file_pattern, file_pattern_label);
657 add_widget (find_dlg, file_pattern_cbox);
659 file_case_sens_cbox = check_new (y1++, x1, options.file_case_sens, file_case_label);
660 add_widget (find_dlg, file_case_sens_cbox);
662 #ifdef HAVE_CHARSET
663 file_all_charsets_cbox =
664 check_new (y1++, x1, options.file_all_charsets, file_all_charsets_label);
665 add_widget (find_dlg, file_all_charsets_cbox);
666 #endif
668 skip_hidden_cbox = check_new (y1++, x1, options.skip_hidden, file_skip_hidden_label);
669 add_widget (find_dlg, skip_hidden_cbox);
671 /* Continue 2nd column */
672 content_regexp_cbox = check_new (y2++, x2, options.content_regexp, content_regexp_label);
673 widget_disable (WIDGET (content_regexp_cbox), disable);
674 add_widget (find_dlg, content_regexp_cbox);
676 content_case_sens_cbox = check_new (y2++, x2, options.content_case_sens, content_case_label);
677 widget_disable (WIDGET (content_case_sens_cbox), disable);
678 add_widget (find_dlg, content_case_sens_cbox);
680 #ifdef HAVE_CHARSET
681 content_all_charsets_cbox =
682 check_new (y2++, x2, options.content_all_charsets, content_all_charsets_label);
683 widget_disable (WIDGET (content_all_charsets_cbox), disable);
684 add_widget (find_dlg, content_all_charsets_cbox);
685 #endif
687 content_whole_words_cbox =
688 check_new (y2++, x2, options.content_whole_words, content_whole_words_label);
689 widget_disable (WIDGET (content_whole_words_cbox), disable);
690 add_widget (find_dlg, content_whole_words_cbox);
692 content_first_hit_cbox =
693 check_new (y2++, x2, options.content_first_hit, content_first_hit_label);
694 widget_disable (WIDGET (content_first_hit_cbox), disable);
695 add_widget (find_dlg, content_first_hit_cbox);
697 /* buttons */
698 y1 = max (y1, y2);
699 x1 = (cols - b12) / 2;
700 add_widget (find_dlg, hline_new (y1++, -1, -1));
701 add_widget (find_dlg, button_new (y1, x1, B_ENTER, DEFPUSH_BUTTON, buts[1], NULL));
702 add_widget (find_dlg, button_new (y1, x1 + b1 + 1, B_CANCEL, NORMAL_BUTTON, buts[2], NULL));
704 find_par_start:
705 dlg_select_widget (in_name);
707 switch (run_dlg (find_dlg))
709 case B_CANCEL:
710 return_value = FALSE;
711 break;
713 case B_TREE:
715 char *temp_dir;
717 temp_dir = in_start->buffer;
718 if ((temp_dir[0] == '\0') || ((temp_dir[0] == '.') && (temp_dir[1] == '\0')))
719 temp_dir = vfs_path_to_str (current_panel->cwd_vpath);
720 else
721 temp_dir = g_strdup (temp_dir);
723 if (in_start_dir != INPUT_LAST_TEXT)
724 g_free (in_start_dir);
725 in_start_dir = tree_box (temp_dir);
726 if (in_start_dir == NULL)
727 in_start_dir = temp_dir;
728 else
729 g_free (temp_dir);
731 input_assign_text (in_start, in_start_dir);
733 /* Warning: Dreadful goto */
734 goto find_par_start;
737 default:
739 char *s;
741 #ifdef HAVE_CHARSET
742 options.file_all_charsets = file_all_charsets_cbox->state & C_BOOL;
743 options.content_all_charsets = content_all_charsets_cbox->state & C_BOOL;
744 #endif
745 options.content_use = content_use_cbox->state & C_BOOL;
746 options.content_case_sens = content_case_sens_cbox->state & C_BOOL;
747 options.content_regexp = content_regexp_cbox->state & C_BOOL;
748 options.content_first_hit = content_first_hit_cbox->state & C_BOOL;
749 options.content_whole_words = content_whole_words_cbox->state & C_BOOL;
750 options.find_recurs = recursively_cbox->state & C_BOOL;
751 options.file_pattern = file_pattern_cbox->state & C_BOOL;
752 options.file_case_sens = file_case_sens_cbox->state & C_BOOL;
753 options.skip_hidden = skip_hidden_cbox->state & C_BOOL;
754 options.ignore_dirs_enable = ignore_dirs_cbox->state & C_BOOL;
755 g_free (options.ignore_dirs);
756 options.ignore_dirs = g_strdup (in_ignore->buffer);
758 *content = (options.content_use && in_with->buffer[0] != '\0')
759 ? g_strdup (in_with->buffer) : NULL;
760 *start_dir = in_start->buffer[0] != '\0' ? in_start->buffer : (char *) ".";
761 *pattern = g_strdup (in_name->buffer);
762 if (in_start_dir != INPUT_LAST_TEXT)
763 g_free (in_start_dir);
764 in_start_dir = g_strdup (*start_dir);
766 s = tilde_expand (*start_dir);
767 canonicalize_pathname (s);
769 if (s[0] == '.' && s[1] == '\0')
771 *start_dir = vfs_path_to_str (current_panel->cwd_vpath);
772 /* FIXME: is current_panel->cwd_vpath canonicalized? */
773 /* relative paths will be used in panelization */
774 *start_dir_len = (ssize_t) strlen (*start_dir);
775 g_free (s);
777 else if (g_path_is_absolute (s))
779 *start_dir = s;
780 *start_dir_len = -1;
782 else
784 /* relative paths will be used in panelization */
785 char *cwd_str;
787 cwd_str = vfs_path_to_str (current_panel->cwd_vpath);
788 *start_dir = mc_build_filename (cwd_str, s, (char *) NULL);
789 *start_dir_len = (ssize_t) strlen (cwd_str);
790 g_free (cwd_str);
791 g_free (s);
794 if (!options.ignore_dirs_enable || in_ignore->buffer[0] == '\0'
795 || (in_ignore->buffer[0] == '.' && in_ignore->buffer[1] == '\0'))
796 *ignore_dirs = NULL;
797 else
798 *ignore_dirs = g_strdup (in_ignore->buffer);
800 find_save_options ();
802 return_value = TRUE;
806 destroy_dlg (find_dlg);
808 return return_value;
811 /* --------------------------------------------------------------------------------------------- */
813 #if GLIB_CHECK_VERSION (2, 14, 0)
814 static inline void
815 push_directory (const vfs_path_t * dir)
817 g_queue_push_head (&dir_queue, (void *) dir);
820 /* --------------------------------------------------------------------------------------------- */
822 static inline vfs_path_t *
823 pop_directory (void)
825 return (vfs_path_t *) g_queue_pop_tail (&dir_queue);
828 /* --------------------------------------------------------------------------------------------- */
829 /** Remove all the items from the stack */
831 static void
832 clear_stack (void)
834 g_queue_foreach (&dir_queue, (GFunc) vfs_path_free, NULL);
835 g_queue_clear (&dir_queue);
838 /* --------------------------------------------------------------------------------------------- */
840 #else /* GLIB_CHECK_VERSION */
841 static void
842 push_directory (const vfs_path_t * dir)
844 dir_stack *new;
846 new = g_new (dir_stack, 1);
847 new->name = (vfs_path_t *) dir;
848 new->prev = dir_stack_base;
849 dir_stack_base = new;
852 /* --------------------------------------------------------------------------------------------- */
854 static vfs_path_t *
855 pop_directory (void)
857 vfs_path_t *name = NULL;
859 if (dir_stack_base != NULL)
861 dir_stack *next;
862 name = dir_stack_base->name;
863 next = dir_stack_base->prev;
864 g_free (dir_stack_base);
865 dir_stack_base = next;
868 return name;
871 /* --------------------------------------------------------------------------------------------- */
872 /** Remove all the items from the stack */
874 static void
875 clear_stack (void)
877 vfs_path_t *dir = NULL;
879 while ((dir = pop_directory ()) != NULL)
880 vfs_path_free (dir);
882 #endif /* GLIB_CHECK_VERSION */
884 /* --------------------------------------------------------------------------------------------- */
886 static void
887 insert_file (const char *dir, const char *file)
889 char *tmp_name = NULL;
890 static char *dirname = NULL;
892 while (dir[0] == PATH_SEP && dir[1] == PATH_SEP)
893 dir++;
895 if (old_dir)
897 if (strcmp (old_dir, dir))
899 g_free (old_dir);
900 old_dir = g_strdup (dir);
901 dirname = add_to_list (dir, NULL);
904 else
906 old_dir = g_strdup (dir);
907 dirname = add_to_list (dir, NULL);
910 tmp_name = g_strdup_printf (" %s", file);
911 add_to_list (tmp_name, dirname);
912 g_free (tmp_name);
915 /* --------------------------------------------------------------------------------------------- */
917 static void
918 find_add_match (const char *dir, const char *file)
920 insert_file (dir, file);
922 /* Don't scroll */
923 if (matches == 0)
924 listbox_select_first (find_list);
925 send_message (find_list, NULL, MSG_DRAW, 0, NULL);
927 matches++;
928 found_num_update ();
931 /* --------------------------------------------------------------------------------------------- */
933 * get_line_at:
935 * Returns malloced null-terminated line from file file_fd.
936 * Input is buffered in buf_size long buffer.
937 * Current pos in buf is stored in pos.
938 * n_read - number of read chars.
939 * has_newline - is there newline ?
942 static char *
943 get_line_at (int file_fd, char *buf, int buf_size, int *pos, int *n_read, gboolean * has_newline)
945 char *buffer = NULL;
946 int buffer_size = 0;
947 char ch = 0;
948 int i = 0;
950 while (TRUE)
952 if (*pos >= *n_read)
954 *pos = 0;
955 *n_read = mc_read (file_fd, buf, buf_size);
956 if (*n_read <= 0)
957 break;
960 ch = buf[(*pos)++];
961 if (ch == '\0')
963 /* skip possible leading zero(s) */
964 if (i == 0)
965 continue;
966 break;
969 if (i >= buffer_size - 1)
970 buffer = g_realloc (buffer, buffer_size += 80);
972 /* Strip newline */
973 if (ch == '\n')
974 break;
976 buffer[i++] = ch;
979 *has_newline = (ch != '\0');
981 if (buffer != NULL)
982 buffer[i] = '\0';
984 return buffer;
987 /* --------------------------------------------------------------------------------------------- */
989 static FindProgressStatus
990 check_find_events (WDialog * h)
992 Gpm_Event event;
993 int c;
995 event.x = -1;
996 c = tty_get_event (&event, h->mouse_status == MOU_REPEAT, FALSE);
997 if (c != EV_NONE)
999 dlg_process_event (h, c, &event);
1000 if (h->ret_value == B_ENTER
1001 || h->ret_value == B_CANCEL || h->ret_value == B_AGAIN || h->ret_value == B_PANELIZE)
1003 /* dialog terminated */
1004 return FIND_ABORT;
1006 if ((WIDGET (h)->options & W_WANT_IDLE) == 0)
1008 /* searching suspended */
1009 return FIND_SUSPEND;
1013 return FIND_CONT;
1016 /* --------------------------------------------------------------------------------------------- */
1018 * search_content:
1020 * Search the content_pattern string in the DIRECTORY/FILE.
1021 * It will add the found entries to the find listbox.
1023 * returns FALSE if do_search should look for another file
1024 * TRUE if do_search should exit and proceed to the event handler
1027 static gboolean
1028 search_content (WDialog * h, const char *directory, const char *filename)
1030 struct stat s;
1031 char buffer[BUF_4K];
1032 int file_fd;
1033 gboolean ret_val = FALSE;
1034 vfs_path_t *vpath;
1036 vpath = vfs_path_build_filename (directory, filename, (char *) NULL);
1038 if (mc_stat (vpath, &s) != 0 || !S_ISREG (s.st_mode))
1040 vfs_path_free (vpath);
1041 return FALSE;
1044 file_fd = mc_open (vpath, O_RDONLY);
1045 vfs_path_free (vpath);
1047 if (file_fd == -1)
1048 return FALSE;
1050 g_snprintf (buffer, sizeof (buffer), _("Grepping in %s"), filename);
1051 status_update (str_trunc (buffer, WIDGET (h)->cols - 8));
1053 mc_refresh ();
1055 tty_enable_interrupt_key ();
1056 tty_got_interrupt ();
1059 int line = 1;
1060 int pos = 0;
1061 int n_read = 0;
1062 gboolean has_newline;
1063 char *p = NULL;
1064 gboolean found = FALSE;
1065 gsize found_len;
1066 char result[BUF_MEDIUM];
1068 if (resuming)
1070 /* We've been previously suspended, start from the previous position */
1071 resuming = 0;
1072 line = last_line;
1073 pos = last_pos;
1075 while (!ret_val
1076 && (p = get_line_at (file_fd, buffer, sizeof (buffer),
1077 &pos, &n_read, &has_newline)) != NULL)
1079 if (!found /* Search in binary line once */
1080 && mc_search_run (search_content_handle,
1081 (const void *) p, 0, strlen (p), &found_len))
1083 g_snprintf (result, sizeof (result), "%d:%s", line, filename);
1084 find_add_match (directory, result);
1085 found = TRUE;
1087 g_free (p);
1089 if (found && options.content_first_hit)
1090 break;
1092 if (has_newline)
1094 found = FALSE;
1095 line++;
1098 if ((line & 0xff) == 0)
1100 FindProgressStatus res;
1101 res = check_find_events (h);
1102 switch (res)
1104 case FIND_ABORT:
1105 stop_idle (h);
1106 ret_val = TRUE;
1107 break;
1108 case FIND_SUSPEND:
1109 resuming = 1;
1110 last_line = line;
1111 last_pos = pos;
1112 ret_val = TRUE;
1113 break;
1114 default:
1115 break;
1120 tty_disable_interrupt_key ();
1121 mc_close (file_fd);
1122 return ret_val;
1125 /* --------------------------------------------------------------------------------------------- */
1128 If dir is absolute, this means we're within dir and searching file here.
1129 If dir is relative, this means we're going to add dir to the directory stack.
1131 static gboolean
1132 find_ignore_dir_search (const char *dir)
1134 if (find_ignore_dirs != NULL)
1136 const size_t dlen = strlen (dir);
1137 const unsigned char dabs = g_path_is_absolute (dir) ? 1 : 0;
1139 char **ignore_dir;
1141 for (ignore_dir = find_ignore_dirs; *ignore_dir != NULL; ignore_dir++)
1143 const size_t ilen = strlen (*ignore_dir);
1144 const unsigned char iabs = g_path_is_absolute (*ignore_dir) ? 2 : 0;
1146 /* ignore dir is too long -- skip it */
1147 if (dlen < ilen)
1148 continue;
1150 /* handle absolute and relative paths */
1151 switch (iabs | dabs)
1153 case 0: /* both paths are relative */
1154 case 3: /* both paths are abolute */
1155 /* if ignore dir is not a path of dir -- skip it */
1156 if (strncmp (dir, *ignore_dir, ilen) == 0)
1158 /* be sure that ignore dir is not a part of dir like:
1159 ignore dir is "h", dir is "home" */
1160 if (dir[ilen] == '\0' || dir[ilen] == PATH_SEP)
1161 return TRUE;
1163 break;
1164 case 1: /* dir is absolute, ignore_dir is relative */
1166 char *d;
1168 d = strstr (dir, *ignore_dir);
1169 if (d != NULL && d[-1] == PATH_SEP && (d[ilen] == '\0' || d[ilen] == PATH_SEP))
1170 return TRUE;
1172 break;
1173 case 2: /* dir is relative, ignore_dir is absolute */
1174 /* FIXME: skip this case */
1175 break;
1176 default: /* this cannot occurs */
1177 return FALSE;
1182 return FALSE;
1185 /* --------------------------------------------------------------------------------------------- */
1187 static void
1188 find_rotate_dash (const WDialog * h, gboolean finish)
1190 static const char rotating_dash[] = "|/-\\";
1191 static unsigned int pos = 0;
1193 if (verbose)
1195 const Widget *w = WIDGET (h);
1197 pos = (pos + 1) % 4;
1198 tty_setcolor (h->color[DLG_COLOR_NORMAL]);
1199 widget_move (h, w->lines - 7, w->cols - 4);
1200 tty_print_char (finish ? ' ' : rotating_dash[pos]);
1201 mc_refresh ();
1205 /* --------------------------------------------------------------------------------------------- */
1207 static int
1208 do_search (WDialog * h)
1210 static struct dirent *dp = NULL;
1211 static DIR *dirp = NULL;
1212 static char *directory = NULL;
1213 struct stat tmp_stat;
1214 static int subdirs_left = 0;
1215 gsize bytes_found;
1216 unsigned short count;
1218 if (h == NULL)
1219 { /* someone forces me to close dirp */
1220 if (dirp != NULL)
1222 mc_closedir (dirp);
1223 dirp = NULL;
1225 g_free (directory);
1226 directory = NULL;
1227 dp = NULL;
1228 return 1;
1231 for (count = 0; count < 32; count++)
1233 while (dp == NULL)
1235 if (dirp != NULL)
1237 mc_closedir (dirp);
1238 dirp = NULL;
1241 while (dirp == NULL)
1243 vfs_path_t *tmp_vpath = NULL;
1245 tty_setcolor (REVERSE_COLOR);
1247 while (TRUE)
1249 tmp_vpath = pop_directory ();
1250 if (tmp_vpath == NULL)
1252 running = FALSE;
1253 if (ignore_count == 0)
1254 status_update (_("Finished"));
1255 else
1257 char msg[BUF_SMALL];
1258 g_snprintf (msg, sizeof (msg),
1259 ngettext ("Finished (ignored %zd directory)",
1260 "Finished (ignored %zd directories)",
1261 ignore_count), ignore_count);
1262 status_update (msg);
1264 find_rotate_dash (h, TRUE);
1265 stop_idle (h);
1266 return 0;
1269 /* handle absolute ignore dirs here */
1271 char *tmp;
1272 gboolean ok;
1274 tmp = vfs_path_to_str (tmp_vpath);
1275 ok = find_ignore_dir_search (tmp);
1276 g_free (tmp);
1277 if (!ok)
1278 break;
1281 vfs_path_free (tmp_vpath);
1282 ignore_count++;
1285 g_free (directory);
1286 directory = vfs_path_to_str (tmp_vpath);
1288 if (verbose)
1290 char buffer[BUF_MEDIUM];
1292 g_snprintf (buffer, sizeof (buffer), _("Searching %s"), directory);
1293 status_update (str_trunc (directory, WIDGET (h)->cols - 8));
1295 /* mc_stat should not be called after mc_opendir
1296 because vfs_s_opendir modifies the st_nlink
1298 if (mc_stat (tmp_vpath, &tmp_stat) == 0)
1299 subdirs_left = tmp_stat.st_nlink - 2;
1300 else
1301 subdirs_left = 0;
1303 dirp = mc_opendir (tmp_vpath);
1304 vfs_path_free (tmp_vpath);
1305 } /* while (!dirp) */
1307 /* skip invalid filenames */
1308 while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1310 } /* while (!dp) */
1312 if (strcmp (dp->d_name, ".") == 0 || strcmp (dp->d_name, "..") == 0)
1314 /* skip invalid filenames */
1315 while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1318 return 1;
1321 if (!(options.skip_hidden && (dp->d_name[0] == '.')))
1323 gboolean search_ok;
1325 if ((subdirs_left != 0) && options.find_recurs && (directory != NULL))
1326 { /* Can directory be NULL ? */
1327 /* handle relative ignore dirs here */
1328 if (options.ignore_dirs_enable && find_ignore_dir_search (dp->d_name))
1329 ignore_count++;
1330 else
1332 vfs_path_t *tmp_vpath;
1334 tmp_vpath = vfs_path_build_filename (directory, dp->d_name, (char *) NULL);
1336 if (mc_lstat (tmp_vpath, &tmp_stat) == 0 && S_ISDIR (tmp_stat.st_mode))
1338 push_directory (tmp_vpath);
1339 subdirs_left--;
1341 else
1342 vfs_path_free (tmp_vpath);
1346 search_ok = mc_search_run (search_file_handle, dp->d_name,
1347 0, strlen (dp->d_name), &bytes_found);
1349 if (search_ok)
1351 if (content_pattern == NULL)
1352 find_add_match (directory, dp->d_name);
1353 else if (search_content (h, directory, dp->d_name))
1354 return 1;
1358 /* skip invalid filenames */
1359 while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1361 } /* for */
1363 find_rotate_dash (h, FALSE);
1365 return 1;
1368 /* --------------------------------------------------------------------------------------------- */
1370 static void
1371 init_find_vars (void)
1373 g_free (old_dir);
1374 old_dir = NULL;
1375 matches = 0;
1376 ignore_count = 0;
1378 /* Remove all the items from the stack */
1379 clear_stack ();
1381 g_strfreev (find_ignore_dirs);
1382 find_ignore_dirs = NULL;
1385 /* --------------------------------------------------------------------------------------------- */
1387 static void
1388 find_do_view_edit (int unparsed_view, int edit, char *dir, char *file)
1390 char *fullname = NULL;
1391 const char *filename = NULL;
1392 int line;
1393 vfs_path_t *fullname_vpath;
1395 if (content_pattern != NULL)
1397 filename = strchr (file + 4, ':') + 1;
1398 line = atoi (file + 4);
1400 else
1402 filename = file + 4;
1403 line = 0;
1406 fullname_vpath = vfs_path_build_filename (dir, filename, (char *) NULL);
1407 if (edit)
1408 do_edit_at_line (fullname_vpath, use_internal_edit, line);
1409 else
1410 view_file_at_line (fullname_vpath, unparsed_view, use_internal_view, line);
1411 vfs_path_free (fullname_vpath);
1412 g_free (fullname);
1415 /* --------------------------------------------------------------------------------------------- */
1417 static cb_ret_t
1418 view_edit_currently_selected_file (int unparsed_view, int edit)
1420 char *dir = NULL;
1421 char *text = NULL;
1423 listbox_get_current (find_list, &text, (void **) &dir);
1425 if ((text == NULL) || (dir == NULL))
1426 return MSG_NOT_HANDLED;
1428 find_do_view_edit (unparsed_view, edit, dir, text);
1429 return MSG_HANDLED;
1432 /* --------------------------------------------------------------------------------------------- */
1434 static void
1435 find_calc_button_locations (const WDialog * h, gboolean all_buttons)
1437 const int cols = WIDGET (h)->cols;
1439 int l1, l2;
1441 l1 = fbuts[0].len + fbuts[1].len + fbuts[is_start ? 3 : 2].len + fbuts[4].len + 3;
1442 l2 = fbuts[5].len + fbuts[6].len + fbuts[7].len + 2;
1444 fbuts[0].x = (cols - l1) / 2;
1445 fbuts[1].x = fbuts[0].x + fbuts[0].len + 1;
1446 fbuts[2].x = fbuts[1].x + fbuts[1].len + 1;
1447 fbuts[3].x = fbuts[2].x;
1448 fbuts[4].x = fbuts[2].x + fbuts[is_start ? 3 : 2].len + 1;
1450 if (all_buttons)
1452 fbuts[5].x = (cols - l2) / 2;
1453 fbuts[6].x = fbuts[5].x + fbuts[5].len + 1;
1454 fbuts[7].x = fbuts[6].x + fbuts[6].len + 1;
1458 /* --------------------------------------------------------------------------------------------- */
1460 static void
1461 find_relocate_buttons (const WDialog * h, gboolean all_buttons)
1463 size_t i;
1465 find_calc_button_locations (h, all_buttons);
1467 for (i = 0; i < fbuts_num; i++)
1468 fbuts[i].button->x = WIDGET (h)->x + fbuts[i].x;
1471 /* --------------------------------------------------------------------------------------------- */
1473 static cb_ret_t
1474 find_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
1476 WDialog *h = DIALOG (w);
1478 switch (msg)
1480 case MSG_KEY:
1481 if (parm == KEY_F (3) || parm == KEY_F (13))
1483 int unparsed_view = (parm == KEY_F (13));
1484 return view_edit_currently_selected_file (unparsed_view, 0);
1486 if (parm == KEY_F (4))
1487 return view_edit_currently_selected_file (0, 1);
1488 return MSG_NOT_HANDLED;
1490 case MSG_RESIZE:
1491 dlg_set_size (h, LINES - 4, COLS - 16);
1492 find_relocate_buttons (h, TRUE);
1493 return MSG_HANDLED;
1495 case MSG_IDLE:
1496 do_search (h);
1497 return MSG_HANDLED;
1499 default:
1500 return dlg_default_callback (w, sender, msg, parm, data);
1504 /* --------------------------------------------------------------------------------------------- */
1505 /** Handles the Stop/Start button in the find window */
1507 static int
1508 start_stop (WButton * button, int action)
1510 Widget *w = WIDGET (button);
1512 (void) action;
1514 running = is_start;
1515 widget_want_idle (WIDGET (find_dlg), running);
1516 is_start = !is_start;
1518 status_update (is_start ? _("Stopped") : _("Searching"));
1519 button_set_text (button, fbuts[is_start ? 3 : 2].text);
1521 find_relocate_buttons (w->owner, FALSE);
1522 dlg_redraw (w->owner);
1524 return 0;
1527 /* --------------------------------------------------------------------------------------------- */
1528 /** Handle view command, when invoked as a button */
1530 static int
1531 find_do_view_file (WButton * button, int action)
1533 (void) button;
1534 (void) action;
1536 view_edit_currently_selected_file (0, 0);
1537 return 0;
1540 /* --------------------------------------------------------------------------------------------- */
1541 /** Handle edit command, when invoked as a button */
1543 static int
1544 find_do_edit_file (WButton * button, int action)
1546 (void) button;
1547 (void) action;
1549 view_edit_currently_selected_file (0, 1);
1550 return 0;
1553 /* --------------------------------------------------------------------------------------------- */
1555 static void
1556 setup_gui (void)
1558 size_t i;
1559 int lines, cols;
1560 int y;
1562 #ifdef ENABLE_NLS
1563 static gboolean i18n_flag = FALSE;
1565 if (!i18n_flag)
1567 for (i = 0; i < fbuts_num; i++)
1569 fbuts[i].text = _(fbuts[i].text);
1570 fbuts[i].len = str_term_width1 (fbuts[i].text) + 3;
1571 if (fbuts[i].flags == DEFPUSH_BUTTON)
1572 fbuts[i].len += 2;
1575 i18n_flag = TRUE;
1577 #endif /* ENABLE_NLS */
1579 lines = LINES - 4;
1580 cols = COLS - 16;
1582 find_dlg =
1583 create_dlg (TRUE, 0, 0, lines, cols, dialog_colors, find_callback, NULL, "[Find File]",
1584 _("Find File"), DLG_CENTER);
1586 find_calc_button_locations (find_dlg, TRUE);
1588 y = 2;
1589 find_list = listbox_new (y, 2, lines - 10, cols - 4, FALSE, NULL);
1590 add_widget_autopos (find_dlg, find_list, WPOS_KEEP_ALL, NULL);
1591 y += WIDGET (find_list)->lines;
1593 add_widget_autopos (find_dlg, hline_new (y++, -1, -1), WPOS_KEEP_BOTTOM, NULL);
1595 found_num_label = label_new (y++, 4, "");
1596 add_widget_autopos (find_dlg, found_num_label, WPOS_KEEP_BOTTOM, NULL);
1598 status_label = label_new (y++, 4, _("Searching"));
1599 add_widget_autopos (find_dlg, status_label, WPOS_KEEP_BOTTOM, NULL);
1601 add_widget_autopos (find_dlg, hline_new (y++, -1, -1), WPOS_KEEP_BOTTOM, NULL);
1603 for (i = 0; i < fbuts_num; i++)
1605 if (i == 3)
1606 fbuts[3].button = fbuts[2].button;
1607 else
1609 fbuts[i].button =
1610 WIDGET (button_new
1611 (y, fbuts[i].x, fbuts[i].ret_cmd, fbuts[i].flags, fbuts[i].text,
1612 fbuts[i].callback));
1613 add_widget_autopos (find_dlg, fbuts[i].button, WPOS_KEEP_BOTTOM, NULL);
1616 if (i == quit_button)
1617 y++;
1620 dlg_select_widget (find_list);
1623 /* --------------------------------------------------------------------------------------------- */
1625 static int
1626 run_process (void)
1628 int ret;
1630 search_content_handle = mc_search_new (content_pattern, -1);
1631 if (search_content_handle)
1633 search_content_handle->search_type =
1634 options.content_regexp ? MC_SEARCH_T_REGEX : MC_SEARCH_T_NORMAL;
1635 search_content_handle->is_case_sensitive = options.content_case_sens;
1636 search_content_handle->whole_words = options.content_whole_words;
1637 search_content_handle->is_all_charsets = options.content_all_charsets;
1639 search_file_handle = mc_search_new (find_pattern, -1);
1640 search_file_handle->search_type = options.file_pattern ? MC_SEARCH_T_GLOB : MC_SEARCH_T_REGEX;
1641 search_file_handle->is_case_sensitive = options.file_case_sens;
1642 search_file_handle->is_all_charsets = options.file_all_charsets;
1643 search_file_handle->is_entire_line = options.file_pattern;
1645 resuming = 0;
1647 widget_want_idle (WIDGET (find_dlg), TRUE);
1648 ret = run_dlg (find_dlg);
1650 mc_search_free (search_file_handle);
1651 search_file_handle = NULL;
1652 mc_search_free (search_content_handle);
1653 search_content_handle = NULL;
1655 return ret;
1658 /* --------------------------------------------------------------------------------------------- */
1660 static void
1661 kill_gui (void)
1663 widget_want_idle (WIDGET (find_dlg), FALSE);
1664 destroy_dlg (find_dlg);
1667 /* --------------------------------------------------------------------------------------------- */
1669 static int
1670 do_find (const char *start_dir, ssize_t start_dir_len, const char *ignore_dirs,
1671 const char *pattern, const char *content, char **dirname, char **filename)
1673 int return_value = 0;
1674 char *dir_tmp = NULL, *file_tmp = NULL;
1676 setup_gui ();
1678 /* FIXME: Need to cleanup this, this ought to be passed non-globaly */
1679 find_pattern = (char *) pattern;
1681 content_pattern = NULL;
1682 if (options.content_use && content != NULL && str_is_valid_string (content))
1683 content_pattern = g_strdup (content);
1685 init_find_vars ();
1686 parse_ignore_dirs (ignore_dirs);
1687 push_directory (vfs_path_from_str (start_dir));
1689 return_value = run_process ();
1691 /* Clear variables */
1692 init_find_vars ();
1694 get_list_info (&file_tmp, &dir_tmp);
1696 if (dir_tmp)
1697 *dirname = g_strdup (dir_tmp);
1698 if (file_tmp)
1699 *filename = g_strdup (file_tmp);
1701 if (return_value == B_PANELIZE && *filename)
1703 int status, link_to_dir, stale_link;
1704 int next_free = 0;
1705 int i;
1706 struct stat st;
1707 GList *entry;
1708 dir_list *list = &current_panel->dir;
1709 char *name = NULL;
1711 if (set_zero_dir (list))
1712 next_free++;
1714 for (i = 0, entry = find_list->list; entry != NULL; i++, entry = g_list_next (entry))
1716 const char *lc_filename = NULL;
1717 WLEntry *le = LENTRY (entry->data);
1718 char *p;
1720 if ((le->text == NULL) || (le->data == NULL))
1721 continue;
1723 if (content_pattern != NULL)
1724 lc_filename = strchr (le->text + 4, ':') + 1;
1725 else
1726 lc_filename = le->text + 4;
1728 name = mc_build_filename (le->data, lc_filename, (char *) NULL);
1729 /* skip initial start dir */
1730 if (start_dir_len < 0)
1731 p = name;
1732 else
1734 p = name + (size_t) start_dir_len;
1735 if (*p == PATH_SEP)
1736 p++;
1739 status = handle_path (list, p, &st, next_free, &link_to_dir, &stale_link);
1740 if (status == 0)
1742 g_free (name);
1743 continue;
1745 if (status == -1)
1747 g_free (name);
1748 break;
1751 /* don't add files more than once to the panel */
1752 if (content_pattern != NULL && next_free > 0
1753 && strcmp (list->list[next_free - 1].fname, p) == 0)
1755 g_free (name);
1756 continue;
1759 if (next_free == 0) /* first turn i.e clean old list */
1760 panel_clean_dir (current_panel);
1761 list->list[next_free].fnamelen = strlen (p);
1762 list->list[next_free].fname = g_strndup (p, list->list[next_free].fnamelen);
1763 list->list[next_free].f.marked = 0;
1764 list->list[next_free].f.link_to_dir = link_to_dir;
1765 list->list[next_free].f.stale_link = stale_link;
1766 list->list[next_free].f.dir_size_computed = 0;
1767 list->list[next_free].st = st;
1768 list->list[next_free].sort_key = NULL;
1769 list->list[next_free].second_sort_key = NULL;
1770 next_free++;
1771 g_free (name);
1772 if (!(next_free & 15))
1773 rotate_dash ();
1776 if (next_free)
1778 current_panel->count = next_free;
1779 current_panel->is_panelized = TRUE;
1781 /* absolute path */
1782 if (start_dir_len < 0)
1784 int ret;
1785 vfs_path_free (current_panel->cwd_vpath);
1786 current_panel->cwd_vpath = vfs_path_from_str (PATH_SEP_STR);
1787 ret = chdir (PATH_SEP_STR);
1788 (void) ret;
1790 panelize_save_panel (current_panel);
1794 g_free (content_pattern);
1795 kill_gui ();
1796 do_search (NULL); /* force do_search to release resources */
1797 g_free (old_dir);
1798 old_dir = NULL;
1800 return return_value;
1803 /* --------------------------------------------------------------------------------------------- */
1804 /*** public functions ****************************************************************************/
1805 /* --------------------------------------------------------------------------------------------- */
1807 void
1808 find_file (void)
1810 char *start_dir = NULL, *pattern = NULL, *content = NULL, *ignore_dirs = NULL;
1811 ssize_t start_dir_len;
1812 char *filename = NULL, *dirname = NULL;
1813 int v;
1815 while (find_parameters (&start_dir, &start_dir_len, &ignore_dirs, &pattern, &content))
1817 if (pattern[0] == '\0')
1818 break; /* nothing search */
1820 dirname = filename = NULL;
1821 is_start = FALSE;
1822 v = do_find (start_dir, start_dir_len, ignore_dirs, pattern, content, &dirname, &filename);
1823 g_free (ignore_dirs);
1824 g_free (pattern);
1826 if (v == B_ENTER)
1828 if (dirname != NULL)
1830 vfs_path_t *dirname_vpath;
1832 dirname_vpath = vfs_path_from_str (dirname);
1833 do_cd (dirname_vpath, cd_exact);
1834 vfs_path_free (dirname_vpath);
1835 if (filename != NULL)
1836 try_to_select (current_panel,
1837 filename + (content != NULL
1838 ? strchr (filename + 4, ':') - filename + 1 : 4));
1840 else if (filename != NULL)
1842 vfs_path_t *filename_vpath;
1844 filename_vpath = vfs_path_from_str (filename);
1845 do_cd (filename_vpath, cd_exact);
1846 vfs_path_free (filename_vpath);
1849 g_free (dirname);
1850 g_free (filename);
1851 break;
1854 g_free (content);
1855 g_free (dirname);
1856 g_free (filename);
1858 if (v == B_CANCEL)
1859 break;
1861 if (v == B_PANELIZE)
1863 panel_re_sort (current_panel);
1864 try_to_select (current_panel, NULL);
1865 break;
1870 /* --------------------------------------------------------------------------------------------- */