Ticket #3598: cleanup some -Wcast-qual compiler warnings.
[midnight-commander.git] / src / filemanager / find.c
blobee5ad19c0fb02708975f103bb98ab179eef795ad
1 /*
2 Find file command for the Midnight Commander
4 Copyright (C) 1995-2016
5 Free Software Foundation, Inc.
7 Written by:
8 Miguel de Icaza, 1995
9 Slava Zanko <slavazanko@gmail.com>, 2013
10 Andrew Borodin <aborodin@vmail.ru>, 2013
12 This file is part of the Midnight Commander.
14 The Midnight Commander is free software: you can redistribute it
15 and/or modify it under the terms of the GNU General Public License as
16 published by the Free Software Foundation, either version 3 of the License,
17 or (at your option) any later version.
19 The Midnight Commander is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
24 You should have received a copy of the GNU General Public License
25 along with this program. If not, see <http://www.gnu.org/licenses/>.
28 /** \file find.c
29 * \brief Source: Find file command
32 #include <config.h>
34 #include <ctype.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <sys/stat.h>
39 #include <sys/time.h>
41 #include "lib/global.h"
43 #include "lib/tty/tty.h"
44 #include "lib/tty/key.h"
45 #include "lib/skin.h"
46 #include "lib/search.h"
47 #include "lib/mcconfig.h"
48 #include "lib/vfs/vfs.h"
49 #include "lib/strutil.h"
50 #include "lib/widget.h"
51 #include "lib/util.h" /* canonicalize_pathname() */
53 #include "src/setup.h" /* verbose */
54 #include "src/history.h" /* MC_HISTORY_SHARED_SEARCH */
56 #include "dir.h"
57 #include "cmd.h" /* view_file_at_line() */
58 #include "midnight.h" /* current_panel */
59 #include "boxes.h"
60 #include "panelize.h"
62 #include "find.h"
64 /*** global variables ****************************************************************************/
66 /*** file scope macro definitions ****************************************************************/
68 #define MAX_REFRESH_INTERVAL (G_USEC_PER_SEC / 20) /* 50 ms */
69 #define MIN_REFRESH_FILE_SIZE (256 * 1024) /* 256 KB */
71 /*** file scope type declarations ****************************************************************/
73 /* A couple of extra messages we need */
74 enum
76 B_STOP = B_USER + 1,
77 B_AGAIN,
78 B_PANELIZE,
79 B_TREE,
80 B_VIEW
83 typedef enum
85 FIND_CONT = 0,
86 FIND_SUSPEND,
87 FIND_ABORT
88 } FindProgressStatus;
90 /* find file options */
91 typedef struct
93 /* file name options */
94 gboolean file_case_sens;
95 gboolean file_pattern;
96 gboolean find_recurs;
97 gboolean skip_hidden;
98 gboolean file_all_charsets;
100 /* file content options */
101 gboolean content_case_sens;
102 gboolean content_regexp;
103 gboolean content_first_hit;
104 gboolean content_whole_words;
105 gboolean content_all_charsets;
107 /* whether use ignore dirs or not */
108 gboolean ignore_dirs_enable;
109 /* list of directories to be ignored, separated by ':' */
110 char *ignore_dirs;
111 } find_file_options_t;
113 typedef struct
115 char *dir;
116 gsize start;
117 gsize end;
118 } find_match_location_t;
120 /*** file scope variables ************************************************************************/
122 /* button callbacks */
123 static int start_stop (WButton * button, int action);
124 static int find_do_view_file (WButton * button, int action);
125 static int find_do_edit_file (WButton * button, int action);
127 /* Parsed ignore dirs */
128 static char **find_ignore_dirs = NULL;
130 /* static variables to remember find parameters */
131 static WInput *in_start; /* Start path */
132 static WInput *in_name; /* Filename */
133 static WInput *in_with; /* Text */
134 static WInput *in_ignore;
135 static WLabel *content_label; /* 'Content:' label */
136 static WCheck *file_case_sens_cbox; /* "case sensitive" checkbox */
137 static WCheck *file_pattern_cbox; /* File name is glob or regexp */
138 static WCheck *recursively_cbox;
139 static WCheck *skip_hidden_cbox;
140 static WCheck *content_case_sens_cbox; /* "case sensitive" checkbox */
141 static WCheck *content_regexp_cbox; /* "find regular expression" checkbox */
142 static WCheck *content_first_hit_cbox; /* "First hit" checkbox" */
143 static WCheck *content_whole_words_cbox; /* "whole words" checkbox */
144 #ifdef HAVE_CHARSET
145 static WCheck *file_all_charsets_cbox;
146 static WCheck *content_all_charsets_cbox;
147 #endif
148 static WCheck *ignore_dirs_cbox;
150 static gboolean running = FALSE; /* nice flag */
151 static char *find_pattern = NULL; /* Pattern to search */
152 static char *content_pattern = NULL; /* pattern to search inside files; if
153 content_regexp_flag is true, it contains the
154 regex pattern, else the search string. */
155 static unsigned long matches; /* Number of matches */
156 static gboolean is_start = FALSE; /* Status of the start/stop toggle button */
157 static char *old_dir = NULL;
159 static struct timeval last_refresh;
161 /* Where did we stop */
162 static gboolean resuming;
163 static int last_line;
164 static int last_pos;
165 static off_t last_off;
166 static int last_i;
168 static size_t ignore_count = 0;
170 static WDialog *find_dlg; /* The dialog */
171 static WLabel *status_label; /* Finished, Searching etc. */
172 static WLabel *found_num_label; /* Number of found items */
174 /* This keeps track of the directory stack */
175 static GQueue dir_queue = G_QUEUE_INIT;
177 /* *INDENT-OFF* */
178 static struct
180 int ret_cmd;
181 button_flags_t flags;
182 const char *text;
183 int len; /* length including space and brackets */
184 int x;
185 Widget *button;
186 bcback_fn callback;
187 } fbuts[] =
189 { B_ENTER, DEFPUSH_BUTTON, N_("&Chdir"), 0, 0, NULL, NULL },
190 { B_AGAIN, NORMAL_BUTTON, N_("&Again"), 0, 0, NULL, NULL },
191 { B_STOP, NORMAL_BUTTON, N_("S&uspend"), 0, 0, NULL, start_stop },
192 { B_STOP, NORMAL_BUTTON, N_("Con&tinue"), 0, 0, NULL, NULL },
193 { B_CANCEL, NORMAL_BUTTON, N_("&Quit"), 0, 0, NULL, NULL },
195 { B_PANELIZE, NORMAL_BUTTON, N_("Pane&lize"), 0, 0, NULL, NULL },
196 { B_VIEW, NORMAL_BUTTON, N_("&View - F3"), 0, 0, NULL, find_do_view_file },
197 { B_VIEW, NORMAL_BUTTON, N_("&Edit - F4"), 0, 0, NULL, find_do_edit_file }
199 /* *INDENT-ON* */
201 static const size_t fbuts_num = G_N_ELEMENTS (fbuts);
202 static const size_t quit_button = 4; /* index of "Quit" button */
204 static WListbox *find_list; /* Listbox with the file list */
206 static find_file_options_t options = {
207 TRUE, TRUE, TRUE, FALSE, FALSE,
208 TRUE, FALSE, FALSE, FALSE, FALSE,
209 FALSE, NULL
212 static char *in_start_dir = INPUT_LAST_TEXT;
214 static mc_search_t *search_file_handle = NULL;
215 static mc_search_t *search_content_handle = NULL;
217 /* --------------------------------------------------------------------------------------------- */
218 /*** file scope functions ************************************************************************/
219 /* --------------------------------------------------------------------------------------------- */
221 /* don't use max macro to avoid double str_term_width1() call in widget length caclulation */
222 #undef max
224 static int
225 max (int a, int b)
227 return (a > b ? a : b);
230 /* --------------------------------------------------------------------------------------------- */
232 static void
233 parse_ignore_dirs (const char *ignore_dirs)
235 size_t r = 0, w = 0; /* read and write iterators */
237 if (!options.ignore_dirs_enable || ignore_dirs == NULL || ignore_dirs[0] == '\0')
238 return;
240 find_ignore_dirs = g_strsplit (ignore_dirs, ":", -1);
242 /* Values like '/foo::/bar: produce holes in list.
243 * Find and remove them */
244 for (; find_ignore_dirs[r] != NULL; r++)
246 if (find_ignore_dirs[r][0] == '\0')
248 /* empty entry -- skip it */
249 MC_PTR_FREE (find_ignore_dirs[r]);
250 continue;
253 if (r != w)
255 /* copy entry to the previous free array cell */
256 find_ignore_dirs[w] = find_ignore_dirs[r];
257 find_ignore_dirs[r] = NULL;
260 canonicalize_pathname (find_ignore_dirs[w]);
261 if (find_ignore_dirs[w][0] != '\0')
262 w++;
263 else
264 MC_PTR_FREE (find_ignore_dirs[w]);
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_case_sens =
296 mc_config_get_bool (mc_main_config, "FindFile", "content_case_sens", TRUE);
297 options.content_regexp =
298 mc_config_get_bool (mc_main_config, "FindFile", "content_regexp", FALSE);
299 options.content_first_hit =
300 mc_config_get_bool (mc_main_config, "FindFile", "content_first_hit", FALSE);
301 options.content_whole_words =
302 mc_config_get_bool (mc_main_config, "FindFile", "content_whole_words", FALSE);
303 options.content_all_charsets =
304 mc_config_get_bool (mc_main_config, "FindFile", "content_all_charsets", FALSE);
305 options.ignore_dirs_enable =
306 mc_config_get_bool (mc_main_config, "FindFile", "ignore_dirs_enable", TRUE);
307 options.ignore_dirs = mc_config_get_string (mc_main_config, "FindFile", "ignore_dirs", "");
309 if (options.ignore_dirs[0] == '\0')
310 MC_PTR_FREE (options.ignore_dirs);
313 /* --------------------------------------------------------------------------------------------- */
315 static void
316 find_save_options (void)
318 mc_config_set_bool (mc_main_config, "FindFile", "file_case_sens", options.file_case_sens);
319 mc_config_set_bool (mc_main_config, "FindFile", "file_shell_pattern", options.file_pattern);
320 mc_config_set_bool (mc_main_config, "FindFile", "file_find_recurs", options.find_recurs);
321 mc_config_set_bool (mc_main_config, "FindFile", "file_skip_hidden", options.skip_hidden);
322 mc_config_set_bool (mc_main_config, "FindFile", "file_all_charsets", options.file_all_charsets);
323 mc_config_set_bool (mc_main_config, "FindFile", "content_case_sens", options.content_case_sens);
324 mc_config_set_bool (mc_main_config, "FindFile", "content_regexp", options.content_regexp);
325 mc_config_set_bool (mc_main_config, "FindFile", "content_first_hit", options.content_first_hit);
326 mc_config_set_bool (mc_main_config, "FindFile", "content_whole_words",
327 options.content_whole_words);
328 mc_config_set_bool (mc_main_config, "FindFile", "content_all_charsets",
329 options.content_all_charsets);
330 mc_config_set_bool (mc_main_config, "FindFile", "ignore_dirs_enable",
331 options.ignore_dirs_enable);
332 mc_config_set_string (mc_main_config, "FindFile", "ignore_dirs", options.ignore_dirs);
335 /* --------------------------------------------------------------------------------------------- */
337 static inline char *
338 add_to_list (const char *text, void *data)
340 return listbox_add_item (find_list, LISTBOX_APPEND_AT_END, 0, text, data, TRUE);
343 /* --------------------------------------------------------------------------------------------- */
345 static inline void
346 stop_idle (void *data)
348 widget_want_idle (WIDGET (data), FALSE);
351 /* --------------------------------------------------------------------------------------------- */
353 static inline void
354 status_update (const char *text)
356 label_set_text (status_label, text);
359 /* --------------------------------------------------------------------------------------------- */
361 static void
362 found_num_update (void)
364 char buffer[BUF_TINY];
365 g_snprintf (buffer, sizeof (buffer), _("Found: %ld"), matches);
366 label_set_text (found_num_label, buffer);
369 /* --------------------------------------------------------------------------------------------- */
371 static void
372 get_list_info (char **file, char **dir, gsize * start, gsize * end)
374 find_match_location_t *location;
376 listbox_get_current (find_list, file, (void **) &location);
377 if (location != NULL)
379 if (dir != NULL)
380 *dir = location->dir;
381 if (start != NULL)
382 *start = location->start;
383 if (end != NULL)
384 *end = location->end;
386 else
388 if (dir != NULL)
389 *dir = NULL;
393 /* --------------------------------------------------------------------------------------------- */
394 /** check regular expression */
396 static gboolean
397 find_check_regexp (const char *r)
399 mc_search_t *search;
400 gboolean regexp_ok = FALSE;
402 search = mc_search_new (r, NULL);
404 if (search != NULL)
406 search->search_type = MC_SEARCH_T_REGEX;
407 regexp_ok = mc_search_prepare (search);
408 mc_search_free (search);
411 return regexp_ok;
414 /* --------------------------------------------------------------------------------------------- */
416 static void
417 find_toggle_enable_params (void)
419 gboolean disable = in_name->buffer[0] == '\0';
421 widget_disable (WIDGET (file_pattern_cbox), disable);
422 widget_disable (WIDGET (file_case_sens_cbox), disable);
423 #ifdef HAVE_CHARSET
424 widget_disable (WIDGET (file_all_charsets_cbox), disable);
425 #endif
428 /* --------------------------------------------------------------------------------------------- */
430 static void
431 find_toggle_enable_content (void)
433 gboolean disable = in_with->buffer[0] == '\0';
435 widget_disable (WIDGET (content_regexp_cbox), disable);
436 widget_disable (WIDGET (content_case_sens_cbox), disable);
437 #ifdef HAVE_CHARSET
438 widget_disable (WIDGET (content_all_charsets_cbox), disable);
439 #endif
440 widget_disable (WIDGET (content_whole_words_cbox), disable);
441 widget_disable (WIDGET (content_first_hit_cbox), disable);
444 /* --------------------------------------------------------------------------------------------- */
446 * Callback for the parameter dialog.
447 * Validate regex, prevent closing the dialog if it's invalid.
450 static cb_ret_t
451 find_parm_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
453 /* FIXME: HACK: use first draw of dialog to resolve widget state dependencies.
454 * Use this time moment to check input field content. We can't do that in MSG_INIT
455 * because history is not loaded yet.
456 * Probably, we want new MSG_ACTIVATE message as complement to MSG_VALIDATE one. Or
457 * we could name it MSG_POST_INIT.
459 * In one or two other places we use MSG_IDLE instead of MSG_DRAW for a similar
460 * purpose. We should remember to fix those places too when we introduce the new
461 * message.
463 static gboolean first_draw = TRUE;
465 WDialog *h = DIALOG (w);
467 switch (msg)
469 case MSG_INIT:
470 first_draw = TRUE;
471 return MSG_HANDLED;
473 case MSG_NOTIFY:
474 if (sender == WIDGET (ignore_dirs_cbox))
476 gboolean disable = !(ignore_dirs_cbox->state & C_BOOL);
478 widget_disable (WIDGET (in_ignore), disable);
480 return MSG_HANDLED;
483 return MSG_NOT_HANDLED;
485 case MSG_VALIDATE:
486 if (h->ret_value != B_ENTER)
487 return MSG_HANDLED;
489 /* check filename regexp */
490 if (!(file_pattern_cbox->state & C_BOOL)
491 && (in_name->buffer[0] != '\0') && !find_check_regexp (in_name->buffer))
493 h->state = DLG_ACTIVE; /* Don't stop the dialog */
494 message (D_ERROR, MSG_ERROR, _("Malformed regular expression"));
495 dlg_select_widget (in_name);
496 return MSG_HANDLED;
499 /* check content regexp */
500 if ((content_regexp_cbox->state & C_BOOL) && (in_with->buffer[0] != '\0')
501 && !find_check_regexp (in_with->buffer))
503 h->state = DLG_ACTIVE; /* Don't stop the dialog */
504 message (D_ERROR, MSG_ERROR, _("Malformed regular expression"));
505 dlg_select_widget (in_with);
506 return MSG_HANDLED;
509 return MSG_HANDLED;
511 case MSG_POST_KEY:
512 if (h->current->data == in_name)
513 find_toggle_enable_params ();
514 else if (h->current->data == in_with)
515 find_toggle_enable_content ();
516 return MSG_HANDLED;
518 case MSG_DRAW:
519 if (first_draw)
521 find_toggle_enable_params ();
522 find_toggle_enable_content ();
525 first_draw = FALSE;
526 /* fall through to call MSG_DRAW default handler */
528 default:
529 return dlg_default_callback (w, sender, msg, parm, data);
533 /* --------------------------------------------------------------------------------------------- */
535 * find_parameters: gets information from the user
537 * If the return value is TRUE, then the following holds:
539 * start_dir, ignore_dirs, pattern and content contain the information provided by the user.
540 * They are newly allocated strings and must be freed when uneeded.
542 * start_dir_len is -1 when user entered an absolute path, otherwise it is a length
543 * of start_dir (which is absolute). It is used to get a relative pats of find results.
546 static gboolean
547 find_parameters (char **start_dir, ssize_t * start_dir_len,
548 char **ignore_dirs, char **pattern, char **content)
550 /* Size of the find parameters window */
551 #ifdef HAVE_CHARSET
552 const int lines = 18;
553 #else
554 const int lines = 17;
555 #endif
556 int cols = 68;
558 gboolean return_value;
560 /* file name */
561 const char *file_name_label = N_("File name:");
562 const char *file_recurs_label = N_("&Find recursively");
563 const char *file_pattern_label = N_("&Using shell patterns");
564 #ifdef HAVE_CHARSET
565 const char *file_all_charsets_label = N_("&All charsets");
566 #endif
567 const char *file_case_label = N_("Cas&e sensitive");
568 const char *file_skip_hidden_label = N_("S&kip hidden");
570 /* file content */
571 const char *content_content_label = N_("Content:");
572 const char *content_use_label = N_("Sea&rch for content");
573 const char *content_regexp_label = N_("Re&gular expression");
574 const char *content_case_label = N_("Case sens&itive");
575 #ifdef HAVE_CHARSET
576 const char *content_all_charsets_label = N_("A&ll charsets");
577 #endif
578 const char *content_whole_words_label = N_("&Whole words");
579 const char *content_first_hit_label = N_("Fir&st hit");
581 const char *buts[] = { N_("&Tree"), N_("&OK"), N_("&Cancel") };
583 /* button lengths */
584 int b0, b1, b2, b12;
585 int y1, y2, x1, x2;
586 /* column width */
587 int cw;
589 #ifdef ENABLE_NLS
591 size_t i;
593 file_name_label = _(file_name_label);
594 file_recurs_label = _(file_recurs_label);
595 file_pattern_label = _(file_pattern_label);
596 #ifdef HAVE_CHARSET
597 file_all_charsets_label = _(file_all_charsets_label);
598 #endif
599 file_case_label = _(file_case_label);
600 file_skip_hidden_label = _(file_skip_hidden_label);
602 /* file content */
603 content_content_label = _(content_content_label);
604 content_use_label = _(content_use_label);
605 content_regexp_label = _(content_regexp_label);
606 content_case_label = _(content_case_label);
607 #ifdef HAVE_CHARSET
608 content_all_charsets_label = _(content_all_charsets_label);
609 #endif
610 content_whole_words_label = _(content_whole_words_label);
611 content_first_hit_label = _(content_first_hit_label);
613 for (i = 0; i < G_N_ELEMENTS (buts); i++)
614 buts[i] = _(buts[i]);
616 #endif /* ENABLE_NLS */
618 /* caclulate dialog width */
620 /* widget widths */
621 cw = str_term_width1 (file_name_label);
622 cw = max (cw, str_term_width1 (file_recurs_label) + 4);
623 cw = max (cw, str_term_width1 (file_pattern_label) + 4);
624 #ifdef HAVE_CHARSET
625 cw = max (cw, str_term_width1 (file_all_charsets_label) + 4);
626 #endif
627 cw = max (cw, str_term_width1 (file_case_label) + 4);
628 cw = max (cw, str_term_width1 (file_skip_hidden_label) + 4);
630 cw = max (cw, str_term_width1 (content_content_label) + 4);
631 cw = max (cw, str_term_width1 (content_use_label) + 4);
632 cw = max (cw, str_term_width1 (content_regexp_label) + 4);
633 cw = max (cw, str_term_width1 (content_case_label) + 4);
634 #ifdef HAVE_CHARSET
635 cw = max (cw, str_term_width1 (content_all_charsets_label) + 4);
636 #endif
637 cw = max (cw, str_term_width1 (content_whole_words_label) + 4);
638 cw = max (cw, str_term_width1 (content_first_hit_label) + 4);
640 /* button width */
641 b0 = str_term_width1 (buts[0]) + 3;
642 b1 = str_term_width1 (buts[1]) + 5; /* default button */
643 b2 = str_term_width1 (buts[2]) + 3;
644 b12 = b1 + b2 + 1;
646 cols = max (cols, max (b12, cw * 2 + 1) + 6);
648 find_load_options ();
650 if (in_start_dir == NULL)
651 in_start_dir = g_strdup (".");
653 find_dlg =
654 dlg_create (TRUE, 0, 0, lines, cols, dialog_colors, find_parm_callback, NULL, "[Find File]",
655 _("Find File"), DLG_CENTER);
657 x1 = 3;
658 x2 = cols / 2 + 1;
659 cw = (cols - 7) / 2;
660 y1 = 2;
662 add_widget (find_dlg, label_new (y1++, x1, _("Start at:")));
663 in_start =
664 input_new (y1, x1, input_colors, cols - b0 - 7, in_start_dir, "start",
665 INPUT_COMPLETE_CD | INPUT_COMPLETE_FILENAMES);
666 add_widget (find_dlg, in_start);
668 add_widget (find_dlg, button_new (y1++, cols - b0 - 3, B_TREE, NORMAL_BUTTON, buts[0], NULL));
670 ignore_dirs_cbox =
671 check_new (y1++, x1, options.ignore_dirs_enable, _("Ena&ble ignore directories:"));
672 add_widget (find_dlg, ignore_dirs_cbox);
674 in_ignore =
675 input_new (y1++, x1, input_colors, cols - 6,
676 options.ignore_dirs != NULL ? options.ignore_dirs : "", "ignoredirs",
677 INPUT_COMPLETE_CD | INPUT_COMPLETE_FILENAMES);
678 add_widget (find_dlg, in_ignore);
680 add_widget (find_dlg, hline_new (y1++, -1, -1));
682 y2 = y1;
684 /* Start 1st column */
685 add_widget (find_dlg, label_new (y1++, x1, file_name_label));
686 in_name =
687 input_new (y1++, x1, input_colors, cw, INPUT_LAST_TEXT, "name",
688 INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_CD);
689 add_widget (find_dlg, in_name);
691 /* Start 2nd column */
692 content_label = label_new (y2++, x2, content_content_label);
693 add_widget (find_dlg, content_label);
694 in_with =
695 input_new (y2++, x2, input_colors, cw, INPUT_LAST_TEXT,
696 MC_HISTORY_SHARED_SEARCH, INPUT_COMPLETE_NONE);
697 in_with->label = content_label;
698 add_widget (find_dlg, in_with);
700 /* Continue 1st column */
701 recursively_cbox = check_new (y1++, x1, options.find_recurs, file_recurs_label);
702 add_widget (find_dlg, recursively_cbox);
704 file_pattern_cbox = check_new (y1++, x1, options.file_pattern, file_pattern_label);
705 add_widget (find_dlg, file_pattern_cbox);
707 file_case_sens_cbox = check_new (y1++, x1, options.file_case_sens, file_case_label);
708 add_widget (find_dlg, file_case_sens_cbox);
710 #ifdef HAVE_CHARSET
711 file_all_charsets_cbox =
712 check_new (y1++, x1, options.file_all_charsets, file_all_charsets_label);
713 add_widget (find_dlg, file_all_charsets_cbox);
714 #endif
716 skip_hidden_cbox = check_new (y1++, x1, options.skip_hidden, file_skip_hidden_label);
717 add_widget (find_dlg, skip_hidden_cbox);
719 /* Continue 2nd column */
720 content_whole_words_cbox =
721 check_new (y2++, x2, options.content_whole_words, content_whole_words_label);
722 add_widget (find_dlg, content_whole_words_cbox);
724 content_regexp_cbox = check_new (y2++, x2, options.content_regexp, content_regexp_label);
725 add_widget (find_dlg, content_regexp_cbox);
727 content_case_sens_cbox = check_new (y2++, x2, options.content_case_sens, content_case_label);
728 add_widget (find_dlg, content_case_sens_cbox);
730 #ifdef HAVE_CHARSET
731 content_all_charsets_cbox =
732 check_new (y2++, x2, options.content_all_charsets, content_all_charsets_label);
733 add_widget (find_dlg, content_all_charsets_cbox);
734 #endif
736 content_first_hit_cbox =
737 check_new (y2++, x2, options.content_first_hit, content_first_hit_label);
738 add_widget (find_dlg, content_first_hit_cbox);
740 /* buttons */
741 y1 = max (y1, y2);
742 x1 = (cols - b12) / 2;
743 add_widget (find_dlg, hline_new (y1++, -1, -1));
744 add_widget (find_dlg, button_new (y1, x1, B_ENTER, DEFPUSH_BUTTON, buts[1], NULL));
745 add_widget (find_dlg, button_new (y1, x1 + b1 + 1, B_CANCEL, NORMAL_BUTTON, buts[2], NULL));
747 find_par_start:
748 dlg_select_widget (in_name);
750 switch (dlg_run (find_dlg))
752 case B_CANCEL:
753 return_value = FALSE;
754 break;
756 case B_TREE:
758 char *temp_dir;
760 temp_dir = in_start->buffer;
761 if (*temp_dir == '\0' || DIR_IS_DOT (temp_dir))
762 temp_dir = g_strdup (vfs_path_as_str (current_panel->cwd_vpath));
763 else
764 temp_dir = g_strdup (temp_dir);
766 if (in_start_dir != INPUT_LAST_TEXT)
767 g_free (in_start_dir);
768 in_start_dir = tree_box (temp_dir);
769 if (in_start_dir == NULL)
770 in_start_dir = temp_dir;
771 else
772 g_free (temp_dir);
774 input_assign_text (in_start, in_start_dir);
776 /* Warning: Dreadful goto */
777 goto find_par_start;
780 default:
782 char *s;
784 #ifdef HAVE_CHARSET
785 options.file_all_charsets = file_all_charsets_cbox->state & C_BOOL;
786 options.content_all_charsets = content_all_charsets_cbox->state & C_BOOL;
787 #endif
788 options.content_case_sens = content_case_sens_cbox->state & C_BOOL;
789 options.content_regexp = content_regexp_cbox->state & C_BOOL;
790 options.content_first_hit = content_first_hit_cbox->state & C_BOOL;
791 options.content_whole_words = content_whole_words_cbox->state & C_BOOL;
792 options.find_recurs = recursively_cbox->state & C_BOOL;
793 options.file_pattern = file_pattern_cbox->state & C_BOOL;
794 options.file_case_sens = file_case_sens_cbox->state & C_BOOL;
795 options.skip_hidden = skip_hidden_cbox->state & C_BOOL;
796 options.ignore_dirs_enable = ignore_dirs_cbox->state & C_BOOL;
797 g_free (options.ignore_dirs);
798 options.ignore_dirs = g_strdup (in_ignore->buffer);
800 *content = in_with->buffer[0] != '\0' ? g_strdup (in_with->buffer) : NULL;
801 if (in_name->buffer[0] != '\0')
802 *pattern = g_strdup (in_name->buffer);
803 else
804 *pattern = g_strdup (options.file_pattern ? "*" : ".*");
805 *start_dir = in_start->buffer[0] != '\0' ? in_start->buffer : (char *) ".";
806 if (in_start_dir != INPUT_LAST_TEXT)
807 g_free (in_start_dir);
808 in_start_dir = g_strdup (*start_dir);
810 s = tilde_expand (*start_dir);
811 canonicalize_pathname (s);
813 if (DIR_IS_DOT (s))
815 *start_dir = g_strdup (vfs_path_as_str (current_panel->cwd_vpath));
816 /* FIXME: is current_panel->cwd_vpath canonicalized? */
817 /* relative paths will be used in panelization */
818 *start_dir_len = (ssize_t) strlen (*start_dir);
819 g_free (s);
821 else if (g_path_is_absolute (s))
823 *start_dir = s;
824 *start_dir_len = -1;
826 else
828 /* relative paths will be used in panelization */
829 *start_dir =
830 mc_build_filename (vfs_path_as_str (current_panel->cwd_vpath), s,
831 (char *) NULL);
832 *start_dir_len = (ssize_t) strlen (vfs_path_as_str (current_panel->cwd_vpath));
833 g_free (s);
836 if (!options.ignore_dirs_enable || in_ignore->buffer[0] == '\0'
837 || DIR_IS_DOT (in_ignore->buffer))
838 *ignore_dirs = NULL;
839 else
840 *ignore_dirs = g_strdup (in_ignore->buffer);
842 find_save_options ();
844 return_value = TRUE;
848 dlg_destroy (find_dlg);
850 return return_value;
853 /* --------------------------------------------------------------------------------------------- */
855 static inline void
856 push_directory (const vfs_path_t * dir)
858 g_queue_push_head (&dir_queue, (void *) dir);
861 /* --------------------------------------------------------------------------------------------- */
863 static inline vfs_path_t *
864 pop_directory (void)
866 return (vfs_path_t *) g_queue_pop_tail (&dir_queue);
869 /* --------------------------------------------------------------------------------------------- */
870 /** Remove all the items from the stack */
872 static void
873 clear_stack (void)
875 g_queue_foreach (&dir_queue, (GFunc) vfs_path_free, NULL);
876 g_queue_clear (&dir_queue);
879 /* --------------------------------------------------------------------------------------------- */
881 static void
882 insert_file (const char *dir, const char *file, gsize start, gsize end)
884 char *tmp_name = NULL;
885 static char *dirname = NULL;
886 find_match_location_t *location;
888 while (IS_PATH_SEP (dir[0]) && IS_PATH_SEP (dir[1]))
889 dir++;
891 if (old_dir)
893 if (strcmp (old_dir, dir))
895 g_free (old_dir);
896 old_dir = g_strdup (dir);
897 dirname = add_to_list (dir, NULL);
900 else
902 old_dir = g_strdup (dir);
903 dirname = add_to_list (dir, NULL);
906 tmp_name = g_strdup_printf (" %s", file);
907 location = g_malloc (sizeof (*location));
908 location->dir = dirname;
909 location->start = start;
910 location->end = end;
911 add_to_list (tmp_name, location);
912 g_free (tmp_name);
915 /* --------------------------------------------------------------------------------------------- */
917 static void
918 find_add_match (const char *dir, const char *file, gsize start, gsize end)
920 insert_file (dir, file, start, end);
922 /* Don't scroll */
923 if (matches == 0)
924 listbox_select_first (find_list);
925 widget_redraw (WIDGET (find_list));
927 matches++;
928 found_num_update ();
931 /* --------------------------------------------------------------------------------------------- */
933 static FindProgressStatus
934 check_find_events (WDialog * h)
936 Gpm_Event event;
937 int c;
939 event.x = -1;
940 c = tty_get_event (&event, h->mouse_status == MOU_REPEAT, FALSE);
941 if (c != EV_NONE)
943 dlg_process_event (h, c, &event);
944 if (h->ret_value == B_ENTER
945 || h->ret_value == B_CANCEL || h->ret_value == B_AGAIN || h->ret_value == B_PANELIZE)
947 /* dialog terminated */
948 return FIND_ABORT;
950 if ((WIDGET (h)->options & W_WANT_IDLE) == 0)
952 /* searching suspended */
953 return FIND_SUSPEND;
957 return FIND_CONT;
960 /* --------------------------------------------------------------------------------------------- */
962 * search_content:
964 * Search the content_pattern string in the DIRECTORY/FILE.
965 * It will add the found entries to the find listbox.
967 * returns FALSE if do_search should look for another file
968 * TRUE if do_search should exit and proceed to the event handler
971 static gboolean
972 search_content (WDialog * h, const char *directory, const char *filename)
974 struct stat s;
975 char buffer[BUF_4K]; /* raw input buffer */
976 int file_fd;
977 gboolean ret_val = FALSE;
978 vfs_path_t *vpath;
979 struct timeval tv;
980 time_t seconds;
981 suseconds_t useconds;
982 gboolean status_updated = FALSE;
984 vpath = vfs_path_build_filename (directory, filename, (char *) NULL);
986 if (mc_stat (vpath, &s) != 0 || !S_ISREG (s.st_mode))
988 vfs_path_free (vpath);
989 return FALSE;
992 file_fd = mc_open (vpath, O_RDONLY);
993 vfs_path_free (vpath);
995 if (file_fd == -1)
996 return FALSE;
998 /* get time elapsed from last refresh */
999 if (gettimeofday (&tv, NULL) == -1)
1001 tv.tv_sec = 0;
1002 tv.tv_usec = 0;
1003 last_refresh = tv;
1005 seconds = tv.tv_sec - last_refresh.tv_sec;
1006 useconds = tv.tv_usec - last_refresh.tv_usec;
1007 if (useconds < 0)
1009 seconds--;
1010 useconds += G_USEC_PER_SEC;
1013 if (s.st_size >= MIN_REFRESH_FILE_SIZE || seconds > 0 || useconds > MAX_REFRESH_INTERVAL)
1015 g_snprintf (buffer, sizeof (buffer), _("Grepping in %s"), filename);
1016 status_update (str_trunc (buffer, WIDGET (h)->cols - 8));
1017 mc_refresh ();
1018 last_refresh = tv;
1019 status_updated = TRUE;
1022 tty_enable_interrupt_key ();
1023 tty_got_interrupt ();
1026 int line = 1;
1027 int pos = 0;
1028 int n_read = 0;
1029 off_t off = 0; /* file_fd's offset corresponding to strbuf[0] */
1030 gboolean found = FALSE;
1031 gsize found_len;
1032 gsize found_start;
1033 char result[BUF_MEDIUM];
1034 char *strbuf = NULL; /* buffer for fetched string */
1035 int strbuf_size = 0;
1036 int i = -1; /* compensate for a newline we'll add when we first enter the loop */
1038 if (resuming)
1040 /* We've been previously suspended, start from the previous position */
1041 resuming = FALSE;
1042 line = last_line;
1043 pos = last_pos;
1044 off = last_off;
1045 i = last_i;
1048 while (!ret_val)
1050 char ch = '\0';
1051 off += i + 1; /* the previous line, plus a newline character */
1052 i = 0;
1054 /* read to buffer and get line from there */
1055 while (TRUE)
1057 if (pos >= n_read)
1059 pos = 0;
1060 n_read = mc_read (file_fd, buffer, sizeof (buffer));
1061 if (n_read <= 0)
1062 break;
1065 ch = buffer[pos++];
1066 if (ch == '\0')
1068 /* skip possible leading zero(s) */
1069 if (i == 0)
1071 off++;
1072 continue;
1074 break;
1077 if (i >= strbuf_size - 1)
1079 strbuf_size += 128;
1080 strbuf = g_realloc (strbuf, strbuf_size);
1083 /* Strip newline */
1084 if (ch == '\n')
1085 break;
1087 strbuf[i++] = ch;
1090 if (i == 0)
1092 if (ch == '\0')
1093 break;
1095 /* if (ch == '\n'): do not search in empty strings */
1096 goto skip_search;
1099 strbuf[i] = '\0';
1101 if (!found /* Search in binary line once */
1102 && mc_search_run (search_content_handle, (const void *) strbuf, 0, i, &found_len))
1104 if (!status_updated)
1106 /* if we add results for a file, we have to ensure that
1107 name of this file is shown in status bar */
1108 g_snprintf (result, sizeof (result), _("Grepping in %s"), filename);
1109 status_update (str_trunc (result, WIDGET (h)->cols - 8));
1110 mc_refresh ();
1111 last_refresh = tv;
1112 status_updated = TRUE;
1115 g_snprintf (result, sizeof (result), "%d:%s", line, filename);
1116 found_start = off + search_content_handle->normal_offset + 1; /* off by one: ticket 3280 */
1117 find_add_match (directory, result, found_start, found_start + found_len);
1118 found = TRUE;
1121 if (found && options.content_first_hit)
1122 break;
1124 if (ch == '\n')
1126 skip_search:
1127 found = FALSE;
1128 line++;
1131 if ((line & 0xff) == 0)
1133 FindProgressStatus res;
1134 res = check_find_events (h);
1135 switch (res)
1137 case FIND_ABORT:
1138 stop_idle (h);
1139 ret_val = TRUE;
1140 break;
1141 case FIND_SUSPEND:
1142 resuming = TRUE;
1143 last_line = line;
1144 last_pos = pos;
1145 last_off = off;
1146 last_i = i;
1147 ret_val = TRUE;
1148 break;
1149 default:
1150 break;
1155 g_free (strbuf);
1158 tty_disable_interrupt_key ();
1159 mc_close (file_fd);
1160 return ret_val;
1163 /* --------------------------------------------------------------------------------------------- */
1166 If dir is absolute, this means we're within dir and searching file here.
1167 If dir is relative, this means we're going to add dir to the directory stack.
1169 static gboolean
1170 find_ignore_dir_search (const char *dir)
1172 if (find_ignore_dirs != NULL)
1174 const size_t dlen = strlen (dir);
1175 const unsigned char dabs = g_path_is_absolute (dir) ? 1 : 0;
1177 char **ignore_dir;
1179 for (ignore_dir = find_ignore_dirs; *ignore_dir != NULL; ignore_dir++)
1181 const size_t ilen = strlen (*ignore_dir);
1182 const unsigned char iabs = g_path_is_absolute (*ignore_dir) ? 2 : 0;
1184 /* ignore dir is too long -- skip it */
1185 if (dlen < ilen)
1186 continue;
1188 /* handle absolute and relative paths */
1189 switch (iabs | dabs)
1191 case 0: /* both paths are relative */
1192 case 3: /* both paths are abolute */
1193 /* if ignore dir is not a path of dir -- skip it */
1194 if (strncmp (dir, *ignore_dir, ilen) == 0)
1196 /* be sure that ignore dir is not a part of dir like:
1197 ignore dir is "h", dir is "home" */
1198 if (dir[ilen] == '\0' || IS_PATH_SEP (dir[ilen]))
1199 return TRUE;
1201 break;
1202 case 1: /* dir is absolute, ignore_dir is relative */
1204 char *d;
1206 d = strstr (dir, *ignore_dir);
1207 if (d != NULL && IS_PATH_SEP (d[-1])
1208 && (d[ilen] == '\0' || IS_PATH_SEP (d[ilen])))
1209 return TRUE;
1211 break;
1212 case 2: /* dir is relative, ignore_dir is absolute */
1213 /* FIXME: skip this case */
1214 break;
1215 default: /* this cannot occurs */
1216 return FALSE;
1221 return FALSE;
1224 /* --------------------------------------------------------------------------------------------- */
1226 static void
1227 find_rotate_dash (const WDialog * h, gboolean show)
1229 static size_t pos = 0;
1230 static const char rotating_dash[4] = "|/-\\";
1231 const Widget *w = CONST_WIDGET (h);
1233 if (!verbose)
1234 return;
1236 tty_setcolor (h->color[DLG_COLOR_NORMAL]);
1237 widget_move (h, w->lines - 7, w->cols - 4);
1238 tty_print_char (show ? rotating_dash[pos] : ' ');
1239 pos = (pos + 1) % sizeof (rotating_dash);
1240 mc_refresh ();
1243 /* --------------------------------------------------------------------------------------------- */
1245 static int
1246 do_search (WDialog * h)
1248 static struct dirent *dp = NULL;
1249 static DIR *dirp = NULL;
1250 static char *directory = NULL;
1251 struct stat tmp_stat;
1252 gsize bytes_found;
1253 unsigned short count;
1255 if (h == NULL)
1256 { /* someone forces me to close dirp */
1257 if (dirp != NULL)
1259 mc_closedir (dirp);
1260 dirp = NULL;
1262 MC_PTR_FREE (directory);
1263 dp = NULL;
1264 return 1;
1267 for (count = 0; count < 32; count++)
1269 while (dp == NULL)
1271 if (dirp != NULL)
1273 mc_closedir (dirp);
1274 dirp = NULL;
1277 while (dirp == NULL)
1279 vfs_path_t *tmp_vpath = NULL;
1281 tty_setcolor (REVERSE_COLOR);
1283 while (TRUE)
1285 tmp_vpath = pop_directory ();
1286 if (tmp_vpath == NULL)
1288 running = FALSE;
1289 if (ignore_count == 0)
1290 status_update (_("Finished"));
1291 else
1293 char msg[BUF_SMALL];
1294 g_snprintf (msg, sizeof (msg),
1295 ngettext ("Finished (ignored %zd directory)",
1296 "Finished (ignored %zd directories)",
1297 ignore_count), ignore_count);
1298 status_update (msg);
1300 find_rotate_dash (h, FALSE);
1301 stop_idle (h);
1302 return 0;
1305 /* handle absolute ignore dirs here */
1307 gboolean ok;
1309 ok = find_ignore_dir_search (vfs_path_as_str (tmp_vpath));
1310 if (!ok)
1311 break;
1314 vfs_path_free (tmp_vpath);
1315 ignore_count++;
1318 g_free (directory);
1319 directory = g_strdup (vfs_path_as_str (tmp_vpath));
1321 if (verbose)
1323 char buffer[BUF_MEDIUM];
1325 g_snprintf (buffer, sizeof (buffer), _("Searching %s"), directory);
1326 status_update (str_trunc (directory, WIDGET (h)->cols - 8));
1329 dirp = mc_opendir (tmp_vpath);
1330 vfs_path_free (tmp_vpath);
1331 } /* while (!dirp) */
1333 /* skip invalid filenames */
1334 while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1336 } /* while (!dp) */
1338 if (DIR_IS_DOT (dp->d_name) || DIR_IS_DOTDOT (dp->d_name))
1340 /* skip invalid filenames */
1341 while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1344 return 1;
1347 if (!(options.skip_hidden && (dp->d_name[0] == '.')))
1349 gboolean search_ok;
1351 if (options.find_recurs && (directory != NULL))
1352 { /* Can directory be NULL ? */
1353 /* handle relative ignore dirs here */
1354 if (options.ignore_dirs_enable && find_ignore_dir_search (dp->d_name))
1355 ignore_count++;
1356 else
1358 vfs_path_t *tmp_vpath;
1360 tmp_vpath = vfs_path_build_filename (directory, dp->d_name, (char *) NULL);
1362 if (mc_lstat (tmp_vpath, &tmp_stat) == 0 && S_ISDIR (tmp_stat.st_mode))
1363 push_directory (tmp_vpath);
1364 else
1365 vfs_path_free (tmp_vpath);
1369 search_ok = mc_search_run (search_file_handle, dp->d_name,
1370 0, strlen (dp->d_name), &bytes_found);
1372 if (search_ok)
1374 if (content_pattern == NULL)
1375 find_add_match (directory, dp->d_name, 0, 0);
1376 else if (search_content (h, directory, dp->d_name))
1377 return 1;
1381 /* skip invalid filenames */
1382 while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1384 } /* for */
1386 find_rotate_dash (h, TRUE);
1388 return 1;
1391 /* --------------------------------------------------------------------------------------------- */
1393 static void
1394 init_find_vars (void)
1396 MC_PTR_FREE (old_dir);
1397 matches = 0;
1398 ignore_count = 0;
1400 /* Remove all the items from the stack */
1401 clear_stack ();
1403 g_strfreev (find_ignore_dirs);
1404 find_ignore_dirs = NULL;
1407 /* --------------------------------------------------------------------------------------------- */
1409 static void
1410 find_do_view_edit (gboolean unparsed_view, gboolean edit, char *dir, char *file, off_t search_start,
1411 off_t search_end)
1413 char *fullname = NULL;
1414 const char *filename = NULL;
1415 int line;
1416 vfs_path_t *fullname_vpath;
1418 if (content_pattern != NULL)
1420 filename = strchr (file + 4, ':') + 1;
1421 line = atoi (file + 4);
1423 else
1425 filename = file + 4;
1426 line = 0;
1429 fullname_vpath = vfs_path_build_filename (dir, filename, (char *) NULL);
1430 if (edit)
1431 edit_file_at_line (fullname_vpath, use_internal_edit != 0, line);
1432 else
1433 view_file_at_line (fullname_vpath, unparsed_view, use_internal_view != 0, line,
1434 search_start, search_end);
1435 vfs_path_free (fullname_vpath);
1436 g_free (fullname);
1439 /* --------------------------------------------------------------------------------------------- */
1441 static cb_ret_t
1442 view_edit_currently_selected_file (gboolean unparsed_view, gboolean edit)
1444 char *text = NULL;
1445 find_match_location_t *location;
1447 listbox_get_current (find_list, &text, (void **) &location);
1449 if ((text == NULL) || (location == NULL) || (location->dir == NULL))
1450 return MSG_NOT_HANDLED;
1452 find_do_view_edit (unparsed_view, edit, location->dir, text, location->start, location->end);
1453 return MSG_HANDLED;
1456 /* --------------------------------------------------------------------------------------------- */
1458 static void
1459 find_calc_button_locations (const WDialog * h, gboolean all_buttons)
1461 const int cols = CONST_WIDGET (h)->cols;
1463 int l1, l2;
1465 l1 = fbuts[0].len + fbuts[1].len + fbuts[is_start ? 3 : 2].len + fbuts[4].len + 3;
1466 l2 = fbuts[5].len + fbuts[6].len + fbuts[7].len + 2;
1468 fbuts[0].x = (cols - l1) / 2;
1469 fbuts[1].x = fbuts[0].x + fbuts[0].len + 1;
1470 fbuts[2].x = fbuts[1].x + fbuts[1].len + 1;
1471 fbuts[3].x = fbuts[2].x;
1472 fbuts[4].x = fbuts[2].x + fbuts[is_start ? 3 : 2].len + 1;
1474 if (all_buttons)
1476 fbuts[5].x = (cols - l2) / 2;
1477 fbuts[6].x = fbuts[5].x + fbuts[5].len + 1;
1478 fbuts[7].x = fbuts[6].x + fbuts[6].len + 1;
1482 /* --------------------------------------------------------------------------------------------- */
1484 static void
1485 find_relocate_buttons (const WDialog * h, gboolean all_buttons)
1487 size_t i;
1489 find_calc_button_locations (h, all_buttons);
1491 for (i = 0; i < fbuts_num; i++)
1492 fbuts[i].button->x = CONST_WIDGET (h)->x + fbuts[i].x;
1495 /* --------------------------------------------------------------------------------------------- */
1497 static cb_ret_t
1498 find_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
1500 WDialog *h = DIALOG (w);
1502 switch (msg)
1504 case MSG_KEY:
1505 if (parm == KEY_F (3) || parm == KEY_F (13))
1507 gboolean unparsed_view = (parm == KEY_F (13));
1509 return view_edit_currently_selected_file (unparsed_view, FALSE);
1511 if (parm == KEY_F (4))
1512 return view_edit_currently_selected_file (FALSE, TRUE);
1513 return MSG_NOT_HANDLED;
1515 case MSG_RESIZE:
1516 dlg_set_size (h, LINES - 4, COLS - 16);
1517 find_relocate_buttons (h, TRUE);
1518 return MSG_HANDLED;
1520 case MSG_IDLE:
1521 do_search (h);
1522 return MSG_HANDLED;
1524 default:
1525 return dlg_default_callback (w, sender, msg, parm, data);
1529 /* --------------------------------------------------------------------------------------------- */
1530 /** Handles the Stop/Start button in the find window */
1532 static int
1533 start_stop (WButton * button, int action)
1535 Widget *w = WIDGET (button);
1537 (void) action;
1539 running = is_start;
1540 widget_want_idle (WIDGET (find_dlg), running);
1541 is_start = !is_start;
1543 status_update (is_start ? _("Stopped") : _("Searching"));
1544 button_set_text (button, fbuts[is_start ? 3 : 2].text);
1546 find_relocate_buttons (w->owner, FALSE);
1547 dlg_redraw (w->owner);
1549 return 0;
1552 /* --------------------------------------------------------------------------------------------- */
1553 /** Handle view command, when invoked as a button */
1555 static int
1556 find_do_view_file (WButton * button, int action)
1558 (void) button;
1559 (void) action;
1561 view_edit_currently_selected_file (FALSE, FALSE);
1562 return 0;
1565 /* --------------------------------------------------------------------------------------------- */
1566 /** Handle edit command, when invoked as a button */
1568 static int
1569 find_do_edit_file (WButton * button, int action)
1571 (void) button;
1572 (void) action;
1574 view_edit_currently_selected_file (FALSE, TRUE);
1575 return 0;
1578 /* --------------------------------------------------------------------------------------------- */
1580 static void
1581 setup_gui (void)
1583 size_t i;
1584 int lines, cols;
1585 int y;
1587 static gboolean i18n_flag = FALSE;
1589 if (!i18n_flag)
1591 for (i = 0; i < fbuts_num; i++)
1593 #ifdef ENABLE_NLS
1594 fbuts[i].text = _(fbuts[i].text);
1595 #endif /* ENABLE_NLS */
1596 fbuts[i].len = str_term_width1 (fbuts[i].text) + 3;
1597 if (fbuts[i].flags == DEFPUSH_BUTTON)
1598 fbuts[i].len += 2;
1601 i18n_flag = TRUE;
1604 lines = LINES - 4;
1605 cols = COLS - 16;
1607 find_dlg =
1608 dlg_create (TRUE, 0, 0, lines, cols, dialog_colors, find_callback, NULL, "[Find File]",
1609 _("Find File"), DLG_CENTER);
1611 find_calc_button_locations (find_dlg, TRUE);
1613 y = 2;
1614 find_list = listbox_new (y, 2, lines - 10, cols - 4, FALSE, NULL);
1615 add_widget_autopos (find_dlg, find_list, WPOS_KEEP_ALL, NULL);
1616 y += WIDGET (find_list)->lines;
1618 add_widget_autopos (find_dlg, hline_new (y++, -1, -1), WPOS_KEEP_BOTTOM, NULL);
1620 found_num_label = label_new (y++, 4, "");
1621 add_widget_autopos (find_dlg, found_num_label, WPOS_KEEP_BOTTOM, NULL);
1623 status_label = label_new (y++, 4, _("Searching"));
1624 add_widget_autopos (find_dlg, status_label, WPOS_KEEP_BOTTOM, NULL);
1626 add_widget_autopos (find_dlg, hline_new (y++, -1, -1), WPOS_KEEP_BOTTOM, NULL);
1628 for (i = 0; i < fbuts_num; i++)
1630 if (i == 3)
1631 fbuts[3].button = fbuts[2].button;
1632 else
1634 fbuts[i].button =
1635 WIDGET (button_new
1636 (y, fbuts[i].x, fbuts[i].ret_cmd, fbuts[i].flags, fbuts[i].text,
1637 fbuts[i].callback));
1638 add_widget_autopos (find_dlg, fbuts[i].button, WPOS_KEEP_BOTTOM, NULL);
1641 if (i == quit_button)
1642 y++;
1645 dlg_select_widget (find_list);
1648 /* --------------------------------------------------------------------------------------------- */
1650 static int
1651 run_process (void)
1653 int ret;
1655 search_content_handle = mc_search_new (content_pattern, NULL);
1656 if (search_content_handle)
1658 search_content_handle->search_type =
1659 options.content_regexp ? MC_SEARCH_T_REGEX : MC_SEARCH_T_NORMAL;
1660 search_content_handle->is_case_sensitive = options.content_case_sens;
1661 search_content_handle->whole_words = options.content_whole_words;
1662 #ifdef HAVE_CHARSET
1663 search_content_handle->is_all_charsets = options.content_all_charsets;
1664 #endif
1666 search_file_handle = mc_search_new (find_pattern, NULL);
1667 search_file_handle->search_type = options.file_pattern ? MC_SEARCH_T_GLOB : MC_SEARCH_T_REGEX;
1668 search_file_handle->is_case_sensitive = options.file_case_sens;
1669 #ifdef HAVE_CHARSET
1670 search_file_handle->is_all_charsets = options.file_all_charsets;
1671 #endif
1672 search_file_handle->is_entire_line = options.file_pattern;
1674 resuming = FALSE;
1676 widget_want_idle (WIDGET (find_dlg), TRUE);
1677 ret = dlg_run (find_dlg);
1679 mc_search_free (search_file_handle);
1680 search_file_handle = NULL;
1681 mc_search_free (search_content_handle);
1682 search_content_handle = NULL;
1684 return ret;
1687 /* --------------------------------------------------------------------------------------------- */
1689 static void
1690 kill_gui (void)
1692 widget_want_idle (WIDGET (find_dlg), FALSE);
1693 dlg_destroy (find_dlg);
1696 /* --------------------------------------------------------------------------------------------- */
1698 static int
1699 do_find (const char *start_dir, ssize_t start_dir_len, const char *ignore_dirs,
1700 const char *pattern, const char *content, char **dirname, char **filename)
1702 int return_value = 0;
1703 char *dir_tmp = NULL, *file_tmp = NULL;
1705 setup_gui ();
1707 /* FIXME: Need to cleanup this, this ought to be passed non-globaly */
1708 find_pattern = (char *) pattern;
1710 content_pattern = NULL;
1711 if (content != NULL && str_is_valid_string (content))
1712 content_pattern = g_strdup (content);
1714 init_find_vars ();
1715 parse_ignore_dirs (ignore_dirs);
1716 push_directory (vfs_path_from_str (start_dir));
1718 return_value = run_process ();
1720 /* Clear variables */
1721 init_find_vars ();
1723 get_list_info (&file_tmp, &dir_tmp, NULL, NULL);
1725 if (dir_tmp)
1726 *dirname = g_strdup (dir_tmp);
1727 if (file_tmp)
1728 *filename = g_strdup (file_tmp);
1730 if (return_value == B_PANELIZE && *filename)
1732 int link_to_dir, stale_link;
1733 int i;
1734 struct stat st;
1735 GList *entry;
1736 dir_list *list = &current_panel->dir;
1737 char *name = NULL;
1739 dir_list_init (list);
1741 for (i = 0, entry = listbox_get_first_link (find_list); entry != NULL;
1742 i++, entry = g_list_next (entry))
1744 const char *lc_filename = NULL;
1745 WLEntry *le = LENTRY (entry->data);
1746 find_match_location_t *location = le->data;
1747 char *p;
1749 if ((le->text == NULL) || (location == NULL) || (location->dir == NULL))
1750 continue;
1752 if (content_pattern != NULL)
1753 lc_filename = strchr (le->text + 4, ':') + 1;
1754 else
1755 lc_filename = le->text + 4;
1757 name = mc_build_filename (location->dir, lc_filename, (char *) NULL);
1758 /* skip initial start dir */
1759 if (start_dir_len < 0)
1760 p = name;
1761 else
1763 p = name + (size_t) start_dir_len;
1764 if (IS_PATH_SEP (*p))
1765 p++;
1768 if (!handle_path (p, &st, &link_to_dir, &stale_link))
1770 g_free (name);
1771 continue;
1773 /* Need to grow the *list? */
1774 if (list->len == list->size && !dir_list_grow (list, DIR_LIST_RESIZE_STEP))
1776 g_free (name);
1777 break;
1780 /* don't add files more than once to the panel */
1781 if (content_pattern != NULL && list->len != 0
1782 && strcmp (list->list[list->len - 1].fname, p) == 0)
1784 g_free (name);
1785 continue;
1788 if (list->len == 0) /* first turn i.e clean old list */
1789 panel_clean_dir (current_panel);
1790 list->list[list->len].fnamelen = strlen (p);
1791 list->list[list->len].fname = g_strndup (p, list->list[list->len].fnamelen);
1792 list->list[list->len].f.marked = 0;
1793 list->list[list->len].f.link_to_dir = link_to_dir;
1794 list->list[list->len].f.stale_link = stale_link;
1795 list->list[list->len].f.dir_size_computed = 0;
1796 list->list[list->len].st = st;
1797 list->list[list->len].sort_key = NULL;
1798 list->list[list->len].second_sort_key = NULL;
1799 list->len++;
1800 g_free (name);
1801 if ((list->len & 15) == 0)
1802 rotate_dash (TRUE);
1805 if (list->len != 0)
1807 current_panel->is_panelized = TRUE;
1809 /* absolute path */
1810 if (start_dir_len < 0)
1812 int ret;
1813 vfs_path_free (current_panel->cwd_vpath);
1814 current_panel->cwd_vpath = vfs_path_from_str (PATH_SEP_STR);
1815 ret = chdir (PATH_SEP_STR);
1816 (void) ret;
1818 panelize_save_panel (current_panel);
1822 g_free (content_pattern);
1823 kill_gui ();
1824 do_search (NULL); /* force do_search to release resources */
1825 MC_PTR_FREE (old_dir);
1826 rotate_dash (FALSE);
1828 return return_value;
1831 /* --------------------------------------------------------------------------------------------- */
1832 /*** public functions ****************************************************************************/
1833 /* --------------------------------------------------------------------------------------------- */
1835 void
1836 find_file (void)
1838 char *start_dir = NULL, *pattern = NULL, *content = NULL, *ignore_dirs = NULL;
1839 ssize_t start_dir_len;
1841 while (find_parameters (&start_dir, &start_dir_len, &ignore_dirs, &pattern, &content))
1843 char *filename = NULL, *dirname = NULL;
1844 int v = B_CANCEL;
1846 if (pattern[0] != '\0')
1848 last_refresh.tv_sec = 0;
1849 last_refresh.tv_usec = 0;
1851 is_start = FALSE;
1852 v = do_find (start_dir, start_dir_len, ignore_dirs, pattern, content, &dirname,
1853 &filename);
1856 g_free (start_dir);
1857 g_free (ignore_dirs);
1858 g_free (pattern);
1860 if (v == B_ENTER)
1862 if (dirname != NULL)
1864 vfs_path_t *dirname_vpath;
1866 dirname_vpath = vfs_path_from_str (dirname);
1867 do_cd (dirname_vpath, cd_exact);
1868 vfs_path_free (dirname_vpath);
1869 if (filename != NULL)
1870 try_to_select (current_panel,
1871 filename + (content != NULL
1872 ? strchr (filename + 4, ':') - filename + 1 : 4));
1874 else if (filename != NULL)
1876 vfs_path_t *filename_vpath;
1878 filename_vpath = vfs_path_from_str (filename);
1879 do_cd (filename_vpath, cd_exact);
1880 vfs_path_free (filename_vpath);
1884 g_free (content);
1885 g_free (dirname);
1886 g_free (filename);
1888 if (v == B_ENTER || v == B_CANCEL)
1889 break;
1891 if (v == B_PANELIZE)
1893 panel_re_sort (current_panel);
1894 try_to_select (current_panel, NULL);
1895 break;
1900 /* --------------------------------------------------------------------------------------------- */