Build find file dialogs in normal order.
[midnight-commander.git] / src / filemanager / find.c
blob4835b09f3ec9c59fb6626eca0fc8b9c52bdbe15c
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 Dlg_head *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_("&Suspend"), 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 set_idle_proc (data, 0);
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 (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
412 switch (msg)
414 case DLG_ACTION:
415 if (sender == WIDGET (content_use_cbox))
417 gboolean disable = !(content_use_cbox->state & C_BOOL);
419 widget_disable (WIDGET (in_with), disable);
420 widget_disable (WIDGET (content_first_hit_cbox), disable);
421 widget_disable (WIDGET (content_regexp_cbox), disable);
422 widget_disable (WIDGET (content_case_sens_cbox), disable);
423 #ifdef HAVE_CHARSET
424 widget_disable (WIDGET (content_all_charsets_cbox), disable);
425 #endif
426 widget_disable (WIDGET (content_whole_words_cbox), disable);
428 return MSG_HANDLED;
431 if (sender == WIDGET (ignore_dirs_cbox))
433 gboolean disable = !(ignore_dirs_cbox->state & C_BOOL);
435 widget_disable (WIDGET (in_ignore), disable);
437 return MSG_HANDLED;
440 return MSG_NOT_HANDLED;
443 case DLG_VALIDATE:
444 if (h->ret_value != B_ENTER)
445 return MSG_HANDLED;
447 /* check filename regexp */
448 if (!(file_pattern_cbox->state & C_BOOL)
449 && (in_name->buffer[0] != '\0') && !find_check_regexp (in_name->buffer))
451 h->state = DLG_ACTIVE; /* Don't stop the dialog */
452 message (D_ERROR, MSG_ERROR, _("Malformed regular expression"));
453 dlg_select_widget (in_name);
454 return MSG_HANDLED;
457 /* check content regexp */
458 if ((content_use_cbox->state & C_BOOL) && (content_regexp_cbox->state & C_BOOL)
459 && (in_with->buffer[0] != '\0') && !find_check_regexp (in_with->buffer))
461 h->state = DLG_ACTIVE; /* Don't stop the dialog */
462 message (D_ERROR, MSG_ERROR, _("Malformed regular expression"));
463 dlg_select_widget (in_with);
464 return MSG_HANDLED;
467 return MSG_HANDLED;
469 default:
470 return default_dlg_callback (h, sender, msg, parm, data);
474 /* --------------------------------------------------------------------------------------------- */
476 * find_parameters: gets information from the user
478 * If the return value is TRUE, then the following holds:
480 * start_dir, ignore_dirs, pattern and content contain the information provided by the user.
481 * They are newly allocated strings and must be freed when uneeded.
483 * start_dir_len is -1 when user entered an absolute path, otherwise it is a length
484 * of start_dir (which is absolute). It is used to get a relative pats of find results.
487 static gboolean
488 find_parameters (char **start_dir, ssize_t * start_dir_len,
489 char **ignore_dirs, char **pattern, char **content)
491 /* Size of the find parameters window */
492 #ifdef HAVE_CHARSET
493 const int lines = 19;
494 #else
495 const int lines = 18;
496 #endif
497 int cols = 68;
499 gboolean return_value;
501 /* file name */
502 const char *file_name_label = N_("File name:");
503 const char *file_recurs_label = N_("&Find recursively");
504 const char *file_pattern_label = N_("&Using shell patterns");
505 #ifdef HAVE_CHARSET
506 const char *file_all_charsets_label = N_("&All charsets");
507 #endif
508 const char *file_case_label = N_("Cas&e sensitive");
509 const char *file_skip_hidden_label = N_("S&kip hidden");
511 /* file content */
512 const char *content_content_label = N_("Content:");
513 const char *content_use_label = N_("Sea&rch for content");
514 const char *content_regexp_label = N_("Re&gular expression");
515 const char *content_case_label = N_("Case sens&itive");
516 #ifdef HAVE_CHARSET
517 const char *content_all_charsets_label = N_("A&ll charsets");
518 #endif
519 const char *content_whole_words_label = N_("&Whole words");
520 const char *content_first_hit_label = N_("Fir&st hit");
522 const char *buts[] = { N_("&Tree"), N_("&OK"), N_("&Cancel") };
524 /* button lengths */
525 int b0, b1, b2, b12;
526 int y1, y2, x1, x2;
527 /* column width */
528 int cw;
530 gboolean disable;
532 #ifdef ENABLE_NLS
534 size_t i;
536 file_name_label = _(file_name_label);
537 file_recurs_label = _(file_recurs_label);
538 file_pattern_label = _(file_pattern_label);
539 #ifdef HAVE_CHARSET
540 file_all_charsets_label = _(file_all_charsets_label);
541 #endif
542 file_case_label = _(file_case_label);
543 file_skip_hidden_label = _(file_skip_hidden_label);
545 /* file content */
546 content_content_label = _(content_content_label);
547 content_use_label = _(content_use_label);
548 content_regexp_label = _(content_regexp_label);
549 content_case_label = _(content_case_label);
550 #ifdef HAVE_CHARSET
551 content_all_charsets_label = _(content_all_charsets_label);
552 #endif
553 content_whole_words_label = _(content_whole_words_label);
554 content_first_hit_label = _(content_first_hit_label);
556 for (i = 0; i < G_N_ELEMENTS (buts); i++)
557 buts[i] = _(buts[i]);
559 #endif /* ENABLE_NLS */
561 /* caclulate dialog width */
563 /* widget widths */
564 cw = str_term_width1 (file_name_label);
565 cw = max (cw, str_term_width1 (file_recurs_label) + 4);
566 cw = max (cw, str_term_width1 (file_pattern_label) + 4);
567 #ifdef HAVE_CHARSET
568 cw = max (cw, str_term_width1 (file_all_charsets_label) + 4);
569 #endif
570 cw = max (cw, str_term_width1 (file_case_label) + 4);
571 cw = max (cw, str_term_width1 (file_skip_hidden_label) + 4);
573 cw = max (cw, str_term_width1 (content_content_label) + 4);
574 cw = max (cw, str_term_width1 (content_use_label) + 4);
575 cw = max (cw, str_term_width1 (content_regexp_label) + 4);
576 cw = max (cw, str_term_width1 (content_case_label) + 4);
577 #ifdef HAVE_CHARSET
578 cw = max (cw, str_term_width1 (content_all_charsets_label) + 4);
579 #endif
580 cw = max (cw, str_term_width1 (content_whole_words_label) + 4);
581 cw = max (cw, str_term_width1 (content_first_hit_label) + 4);
583 /* button width */
584 b0 = str_term_width1 (buts[0]) + 3;
585 b1 = str_term_width1 (buts[1]) + 5; /* default button */
586 b2 = str_term_width1 (buts[2]) + 3;
587 b12 = b1 + b2 + 1;
589 cols = max (cols, max (b12, cw * 2 + 1) + 6);
591 find_load_options ();
593 if (in_start_dir == NULL)
594 in_start_dir = g_strdup (".");
596 disable = !options.content_use;
598 find_dlg =
599 create_dlg (TRUE, 0, 0, lines, cols, dialog_colors, find_parm_callback, NULL, "[Find File]",
600 _("Find File"), DLG_CENTER);
602 x1 = 3;
603 x2 = cols / 2 + 1;
604 cw = (cols - 7) / 2;
605 y1 = 2;
607 add_widget (find_dlg, label_new (y1++, x1, _("Start at:")));
608 in_start =
609 input_new (y1, x1, input_get_default_colors (), cols - b0 - 7, in_start_dir, "start",
610 INPUT_COMPLETE_DEFAULT);
611 add_widget (find_dlg, in_start);
613 add_widget (find_dlg, button_new (y1++, cols - b0 - 3, B_TREE, NORMAL_BUTTON, buts[0], NULL));
615 ignore_dirs_cbox =
616 check_new (y1++, x1, options.ignore_dirs_enable, _("Ena&ble ignore directories:"));
617 add_widget (find_dlg, ignore_dirs_cbox);
619 in_ignore =
620 input_new (y1++, x1, input_get_default_colors (), cols - 6,
621 options.ignore_dirs != NULL ? options.ignore_dirs : "", "ignoredirs",
622 INPUT_COMPLETE_DEFAULT);
623 widget_disable (WIDGET (in_ignore), !options.ignore_dirs_enable);
624 add_widget (find_dlg, in_ignore);
626 add_widget (find_dlg, hline_new (y1++, -1, -1));
628 y2 = y1;
630 /* Start 1st column */
631 add_widget (find_dlg, label_new (y1++, x1, file_name_label));
632 in_name =
633 input_new (y1++, x1, input_get_default_colors (), cw, INPUT_LAST_TEXT, "name",
634 INPUT_COMPLETE_DEFAULT);
635 add_widget (find_dlg, in_name);
637 /* Start 2nd column */
638 content_label = label_new (y2++, x2, content_content_label);
639 add_widget (find_dlg, content_label);
640 in_with =
641 input_new (y2++, x2, input_get_default_colors (), cw, INPUT_LAST_TEXT,
642 MC_HISTORY_SHARED_SEARCH, INPUT_COMPLETE_DEFAULT);
643 in_with->label = content_label;
644 widget_disable (WIDGET (in_with), disable);
645 add_widget (find_dlg, in_with);
647 content_use_cbox = check_new (y2++, x2, options.content_use, content_use_label);
648 add_widget (find_dlg, content_use_cbox);
650 /* Continue 1st column */
651 recursively_cbox = check_new (y1++, x1, options.find_recurs, file_recurs_label);
652 add_widget (find_dlg, recursively_cbox);
654 file_pattern_cbox = check_new (y1++, x1, options.file_pattern, file_pattern_label);
655 add_widget (find_dlg, file_pattern_cbox);
657 file_case_sens_cbox = check_new (y1++, x1, options.file_case_sens, file_case_label);
658 add_widget (find_dlg, file_case_sens_cbox);
660 #ifdef HAVE_CHARSET
661 file_all_charsets_cbox =
662 check_new (y1++, x1, options.file_all_charsets, file_all_charsets_label);
663 add_widget (find_dlg, file_all_charsets_cbox);
664 #endif
666 skip_hidden_cbox = check_new (y1++, x1, options.skip_hidden, file_skip_hidden_label);
667 add_widget (find_dlg, skip_hidden_cbox);
669 /* Continue 2nd column */
670 content_regexp_cbox = check_new (y2++, x2, options.content_regexp, content_regexp_label);
671 widget_disable (WIDGET (content_regexp_cbox), disable);
672 add_widget (find_dlg, content_regexp_cbox);
674 content_case_sens_cbox = check_new (y2++, x2, options.content_case_sens, content_case_label);
675 widget_disable (WIDGET (content_case_sens_cbox), disable);
676 add_widget (find_dlg, content_case_sens_cbox);
678 #ifdef HAVE_CHARSET
679 content_all_charsets_cbox =
680 check_new (y2++, x2, options.content_all_charsets, content_all_charsets_label);
681 widget_disable (WIDGET (content_all_charsets_cbox), disable);
682 add_widget (find_dlg, content_all_charsets_cbox);
683 #endif
685 content_whole_words_cbox =
686 check_new (y2++, x2, options.content_whole_words, content_whole_words_label);
687 widget_disable (WIDGET (content_whole_words_cbox), disable);
688 add_widget (find_dlg, content_whole_words_cbox);
690 content_first_hit_cbox =
691 check_new (y2++, x2, options.content_first_hit, content_first_hit_label);
692 widget_disable (WIDGET (content_first_hit_cbox), disable);
693 add_widget (find_dlg, content_first_hit_cbox);
695 /* buttons */
696 y1 = max (y1, y2);
697 x1 = (cols - b12) / 2;
698 add_widget (find_dlg, hline_new (y1++, -1, -1));
699 add_widget (find_dlg, button_new (y1, x1, B_ENTER, DEFPUSH_BUTTON, buts[1], NULL));
700 add_widget (find_dlg, button_new (y1, x1 + b1 + 1, B_CANCEL, NORMAL_BUTTON, buts[2], NULL));
702 find_par_start:
703 dlg_select_widget (in_name);
705 switch (run_dlg (find_dlg))
707 case B_CANCEL:
708 return_value = FALSE;
709 break;
711 case B_TREE:
713 char *temp_dir;
715 temp_dir = in_start->buffer;
716 if ((temp_dir[0] == '\0') || ((temp_dir[0] == '.') && (temp_dir[1] == '\0')))
717 temp_dir = vfs_path_to_str (current_panel->cwd_vpath);
718 else
719 temp_dir = g_strdup (temp_dir);
721 if (in_start_dir != INPUT_LAST_TEXT)
722 g_free (in_start_dir);
723 in_start_dir = tree_box (temp_dir);
724 if (in_start_dir == NULL)
725 in_start_dir = temp_dir;
726 else
727 g_free (temp_dir);
729 input_assign_text (in_start, in_start_dir);
731 /* Warning: Dreadful goto */
732 goto find_par_start;
735 default:
737 char *s;
739 #ifdef HAVE_CHARSET
740 options.file_all_charsets = file_all_charsets_cbox->state & C_BOOL;
741 options.content_all_charsets = content_all_charsets_cbox->state & C_BOOL;
742 #endif
743 options.content_use = content_use_cbox->state & C_BOOL;
744 options.content_case_sens = content_case_sens_cbox->state & C_BOOL;
745 options.content_regexp = content_regexp_cbox->state & C_BOOL;
746 options.content_first_hit = content_first_hit_cbox->state & C_BOOL;
747 options.content_whole_words = content_whole_words_cbox->state & C_BOOL;
748 options.find_recurs = recursively_cbox->state & C_BOOL;
749 options.file_pattern = file_pattern_cbox->state & C_BOOL;
750 options.file_case_sens = file_case_sens_cbox->state & C_BOOL;
751 options.skip_hidden = skip_hidden_cbox->state & C_BOOL;
752 options.ignore_dirs_enable = ignore_dirs_cbox->state & C_BOOL;
753 g_free (options.ignore_dirs);
754 options.ignore_dirs = g_strdup (in_ignore->buffer);
756 *content = (options.content_use && in_with->buffer[0] != '\0')
757 ? g_strdup (in_with->buffer) : NULL;
758 *start_dir = in_start->buffer[0] != '\0' ? in_start->buffer : (char *) ".";
759 *pattern = g_strdup (in_name->buffer);
760 if (in_start_dir != INPUT_LAST_TEXT)
761 g_free (in_start_dir);
762 in_start_dir = g_strdup (*start_dir);
764 s = tilde_expand (*start_dir);
765 canonicalize_pathname (s);
767 if (s[0] == '.' && s[1] == '\0')
769 *start_dir = vfs_path_to_str (current_panel->cwd_vpath);
770 /* FIXME: is current_panel->cwd_vpath canonicalized? */
771 /* relative paths will be used in panelization */
772 *start_dir_len = (ssize_t) strlen (*start_dir);
773 g_free (s);
775 else if (g_path_is_absolute (s))
777 *start_dir = s;
778 *start_dir_len = -1;
780 else
782 /* relative paths will be used in panelization */
783 char *cwd_str;
785 cwd_str = vfs_path_to_str (current_panel->cwd_vpath);
786 *start_dir = mc_build_filename (cwd_str, s, (char *) NULL);
787 *start_dir_len = (ssize_t) strlen (cwd_str);
788 g_free (cwd_str);
789 g_free (s);
792 if (!options.ignore_dirs_enable || in_ignore->buffer[0] == '\0'
793 || (in_ignore->buffer[0] == '.' && in_ignore->buffer[1] == '\0'))
794 *ignore_dirs = NULL;
795 else
796 *ignore_dirs = g_strdup (in_ignore->buffer);
798 find_save_options ();
800 return_value = TRUE;
804 destroy_dlg (find_dlg);
806 return return_value;
809 /* --------------------------------------------------------------------------------------------- */
811 #if GLIB_CHECK_VERSION (2, 14, 0)
812 static inline void
813 push_directory (const vfs_path_t * dir)
815 g_queue_push_head (&dir_queue, (void *) dir);
818 /* --------------------------------------------------------------------------------------------- */
820 static inline vfs_path_t *
821 pop_directory (void)
823 return (vfs_path_t *) g_queue_pop_tail (&dir_queue);
826 /* --------------------------------------------------------------------------------------------- */
827 /** Remove all the items from the stack */
829 static void
830 clear_stack (void)
832 g_queue_foreach (&dir_queue, (GFunc) vfs_path_free, NULL);
833 g_queue_clear (&dir_queue);
836 /* --------------------------------------------------------------------------------------------- */
838 #else /* GLIB_CHECK_VERSION */
839 static void
840 push_directory (const vfs_path_t * dir)
842 dir_stack *new;
844 new = g_new (dir_stack, 1);
845 new->name = (vfs_path_t *) dir;
846 new->prev = dir_stack_base;
847 dir_stack_base = new;
850 /* --------------------------------------------------------------------------------------------- */
852 static vfs_path_t *
853 pop_directory (void)
855 vfs_path_t *name = NULL;
857 if (dir_stack_base != NULL)
859 dir_stack *next;
860 name = dir_stack_base->name;
861 next = dir_stack_base->prev;
862 g_free (dir_stack_base);
863 dir_stack_base = next;
866 return name;
869 /* --------------------------------------------------------------------------------------------- */
870 /** Remove all the items from the stack */
872 static void
873 clear_stack (void)
875 vfs_path_t *dir = NULL;
877 while ((dir = pop_directory ()) != NULL)
878 vfs_path_free (dir);
880 #endif /* GLIB_CHECK_VERSION */
882 /* --------------------------------------------------------------------------------------------- */
884 static void
885 insert_file (const char *dir, const char *file)
887 char *tmp_name = NULL;
888 static char *dirname = NULL;
890 while (dir[0] == PATH_SEP && dir[1] == PATH_SEP)
891 dir++;
893 if (old_dir)
895 if (strcmp (old_dir, dir))
897 g_free (old_dir);
898 old_dir = g_strdup (dir);
899 dirname = add_to_list (dir, NULL);
902 else
904 old_dir = g_strdup (dir);
905 dirname = add_to_list (dir, NULL);
908 tmp_name = g_strdup_printf (" %s", file);
909 add_to_list (tmp_name, dirname);
910 g_free (tmp_name);
913 /* --------------------------------------------------------------------------------------------- */
915 static void
916 find_add_match (const char *dir, const char *file)
918 insert_file (dir, file);
920 /* Don't scroll */
921 if (matches == 0)
922 listbox_select_first (find_list);
923 send_message (WIDGET (find_list), NULL, WIDGET_DRAW, 0, NULL);
925 matches++;
926 found_num_update ();
929 /* --------------------------------------------------------------------------------------------- */
931 * get_line_at:
933 * Returns malloced null-terminated line from file file_fd.
934 * Input is buffered in buf_size long buffer.
935 * Current pos in buf is stored in pos.
936 * n_read - number of read chars.
937 * has_newline - is there newline ?
940 static char *
941 get_line_at (int file_fd, char *buf, int buf_size, int *pos, int *n_read, gboolean * has_newline)
943 char *buffer = NULL;
944 int buffer_size = 0;
945 char ch = 0;
946 int i = 0;
948 while (TRUE)
950 if (*pos >= *n_read)
952 *pos = 0;
953 *n_read = mc_read (file_fd, buf, buf_size);
954 if (*n_read <= 0)
955 break;
958 ch = buf[(*pos)++];
959 if (ch == '\0')
961 /* skip possible leading zero(s) */
962 if (i == 0)
963 continue;
964 break;
967 if (i >= buffer_size - 1)
968 buffer = g_realloc (buffer, buffer_size += 80);
970 /* Strip newline */
971 if (ch == '\n')
972 break;
974 buffer[i++] = ch;
977 *has_newline = (ch != '\0');
979 if (buffer != NULL)
980 buffer[i] = '\0';
982 return buffer;
985 /* --------------------------------------------------------------------------------------------- */
987 static FindProgressStatus
988 check_find_events (Dlg_head * h)
990 Gpm_Event event;
991 int c;
993 event.x = -1;
994 c = tty_get_event (&event, h->mouse_status == MOU_REPEAT, FALSE);
995 if (c != EV_NONE)
997 dlg_process_event (h, c, &event);
998 if (h->ret_value == B_ENTER
999 || h->ret_value == B_CANCEL || h->ret_value == B_AGAIN || h->ret_value == B_PANELIZE)
1001 /* dialog terminated */
1002 return FIND_ABORT;
1004 if (!(h->flags & DLG_WANT_IDLE))
1006 /* searching suspended */
1007 return FIND_SUSPEND;
1011 return FIND_CONT;
1014 /* --------------------------------------------------------------------------------------------- */
1016 * search_content:
1018 * Search the content_pattern string in the DIRECTORY/FILE.
1019 * It will add the found entries to the find listbox.
1021 * returns FALSE if do_search should look for another file
1022 * TRUE if do_search should exit and proceed to the event handler
1025 static gboolean
1026 search_content (Dlg_head * h, const char *directory, const char *filename)
1028 struct stat s;
1029 char buffer[BUF_4K];
1030 int file_fd;
1031 gboolean ret_val = FALSE;
1032 vfs_path_t *vpath;
1034 vpath = vfs_path_build_filename (directory, filename, (char *) NULL);
1036 if (mc_stat (vpath, &s) != 0 || !S_ISREG (s.st_mode))
1038 vfs_path_free (vpath);
1039 return FALSE;
1042 file_fd = mc_open (vpath, O_RDONLY);
1043 vfs_path_free (vpath);
1045 if (file_fd == -1)
1046 return FALSE;
1048 g_snprintf (buffer, sizeof (buffer), _("Grepping in %s"), filename);
1049 status_update (str_trunc (buffer, WIDGET (h)->cols - 8));
1051 mc_refresh ();
1053 tty_enable_interrupt_key ();
1054 tty_got_interrupt ();
1057 int line = 1;
1058 int pos = 0;
1059 int n_read = 0;
1060 gboolean has_newline;
1061 char *p = NULL;
1062 gboolean found = FALSE;
1063 gsize found_len;
1064 char result[BUF_MEDIUM];
1066 if (resuming)
1068 /* We've been previously suspended, start from the previous position */
1069 resuming = 0;
1070 line = last_line;
1071 pos = last_pos;
1073 while (!ret_val
1074 && (p = get_line_at (file_fd, buffer, sizeof (buffer),
1075 &pos, &n_read, &has_newline)) != NULL)
1077 if (!found /* Search in binary line once */
1078 && mc_search_run (search_content_handle,
1079 (const void *) p, 0, strlen (p), &found_len))
1081 g_snprintf (result, sizeof (result), "%d:%s", line, filename);
1082 find_add_match (directory, result);
1083 found = TRUE;
1085 g_free (p);
1087 if (found && options.content_first_hit)
1088 break;
1090 if (has_newline)
1092 found = FALSE;
1093 line++;
1096 if ((line & 0xff) == 0)
1098 FindProgressStatus res;
1099 res = check_find_events (h);
1100 switch (res)
1102 case FIND_ABORT:
1103 stop_idle (h);
1104 ret_val = TRUE;
1105 break;
1106 case FIND_SUSPEND:
1107 resuming = 1;
1108 last_line = line;
1109 last_pos = pos;
1110 ret_val = TRUE;
1111 break;
1112 default:
1113 break;
1118 tty_disable_interrupt_key ();
1119 mc_close (file_fd);
1120 return ret_val;
1123 /* --------------------------------------------------------------------------------------------- */
1126 If dir is absolute, this means we're within dir and searching file here.
1127 If dir is relative, this means we're going to add dir to the directory stack.
1129 static gboolean
1130 find_ignore_dir_search (const char *dir)
1132 if (find_ignore_dirs != NULL)
1134 const size_t dlen = strlen (dir);
1135 const unsigned char dabs = g_path_is_absolute (dir) ? 1 : 0;
1137 char **ignore_dir;
1139 for (ignore_dir = find_ignore_dirs; *ignore_dir != NULL; ignore_dir++)
1141 const size_t ilen = strlen (*ignore_dir);
1142 const unsigned char iabs = g_path_is_absolute (*ignore_dir) ? 2 : 0;
1144 /* ignore dir is too long -- skip it */
1145 if (dlen < ilen)
1146 continue;
1148 /* handle absolute and relative paths */
1149 switch (iabs | dabs)
1151 case 0: /* both paths are relative */
1152 case 3: /* both paths are abolute */
1153 /* if ignore dir is not a path of dir -- skip it */
1154 if (strncmp (dir, *ignore_dir, ilen) == 0)
1156 /* be sure that ignore dir is not a part of dir like:
1157 ignore dir is "h", dir is "home" */
1158 if (dir[ilen] == '\0' || dir[ilen] == PATH_SEP)
1159 return TRUE;
1161 break;
1162 case 1: /* dir is absolute, ignore_dir is relative */
1164 char *d;
1166 d = strstr (dir, *ignore_dir);
1167 if (d != NULL && d[-1] == PATH_SEP && (d[ilen] == '\0' || d[ilen] == PATH_SEP))
1168 return TRUE;
1170 break;
1171 case 2: /* dir is relative, ignore_dir is absolute */
1172 /* FIXME: skip this case */
1173 break;
1174 default: /* this cannot occurs */
1175 return FALSE;
1180 return FALSE;
1183 /* --------------------------------------------------------------------------------------------- */
1185 static void
1186 find_rotate_dash (const Dlg_head * h, gboolean finish)
1188 static const char rotating_dash[] = "|/-\\";
1189 static unsigned int pos = 0;
1191 if (verbose)
1193 const Widget *w = WIDGET (h);
1195 pos = (pos + 1) % 4;
1196 tty_setcolor (h->color[DLG_COLOR_NORMAL]);
1197 widget_move (h, w->lines - 7, w->cols - 4);
1198 tty_print_char (finish ? ' ' : rotating_dash[pos]);
1199 mc_refresh ();
1203 /* --------------------------------------------------------------------------------------------- */
1205 static int
1206 do_search (Dlg_head * h)
1208 static struct dirent *dp = NULL;
1209 static DIR *dirp = NULL;
1210 static char *directory = NULL;
1211 struct stat tmp_stat;
1212 static int subdirs_left = 0;
1213 gsize bytes_found;
1214 unsigned short count;
1216 if (h == NULL)
1217 { /* someone forces me to close dirp */
1218 if (dirp != NULL)
1220 mc_closedir (dirp);
1221 dirp = NULL;
1223 g_free (directory);
1224 directory = NULL;
1225 dp = NULL;
1226 return 1;
1229 for (count = 0; count < 32; count++)
1231 while (dp == NULL)
1233 if (dirp != NULL)
1235 mc_closedir (dirp);
1236 dirp = NULL;
1239 while (dirp == NULL)
1241 vfs_path_t *tmp_vpath = NULL;
1243 tty_setcolor (REVERSE_COLOR);
1245 while (TRUE)
1247 tmp_vpath = pop_directory ();
1248 if (tmp_vpath == NULL)
1250 running = FALSE;
1251 if (ignore_count == 0)
1252 status_update (_("Finished"));
1253 else
1255 char msg[BUF_SMALL];
1256 g_snprintf (msg, sizeof (msg),
1257 ngettext ("Finished (ignored %zd directory)",
1258 "Finished (ignored %zd directories)",
1259 ignore_count), ignore_count);
1260 status_update (msg);
1262 find_rotate_dash (h, TRUE);
1263 stop_idle (h);
1264 return 0;
1267 /* handle absolute ignore dirs here */
1269 char *tmp;
1270 gboolean ok;
1272 tmp = vfs_path_to_str (tmp_vpath);
1273 ok = find_ignore_dir_search (tmp);
1274 g_free (tmp);
1275 if (!ok)
1276 break;
1279 vfs_path_free (tmp_vpath);
1280 ignore_count++;
1283 g_free (directory);
1284 directory = vfs_path_to_str (tmp_vpath);
1286 if (verbose)
1288 char buffer[BUF_MEDIUM];
1290 g_snprintf (buffer, sizeof (buffer), _("Searching %s"), directory);
1291 status_update (str_trunc (directory, WIDGET (h)->cols - 8));
1293 /* mc_stat should not be called after mc_opendir
1294 because vfs_s_opendir modifies the st_nlink
1296 if (mc_stat (tmp_vpath, &tmp_stat) == 0)
1297 subdirs_left = tmp_stat.st_nlink - 2;
1298 else
1299 subdirs_left = 0;
1301 dirp = mc_opendir (tmp_vpath);
1302 vfs_path_free (tmp_vpath);
1303 } /* while (!dirp) */
1305 /* skip invalid filenames */
1306 while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1308 } /* while (!dp) */
1310 if (strcmp (dp->d_name, ".") == 0 || strcmp (dp->d_name, "..") == 0)
1312 /* skip invalid filenames */
1313 while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1316 return 1;
1319 if (!(options.skip_hidden && (dp->d_name[0] == '.')))
1321 gboolean search_ok;
1323 if ((subdirs_left != 0) && options.find_recurs && (directory != NULL))
1324 { /* Can directory be NULL ? */
1325 /* handle relative ignore dirs here */
1326 if (options.ignore_dirs_enable && find_ignore_dir_search (dp->d_name))
1327 ignore_count++;
1328 else
1330 vfs_path_t *tmp_vpath;
1332 tmp_vpath = vfs_path_build_filename (directory, dp->d_name, (char *) NULL);
1334 if (mc_lstat (tmp_vpath, &tmp_stat) == 0 && S_ISDIR (tmp_stat.st_mode))
1336 push_directory (tmp_vpath);
1337 subdirs_left--;
1339 else
1340 vfs_path_free (tmp_vpath);
1344 search_ok = mc_search_run (search_file_handle, dp->d_name,
1345 0, strlen (dp->d_name), &bytes_found);
1347 if (search_ok)
1349 if (content_pattern == NULL)
1350 find_add_match (directory, dp->d_name);
1351 else if (search_content (h, directory, dp->d_name))
1352 return 1;
1356 /* skip invalid filenames */
1357 while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1359 } /* for */
1361 find_rotate_dash (h, FALSE);
1363 return 1;
1366 /* --------------------------------------------------------------------------------------------- */
1368 static void
1369 init_find_vars (void)
1371 g_free (old_dir);
1372 old_dir = NULL;
1373 matches = 0;
1374 ignore_count = 0;
1376 /* Remove all the items from the stack */
1377 clear_stack ();
1379 g_strfreev (find_ignore_dirs);
1380 find_ignore_dirs = NULL;
1383 /* --------------------------------------------------------------------------------------------- */
1385 static void
1386 find_do_view_edit (int unparsed_view, int edit, char *dir, char *file)
1388 char *fullname = NULL;
1389 const char *filename = NULL;
1390 int line;
1391 vfs_path_t *fullname_vpath;
1393 if (content_pattern != NULL)
1395 filename = strchr (file + 4, ':') + 1;
1396 line = atoi (file + 4);
1398 else
1400 filename = file + 4;
1401 line = 0;
1404 fullname_vpath = vfs_path_build_filename (dir, filename, (char *) NULL);
1405 if (edit)
1406 do_edit_at_line (fullname_vpath, use_internal_edit, line);
1407 else
1408 view_file_at_line (fullname_vpath, unparsed_view, use_internal_view, line);
1409 vfs_path_free (fullname_vpath);
1410 g_free (fullname);
1413 /* --------------------------------------------------------------------------------------------- */
1415 static cb_ret_t
1416 view_edit_currently_selected_file (int unparsed_view, int edit)
1418 char *dir = NULL;
1419 char *text = NULL;
1421 listbox_get_current (find_list, &text, (void **) &dir);
1423 if ((text == NULL) || (dir == NULL))
1424 return MSG_NOT_HANDLED;
1426 find_do_view_edit (unparsed_view, edit, dir, text);
1427 return MSG_HANDLED;
1430 /* --------------------------------------------------------------------------------------------- */
1432 static void
1433 find_calc_button_locations (const Dlg_head * h, gboolean all_buttons)
1435 const int cols = WIDGET (h)->cols;
1437 int l1, l2;
1439 l1 = fbuts[0].len + fbuts[1].len + fbuts[is_start ? 3 : 2].len + fbuts[4].len + 3;
1440 l2 = fbuts[5].len + fbuts[6].len + fbuts[7].len + 2;
1442 fbuts[0].x = (cols - l1) / 2;
1443 fbuts[1].x = fbuts[0].x + fbuts[0].len + 1;
1444 fbuts[2].x = fbuts[1].x + fbuts[1].len + 1;
1445 fbuts[3].x = fbuts[2].x;
1446 fbuts[4].x = fbuts[2].x + fbuts[is_start ? 3 : 2].len + 1;
1448 if (all_buttons)
1450 fbuts[5].x = (cols - l2) / 2;
1451 fbuts[6].x = fbuts[5].x + fbuts[5].len + 1;
1452 fbuts[7].x = fbuts[6].x + fbuts[6].len + 1;
1456 /* --------------------------------------------------------------------------------------------- */
1458 static void
1459 find_relocate_buttons (const Dlg_head * h, gboolean all_buttons)
1461 size_t i;
1463 find_calc_button_locations (h, all_buttons);
1465 for (i = 0; i < fbuts_num; i++)
1466 fbuts[i].button->x = WIDGET (h)->x + fbuts[i].x;
1469 /* --------------------------------------------------------------------------------------------- */
1471 static cb_ret_t
1472 find_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
1474 switch (msg)
1476 case DLG_KEY:
1477 if (parm == KEY_F (3) || parm == KEY_F (13))
1479 int unparsed_view = (parm == KEY_F (13));
1480 return view_edit_currently_selected_file (unparsed_view, 0);
1482 if (parm == KEY_F (4))
1483 return view_edit_currently_selected_file (0, 1);
1484 return MSG_NOT_HANDLED;
1486 case DLG_RESIZE:
1487 dlg_set_size (h, LINES - 4, COLS - 16);
1488 find_relocate_buttons (h, TRUE);
1489 return MSG_HANDLED;
1491 case DLG_IDLE:
1492 do_search (h);
1493 return MSG_HANDLED;
1495 default:
1496 return default_dlg_callback (h, sender, msg, parm, data);
1500 /* --------------------------------------------------------------------------------------------- */
1501 /** Handles the Stop/Start button in the find window */
1503 static int
1504 start_stop (WButton * button, int action)
1506 Widget *w = WIDGET (button);
1508 (void) action;
1510 running = is_start;
1511 set_idle_proc (find_dlg, running);
1512 is_start = !is_start;
1514 status_update (is_start ? _("Stopped") : _("Searching"));
1515 button_set_text (button, fbuts[is_start ? 3 : 2].text);
1517 find_relocate_buttons (w->owner, FALSE);
1518 dlg_redraw (w->owner);
1520 return 0;
1523 /* --------------------------------------------------------------------------------------------- */
1524 /** Handle view command, when invoked as a button */
1526 static int
1527 find_do_view_file (WButton * button, int action)
1529 (void) button;
1530 (void) action;
1532 view_edit_currently_selected_file (0, 0);
1533 return 0;
1536 /* --------------------------------------------------------------------------------------------- */
1537 /** Handle edit command, when invoked as a button */
1539 static int
1540 find_do_edit_file (WButton * button, int action)
1542 (void) button;
1543 (void) action;
1545 view_edit_currently_selected_file (0, 1);
1546 return 0;
1549 /* --------------------------------------------------------------------------------------------- */
1551 static void
1552 setup_gui (void)
1554 size_t i;
1555 int lines, cols;
1556 int y;
1558 #ifdef ENABLE_NLS
1559 static gboolean i18n_flag = FALSE;
1561 if (!i18n_flag)
1563 for (i = 0; i < fbuts_num; i++)
1565 fbuts[i].text = _(fbuts[i].text);
1566 fbuts[i].len = str_term_width1 (fbuts[i].text) + 3;
1567 if (fbuts[i].flags == DEFPUSH_BUTTON)
1568 fbuts[i].len += 2;
1571 i18n_flag = TRUE;
1573 #endif /* ENABLE_NLS */
1575 lines = LINES - 4;
1576 cols = COLS - 16;
1578 find_dlg =
1579 create_dlg (TRUE, 0, 0, lines, cols, dialog_colors, find_callback, NULL, "[Find File]",
1580 _("Find File"), DLG_CENTER);
1582 find_calc_button_locations (find_dlg, TRUE);
1584 y = 2;
1585 find_list = listbox_new (y, 2, lines - 10, cols - 4, FALSE, NULL);
1586 add_widget_autopos (find_dlg, find_list, WPOS_KEEP_ALL, NULL);
1587 y += WIDGET (find_list)->lines;
1589 add_widget_autopos (find_dlg, hline_new (y++, -1, -1), WPOS_KEEP_BOTTOM, NULL);
1591 found_num_label = label_new (y++, 4, "");
1592 add_widget_autopos (find_dlg, found_num_label, WPOS_KEEP_BOTTOM, NULL);
1594 status_label = label_new (y++, 4, _("Searching"));
1595 add_widget_autopos (find_dlg, status_label, WPOS_KEEP_BOTTOM, NULL);
1597 add_widget_autopos (find_dlg, hline_new (y++, -1, -1), WPOS_KEEP_BOTTOM, NULL);
1599 for (i = 0; i < fbuts_num; i++)
1601 if (i == 3)
1602 fbuts[3].button = fbuts[2].button;
1603 else
1605 fbuts[i].button =
1606 WIDGET (button_new
1607 (y, fbuts[i].x, fbuts[i].ret_cmd, fbuts[i].flags, fbuts[i].text,
1608 fbuts[i].callback));
1609 add_widget_autopos (find_dlg, fbuts[i].button, WPOS_KEEP_BOTTOM, NULL);
1612 if (i == quit_button)
1613 y++;
1616 dlg_select_widget (find_list);
1619 /* --------------------------------------------------------------------------------------------- */
1621 static int
1622 run_process (void)
1624 int ret;
1626 search_content_handle = mc_search_new (content_pattern, -1);
1627 if (search_content_handle)
1629 search_content_handle->search_type =
1630 options.content_regexp ? MC_SEARCH_T_REGEX : MC_SEARCH_T_NORMAL;
1631 search_content_handle->is_case_sensitive = options.content_case_sens;
1632 search_content_handle->whole_words = options.content_whole_words;
1633 search_content_handle->is_all_charsets = options.content_all_charsets;
1635 search_file_handle = mc_search_new (find_pattern, -1);
1636 search_file_handle->search_type = options.file_pattern ? MC_SEARCH_T_GLOB : MC_SEARCH_T_REGEX;
1637 search_file_handle->is_case_sensitive = options.file_case_sens;
1638 search_file_handle->is_all_charsets = options.file_all_charsets;
1639 search_file_handle->is_entire_line = options.file_pattern;
1641 resuming = 0;
1643 set_idle_proc (find_dlg, 1);
1644 ret = run_dlg (find_dlg);
1646 mc_search_free (search_file_handle);
1647 search_file_handle = NULL;
1648 mc_search_free (search_content_handle);
1649 search_content_handle = NULL;
1651 return ret;
1654 /* --------------------------------------------------------------------------------------------- */
1656 static void
1657 kill_gui (void)
1659 set_idle_proc (find_dlg, 0);
1660 destroy_dlg (find_dlg);
1663 /* --------------------------------------------------------------------------------------------- */
1665 static int
1666 do_find (const char *start_dir, ssize_t start_dir_len, const char *ignore_dirs,
1667 const char *pattern, const char *content, char **dirname, char **filename)
1669 int return_value = 0;
1670 char *dir_tmp = NULL, *file_tmp = NULL;
1672 setup_gui ();
1674 /* FIXME: Need to cleanup this, this ought to be passed non-globaly */
1675 find_pattern = (char *) pattern;
1677 content_pattern = NULL;
1678 if (options.content_use && content != NULL && str_is_valid_string (content))
1679 content_pattern = g_strdup (content);
1681 init_find_vars ();
1682 parse_ignore_dirs (ignore_dirs);
1683 push_directory (vfs_path_from_str (start_dir));
1685 return_value = run_process ();
1687 /* Clear variables */
1688 init_find_vars ();
1690 get_list_info (&file_tmp, &dir_tmp);
1692 if (dir_tmp)
1693 *dirname = g_strdup (dir_tmp);
1694 if (file_tmp)
1695 *filename = g_strdup (file_tmp);
1697 if (return_value == B_PANELIZE && *filename)
1699 int status, link_to_dir, stale_link;
1700 int next_free = 0;
1701 int i;
1702 struct stat st;
1703 GList *entry;
1704 dir_list *list = &current_panel->dir;
1705 char *name = NULL;
1707 if (set_zero_dir (list))
1708 next_free++;
1710 for (i = 0, entry = find_list->list; entry != NULL; i++, entry = g_list_next (entry))
1712 const char *lc_filename = NULL;
1713 WLEntry *le = (WLEntry *) entry->data;
1714 char *p;
1716 if ((le->text == NULL) || (le->data == NULL))
1717 continue;
1719 if (content_pattern != NULL)
1720 lc_filename = strchr (le->text + 4, ':') + 1;
1721 else
1722 lc_filename = le->text + 4;
1724 name = mc_build_filename (le->data, lc_filename, (char *) NULL);
1725 /* skip initial start dir */
1726 if (start_dir_len < 0)
1727 p = name;
1728 else
1730 p = name + (size_t) start_dir_len;
1731 if (*p == PATH_SEP)
1732 p++;
1735 status = handle_path (list, p, &st, next_free, &link_to_dir, &stale_link);
1736 if (status == 0)
1738 g_free (name);
1739 continue;
1741 if (status == -1)
1743 g_free (name);
1744 break;
1747 /* don't add files more than once to the panel */
1748 if (content_pattern != NULL && next_free > 0
1749 && strcmp (list->list[next_free - 1].fname, p) == 0)
1751 g_free (name);
1752 continue;
1755 if (next_free == 0) /* first turn i.e clean old list */
1756 panel_clean_dir (current_panel);
1757 list->list[next_free].fnamelen = strlen (p);
1758 list->list[next_free].fname = g_strndup (p, list->list[next_free].fnamelen);
1759 list->list[next_free].f.marked = 0;
1760 list->list[next_free].f.link_to_dir = link_to_dir;
1761 list->list[next_free].f.stale_link = stale_link;
1762 list->list[next_free].f.dir_size_computed = 0;
1763 list->list[next_free].st = st;
1764 list->list[next_free].sort_key = NULL;
1765 list->list[next_free].second_sort_key = NULL;
1766 next_free++;
1767 g_free (name);
1768 if (!(next_free & 15))
1769 rotate_dash ();
1772 if (next_free)
1774 current_panel->count = next_free;
1775 current_panel->is_panelized = TRUE;
1777 /* absolute path */
1778 if (start_dir_len < 0)
1780 int ret;
1781 vfs_path_free (current_panel->cwd_vpath);
1782 current_panel->cwd_vpath = vfs_path_from_str (PATH_SEP_STR);
1783 ret = chdir (PATH_SEP_STR);
1785 panelize_save_panel (current_panel);
1789 g_free (content_pattern);
1790 kill_gui ();
1791 do_search (NULL); /* force do_search to release resources */
1792 g_free (old_dir);
1793 old_dir = NULL;
1795 return return_value;
1798 /* --------------------------------------------------------------------------------------------- */
1799 /*** public functions ****************************************************************************/
1800 /* --------------------------------------------------------------------------------------------- */
1802 void
1803 find_file (void)
1805 char *start_dir = NULL, *pattern = NULL, *content = NULL, *ignore_dirs = NULL;
1806 ssize_t start_dir_len;
1807 char *filename = NULL, *dirname = NULL;
1808 int v;
1810 while (find_parameters (&start_dir, &start_dir_len, &ignore_dirs, &pattern, &content))
1812 if (pattern[0] == '\0')
1813 break; /* nothing search */
1815 dirname = filename = NULL;
1816 is_start = FALSE;
1817 v = do_find (start_dir, start_dir_len, ignore_dirs, pattern, content, &dirname, &filename);
1818 g_free (ignore_dirs);
1819 g_free (pattern);
1821 if (v == B_ENTER)
1823 if (dirname != NULL)
1825 vfs_path_t *dirname_vpath;
1827 dirname_vpath = vfs_path_from_str (dirname);
1828 do_cd (dirname_vpath, cd_exact);
1829 vfs_path_free (dirname_vpath);
1830 if (filename != NULL)
1831 try_to_select (current_panel,
1832 filename + (content != NULL
1833 ? strchr (filename + 4, ':') - filename + 1 : 4));
1835 else if (filename != NULL)
1837 vfs_path_t *filename_vpath;
1839 filename_vpath = vfs_path_from_str (filename);
1840 do_cd (filename_vpath, cd_exact);
1841 vfs_path_free (filename_vpath);
1844 g_free (dirname);
1845 g_free (filename);
1846 break;
1849 g_free (content);
1850 g_free (dirname);
1851 g_free (filename);
1853 if (v == B_CANCEL)
1854 break;
1856 if (v == B_PANELIZE)
1858 panel_re_sort (current_panel);
1859 try_to_select (current_panel, NULL);
1860 break;
1865 /* --------------------------------------------------------------------------------------------- */