Move WOP_WANT_IDLE option to widget_state_t flags
[midnight-commander.git] / src / filemanager / find.c
blobde245453a883bc32e81b9586ceaeca5ce014811a
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_global.main_config, "FindFile", "file_case_sens", TRUE);
288 options.file_pattern =
289 mc_config_get_bool (mc_global.main_config, "FindFile", "file_shell_pattern", TRUE);
290 options.find_recurs =
291 mc_config_get_bool (mc_global.main_config, "FindFile", "file_find_recurs", TRUE);
292 options.skip_hidden =
293 mc_config_get_bool (mc_global.main_config, "FindFile", "file_skip_hidden", FALSE);
294 options.file_all_charsets =
295 mc_config_get_bool (mc_global.main_config, "FindFile", "file_all_charsets", FALSE);
296 options.content_case_sens =
297 mc_config_get_bool (mc_global.main_config, "FindFile", "content_case_sens", TRUE);
298 options.content_regexp =
299 mc_config_get_bool (mc_global.main_config, "FindFile", "content_regexp", FALSE);
300 options.content_first_hit =
301 mc_config_get_bool (mc_global.main_config, "FindFile", "content_first_hit", FALSE);
302 options.content_whole_words =
303 mc_config_get_bool (mc_global.main_config, "FindFile", "content_whole_words", FALSE);
304 options.content_all_charsets =
305 mc_config_get_bool (mc_global.main_config, "FindFile", "content_all_charsets", FALSE);
306 options.ignore_dirs_enable =
307 mc_config_get_bool (mc_global.main_config, "FindFile", "ignore_dirs_enable", TRUE);
308 options.ignore_dirs =
309 mc_config_get_string (mc_global.main_config, "FindFile", "ignore_dirs", "");
311 if (options.ignore_dirs[0] == '\0')
312 MC_PTR_FREE (options.ignore_dirs);
315 /* --------------------------------------------------------------------------------------------- */
317 static void
318 find_save_options (void)
320 mc_config_set_bool (mc_global.main_config, "FindFile", "file_case_sens",
321 options.file_case_sens);
322 mc_config_set_bool (mc_global.main_config, "FindFile", "file_shell_pattern",
323 options.file_pattern);
324 mc_config_set_bool (mc_global.main_config, "FindFile", "file_find_recurs", options.find_recurs);
325 mc_config_set_bool (mc_global.main_config, "FindFile", "file_skip_hidden", options.skip_hidden);
326 mc_config_set_bool (mc_global.main_config, "FindFile", "file_all_charsets",
327 options.file_all_charsets);
328 mc_config_set_bool (mc_global.main_config, "FindFile", "content_case_sens",
329 options.content_case_sens);
330 mc_config_set_bool (mc_global.main_config, "FindFile", "content_regexp",
331 options.content_regexp);
332 mc_config_set_bool (mc_global.main_config, "FindFile", "content_first_hit",
333 options.content_first_hit);
334 mc_config_set_bool (mc_global.main_config, "FindFile", "content_whole_words",
335 options.content_whole_words);
336 mc_config_set_bool (mc_global.main_config, "FindFile", "content_all_charsets",
337 options.content_all_charsets);
338 mc_config_set_bool (mc_global.main_config, "FindFile", "ignore_dirs_enable",
339 options.ignore_dirs_enable);
340 mc_config_set_string (mc_global.main_config, "FindFile", "ignore_dirs", options.ignore_dirs);
343 /* --------------------------------------------------------------------------------------------- */
345 static inline char *
346 add_to_list (const char *text, void *data)
348 return listbox_add_item (find_list, LISTBOX_APPEND_AT_END, 0, text, data, TRUE);
351 /* --------------------------------------------------------------------------------------------- */
353 static inline void
354 stop_idle (void *data)
356 widget_idle (WIDGET (data), FALSE);
359 /* --------------------------------------------------------------------------------------------- */
361 static inline void
362 status_update (const char *text)
364 label_set_text (status_label, text);
367 /* --------------------------------------------------------------------------------------------- */
369 static void
370 found_num_update (void)
372 char buffer[BUF_TINY];
373 g_snprintf (buffer, sizeof (buffer), _("Found: %ld"), matches);
374 label_set_text (found_num_label, buffer);
377 /* --------------------------------------------------------------------------------------------- */
379 static void
380 get_list_info (char **file, char **dir, gsize * start, gsize * end)
382 find_match_location_t *location;
384 listbox_get_current (find_list, file, (void **) &location);
385 if (location != NULL)
387 if (dir != NULL)
388 *dir = location->dir;
389 if (start != NULL)
390 *start = location->start;
391 if (end != NULL)
392 *end = location->end;
394 else
396 if (dir != NULL)
397 *dir = NULL;
401 /* --------------------------------------------------------------------------------------------- */
402 /** check regular expression */
404 static gboolean
405 find_check_regexp (const char *r)
407 mc_search_t *search;
408 gboolean regexp_ok = FALSE;
410 search = mc_search_new (r, NULL);
412 if (search != NULL)
414 search->search_type = MC_SEARCH_T_REGEX;
415 regexp_ok = mc_search_prepare (search);
416 mc_search_free (search);
419 return regexp_ok;
422 /* --------------------------------------------------------------------------------------------- */
424 static void
425 find_toggle_enable_params (void)
427 gboolean disable = in_name->buffer[0] == '\0';
429 widget_disable (WIDGET (file_pattern_cbox), disable);
430 widget_disable (WIDGET (file_case_sens_cbox), disable);
431 #ifdef HAVE_CHARSET
432 widget_disable (WIDGET (file_all_charsets_cbox), disable);
433 #endif
436 /* --------------------------------------------------------------------------------------------- */
438 static void
439 find_toggle_enable_content (void)
441 gboolean disable = in_with->buffer[0] == '\0';
443 widget_disable (WIDGET (content_regexp_cbox), disable);
444 widget_disable (WIDGET (content_case_sens_cbox), disable);
445 #ifdef HAVE_CHARSET
446 widget_disable (WIDGET (content_all_charsets_cbox), disable);
447 #endif
448 widget_disable (WIDGET (content_whole_words_cbox), disable);
449 widget_disable (WIDGET (content_first_hit_cbox), disable);
452 /* --------------------------------------------------------------------------------------------- */
454 * Callback for the parameter dialog.
455 * Validate regex, prevent closing the dialog if it's invalid.
458 static cb_ret_t
459 find_parm_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
461 /* FIXME: HACK: use first draw of dialog to resolve widget state dependencies.
462 * Use this time moment to check input field content. We can't do that in MSG_INIT
463 * because history is not loaded yet.
464 * Probably, we want new MSG_ACTIVATE message as complement to MSG_VALIDATE one. Or
465 * we could name it MSG_POST_INIT.
467 * In one or two other places we use MSG_IDLE instead of MSG_DRAW for a similar
468 * purpose. We should remember to fix those places too when we introduce the new
469 * message.
471 static gboolean first_draw = TRUE;
473 WDialog *h = DIALOG (w);
475 switch (msg)
477 case MSG_INIT:
478 first_draw = TRUE;
479 return MSG_HANDLED;
481 case MSG_NOTIFY:
482 if (sender == WIDGET (ignore_dirs_cbox))
484 gboolean disable = !(ignore_dirs_cbox->state & C_BOOL);
486 widget_disable (WIDGET (in_ignore), disable);
488 return MSG_HANDLED;
491 return MSG_NOT_HANDLED;
493 case MSG_VALIDATE:
494 if (h->ret_value != B_ENTER)
495 return MSG_HANDLED;
497 /* check filename regexp */
498 if (!(file_pattern_cbox->state & C_BOOL)
499 && (in_name->buffer[0] != '\0') && !find_check_regexp (in_name->buffer))
501 h->state = DLG_ACTIVE; /* Don't stop the dialog */
502 message (D_ERROR, MSG_ERROR, _("Malformed regular expression"));
503 dlg_select_widget (in_name);
504 return MSG_HANDLED;
507 /* check content regexp */
508 if ((content_regexp_cbox->state & C_BOOL) && (in_with->buffer[0] != '\0')
509 && !find_check_regexp (in_with->buffer))
511 h->state = DLG_ACTIVE; /* Don't stop the dialog */
512 message (D_ERROR, MSG_ERROR, _("Malformed regular expression"));
513 dlg_select_widget (in_with);
514 return MSG_HANDLED;
517 return MSG_HANDLED;
519 case MSG_POST_KEY:
520 if (h->current->data == in_name)
521 find_toggle_enable_params ();
522 else if (h->current->data == in_with)
523 find_toggle_enable_content ();
524 return MSG_HANDLED;
526 case MSG_DRAW:
527 if (first_draw)
529 find_toggle_enable_params ();
530 find_toggle_enable_content ();
533 first_draw = FALSE;
534 /* fall through to call MSG_DRAW default handler */
536 default:
537 return dlg_default_callback (w, sender, msg, parm, data);
541 /* --------------------------------------------------------------------------------------------- */
543 * find_parameters: gets information from the user
545 * If the return value is TRUE, then the following holds:
547 * start_dir, ignore_dirs, pattern and content contain the information provided by the user.
548 * They are newly allocated strings and must be freed when uneeded.
550 * start_dir_len is -1 when user entered an absolute path, otherwise it is a length
551 * of start_dir (which is absolute). It is used to get a relative pats of find results.
554 static gboolean
555 find_parameters (char **start_dir, ssize_t * start_dir_len,
556 char **ignore_dirs, char **pattern, char **content)
558 /* Size of the find parameters window */
559 #ifdef HAVE_CHARSET
560 const int lines = 18;
561 #else
562 const int lines = 17;
563 #endif
564 int cols = 68;
566 gboolean return_value;
568 /* file name */
569 const char *file_name_label = N_("File name:");
570 const char *file_recurs_label = N_("&Find recursively");
571 const char *file_pattern_label = N_("&Using shell patterns");
572 #ifdef HAVE_CHARSET
573 const char *file_all_charsets_label = N_("&All charsets");
574 #endif
575 const char *file_case_label = N_("Cas&e sensitive");
576 const char *file_skip_hidden_label = N_("S&kip hidden");
578 /* file content */
579 const char *content_content_label = N_("Content:");
580 const char *content_use_label = N_("Sea&rch for content");
581 const char *content_regexp_label = N_("Re&gular expression");
582 const char *content_case_label = N_("Case sens&itive");
583 #ifdef HAVE_CHARSET
584 const char *content_all_charsets_label = N_("A&ll charsets");
585 #endif
586 const char *content_whole_words_label = N_("&Whole words");
587 const char *content_first_hit_label = N_("Fir&st hit");
589 const char *buts[] = { N_("&Tree"), N_("&OK"), N_("&Cancel") };
591 /* button lengths */
592 int b0, b1, b2, b12;
593 int y1, y2, x1, x2;
594 /* column width */
595 int cw;
597 #ifdef ENABLE_NLS
599 size_t i;
601 file_name_label = _(file_name_label);
602 file_recurs_label = _(file_recurs_label);
603 file_pattern_label = _(file_pattern_label);
604 #ifdef HAVE_CHARSET
605 file_all_charsets_label = _(file_all_charsets_label);
606 #endif
607 file_case_label = _(file_case_label);
608 file_skip_hidden_label = _(file_skip_hidden_label);
610 /* file content */
611 content_content_label = _(content_content_label);
612 content_use_label = _(content_use_label);
613 content_regexp_label = _(content_regexp_label);
614 content_case_label = _(content_case_label);
615 #ifdef HAVE_CHARSET
616 content_all_charsets_label = _(content_all_charsets_label);
617 #endif
618 content_whole_words_label = _(content_whole_words_label);
619 content_first_hit_label = _(content_first_hit_label);
621 for (i = 0; i < G_N_ELEMENTS (buts); i++)
622 buts[i] = _(buts[i]);
624 #endif /* ENABLE_NLS */
626 /* caclulate dialog width */
628 /* widget widths */
629 cw = str_term_width1 (file_name_label);
630 cw = max (cw, str_term_width1 (file_recurs_label) + 4);
631 cw = max (cw, str_term_width1 (file_pattern_label) + 4);
632 #ifdef HAVE_CHARSET
633 cw = max (cw, str_term_width1 (file_all_charsets_label) + 4);
634 #endif
635 cw = max (cw, str_term_width1 (file_case_label) + 4);
636 cw = max (cw, str_term_width1 (file_skip_hidden_label) + 4);
638 cw = max (cw, str_term_width1 (content_content_label) + 4);
639 cw = max (cw, str_term_width1 (content_use_label) + 4);
640 cw = max (cw, str_term_width1 (content_regexp_label) + 4);
641 cw = max (cw, str_term_width1 (content_case_label) + 4);
642 #ifdef HAVE_CHARSET
643 cw = max (cw, str_term_width1 (content_all_charsets_label) + 4);
644 #endif
645 cw = max (cw, str_term_width1 (content_whole_words_label) + 4);
646 cw = max (cw, str_term_width1 (content_first_hit_label) + 4);
648 /* button width */
649 b0 = str_term_width1 (buts[0]) + 3;
650 b1 = str_term_width1 (buts[1]) + 5; /* default button */
651 b2 = str_term_width1 (buts[2]) + 3;
652 b12 = b1 + b2 + 1;
654 cols = max (cols, max (b12, cw * 2 + 1) + 6);
656 find_load_options ();
658 if (in_start_dir == NULL)
659 in_start_dir = g_strdup (".");
661 find_dlg =
662 dlg_create (TRUE, 0, 0, lines, cols, dialog_colors, find_parm_callback, NULL, "[Find File]",
663 _("Find File"), DLG_CENTER);
665 x1 = 3;
666 x2 = cols / 2 + 1;
667 cw = (cols - 7) / 2;
668 y1 = 2;
670 add_widget (find_dlg, label_new (y1++, x1, _("Start at:")));
671 in_start =
672 input_new (y1, x1, input_colors, cols - b0 - 7, in_start_dir, "start",
673 INPUT_COMPLETE_CD | INPUT_COMPLETE_FILENAMES);
674 add_widget (find_dlg, in_start);
676 add_widget (find_dlg, button_new (y1++, cols - b0 - 3, B_TREE, NORMAL_BUTTON, buts[0], NULL));
678 ignore_dirs_cbox =
679 check_new (y1++, x1, options.ignore_dirs_enable, _("Ena&ble ignore directories:"));
680 add_widget (find_dlg, ignore_dirs_cbox);
682 in_ignore =
683 input_new (y1++, x1, input_colors, cols - 6,
684 options.ignore_dirs != NULL ? options.ignore_dirs : "", "ignoredirs",
685 INPUT_COMPLETE_CD | INPUT_COMPLETE_FILENAMES);
686 add_widget (find_dlg, in_ignore);
688 add_widget (find_dlg, hline_new (y1++, -1, -1));
690 y2 = y1;
692 /* Start 1st column */
693 add_widget (find_dlg, label_new (y1++, x1, file_name_label));
694 in_name =
695 input_new (y1++, x1, input_colors, cw, INPUT_LAST_TEXT, "name",
696 INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_CD);
697 add_widget (find_dlg, in_name);
699 /* Start 2nd column */
700 content_label = label_new (y2++, x2, content_content_label);
701 add_widget (find_dlg, content_label);
702 in_with =
703 input_new (y2++, x2, input_colors, cw, INPUT_LAST_TEXT,
704 MC_HISTORY_SHARED_SEARCH, INPUT_COMPLETE_NONE);
705 in_with->label = content_label;
706 add_widget (find_dlg, in_with);
708 /* Continue 1st column */
709 recursively_cbox = check_new (y1++, x1, options.find_recurs, file_recurs_label);
710 add_widget (find_dlg, recursively_cbox);
712 file_pattern_cbox = check_new (y1++, x1, options.file_pattern, file_pattern_label);
713 add_widget (find_dlg, file_pattern_cbox);
715 file_case_sens_cbox = check_new (y1++, x1, options.file_case_sens, file_case_label);
716 add_widget (find_dlg, file_case_sens_cbox);
718 #ifdef HAVE_CHARSET
719 file_all_charsets_cbox =
720 check_new (y1++, x1, options.file_all_charsets, file_all_charsets_label);
721 add_widget (find_dlg, file_all_charsets_cbox);
722 #endif
724 skip_hidden_cbox = check_new (y1++, x1, options.skip_hidden, file_skip_hidden_label);
725 add_widget (find_dlg, skip_hidden_cbox);
727 /* Continue 2nd column */
728 content_whole_words_cbox =
729 check_new (y2++, x2, options.content_whole_words, content_whole_words_label);
730 add_widget (find_dlg, content_whole_words_cbox);
732 content_regexp_cbox = check_new (y2++, x2, options.content_regexp, content_regexp_label);
733 add_widget (find_dlg, content_regexp_cbox);
735 content_case_sens_cbox = check_new (y2++, x2, options.content_case_sens, content_case_label);
736 add_widget (find_dlg, content_case_sens_cbox);
738 #ifdef HAVE_CHARSET
739 content_all_charsets_cbox =
740 check_new (y2++, x2, options.content_all_charsets, content_all_charsets_label);
741 add_widget (find_dlg, content_all_charsets_cbox);
742 #endif
744 content_first_hit_cbox =
745 check_new (y2++, x2, options.content_first_hit, content_first_hit_label);
746 add_widget (find_dlg, content_first_hit_cbox);
748 /* buttons */
749 y1 = max (y1, y2);
750 x1 = (cols - b12) / 2;
751 add_widget (find_dlg, hline_new (y1++, -1, -1));
752 add_widget (find_dlg, button_new (y1, x1, B_ENTER, DEFPUSH_BUTTON, buts[1], NULL));
753 add_widget (find_dlg, button_new (y1, x1 + b1 + 1, B_CANCEL, NORMAL_BUTTON, buts[2], NULL));
755 find_par_start:
756 dlg_select_widget (in_name);
758 switch (dlg_run (find_dlg))
760 case B_CANCEL:
761 return_value = FALSE;
762 break;
764 case B_TREE:
766 char *temp_dir;
768 temp_dir = in_start->buffer;
769 if (*temp_dir == '\0' || DIR_IS_DOT (temp_dir))
770 temp_dir = g_strdup (vfs_path_as_str (current_panel->cwd_vpath));
771 else
772 temp_dir = g_strdup (temp_dir);
774 if (in_start_dir != INPUT_LAST_TEXT)
775 g_free (in_start_dir);
776 in_start_dir = tree_box (temp_dir);
777 if (in_start_dir == NULL)
778 in_start_dir = temp_dir;
779 else
780 g_free (temp_dir);
782 input_assign_text (in_start, in_start_dir);
784 /* Warning: Dreadful goto */
785 goto find_par_start;
788 default:
790 char *s;
792 #ifdef HAVE_CHARSET
793 options.file_all_charsets = file_all_charsets_cbox->state & C_BOOL;
794 options.content_all_charsets = content_all_charsets_cbox->state & C_BOOL;
795 #endif
796 options.content_case_sens = content_case_sens_cbox->state & C_BOOL;
797 options.content_regexp = content_regexp_cbox->state & C_BOOL;
798 options.content_first_hit = content_first_hit_cbox->state & C_BOOL;
799 options.content_whole_words = content_whole_words_cbox->state & C_BOOL;
800 options.find_recurs = recursively_cbox->state & C_BOOL;
801 options.file_pattern = file_pattern_cbox->state & C_BOOL;
802 options.file_case_sens = file_case_sens_cbox->state & C_BOOL;
803 options.skip_hidden = skip_hidden_cbox->state & C_BOOL;
804 options.ignore_dirs_enable = ignore_dirs_cbox->state & C_BOOL;
805 g_free (options.ignore_dirs);
806 options.ignore_dirs = g_strdup (in_ignore->buffer);
808 *content = in_with->buffer[0] != '\0' ? g_strdup (in_with->buffer) : NULL;
809 if (in_name->buffer[0] != '\0')
810 *pattern = g_strdup (in_name->buffer);
811 else
812 *pattern = g_strdup (options.file_pattern ? "*" : ".*");
813 *start_dir = !input_is_empty (in_start) ? in_start->buffer : (char *) ".";
814 if (in_start_dir != INPUT_LAST_TEXT)
815 g_free (in_start_dir);
816 in_start_dir = g_strdup (*start_dir);
818 s = tilde_expand (*start_dir);
819 canonicalize_pathname (s);
821 if (DIR_IS_DOT (s))
823 *start_dir = g_strdup (vfs_path_as_str (current_panel->cwd_vpath));
824 /* FIXME: is current_panel->cwd_vpath canonicalized? */
825 /* relative paths will be used in panelization */
826 *start_dir_len = (ssize_t) strlen (*start_dir);
827 g_free (s);
829 else if (g_path_is_absolute (s))
831 *start_dir = s;
832 *start_dir_len = -1;
834 else
836 /* relative paths will be used in panelization */
837 *start_dir =
838 mc_build_filename (vfs_path_as_str (current_panel->cwd_vpath), s,
839 (char *) NULL);
840 *start_dir_len = (ssize_t) strlen (vfs_path_as_str (current_panel->cwd_vpath));
841 g_free (s);
844 if (!options.ignore_dirs_enable || in_ignore->buffer[0] == '\0'
845 || DIR_IS_DOT (in_ignore->buffer))
846 *ignore_dirs = NULL;
847 else
848 *ignore_dirs = g_strdup (in_ignore->buffer);
850 find_save_options ();
852 return_value = TRUE;
856 dlg_destroy (find_dlg);
858 return return_value;
861 /* --------------------------------------------------------------------------------------------- */
863 static inline void
864 push_directory (vfs_path_t * dir)
866 g_queue_push_head (&dir_queue, (void *) dir);
869 /* --------------------------------------------------------------------------------------------- */
871 static inline vfs_path_t *
872 pop_directory (void)
874 return (vfs_path_t *) g_queue_pop_tail (&dir_queue);
877 /* --------------------------------------------------------------------------------------------- */
878 /** Remove all the items from the stack */
880 static void
881 clear_stack (void)
883 g_queue_foreach (&dir_queue, (GFunc) vfs_path_free, NULL);
884 g_queue_clear (&dir_queue);
887 /* --------------------------------------------------------------------------------------------- */
889 static void
890 insert_file (const char *dir, const char *file, gsize start, gsize end)
892 char *tmp_name = NULL;
893 static char *dirname = NULL;
894 find_match_location_t *location;
896 while (IS_PATH_SEP (dir[0]) && IS_PATH_SEP (dir[1]))
897 dir++;
899 if (old_dir)
901 if (strcmp (old_dir, dir))
903 g_free (old_dir);
904 old_dir = g_strdup (dir);
905 dirname = add_to_list (dir, NULL);
908 else
910 old_dir = g_strdup (dir);
911 dirname = add_to_list (dir, NULL);
914 tmp_name = g_strdup_printf (" %s", file);
915 location = g_malloc (sizeof (*location));
916 location->dir = dirname;
917 location->start = start;
918 location->end = end;
919 add_to_list (tmp_name, location);
920 g_free (tmp_name);
923 /* --------------------------------------------------------------------------------------------- */
925 static void
926 find_add_match (const char *dir, const char *file, gsize start, gsize end)
928 insert_file (dir, file, start, end);
930 /* Don't scroll */
931 if (matches == 0)
932 listbox_select_first (find_list);
933 widget_redraw (WIDGET (find_list));
935 matches++;
936 found_num_update ();
939 /* --------------------------------------------------------------------------------------------- */
941 static FindProgressStatus
942 check_find_events (WDialog * h)
944 Gpm_Event event;
945 int c;
947 event.x = -1;
948 c = tty_get_event (&event, h->mouse_status == MOU_REPEAT, FALSE);
949 if (c != EV_NONE)
951 dlg_process_event (h, c, &event);
952 if (h->ret_value == B_ENTER
953 || h->ret_value == B_CANCEL || h->ret_value == B_AGAIN || h->ret_value == B_PANELIZE)
955 /* dialog terminated */
956 return FIND_ABORT;
958 if (!widget_get_state (WIDGET (h), WST_IDLE))
960 /* searching suspended */
961 return FIND_SUSPEND;
965 return FIND_CONT;
968 /* --------------------------------------------------------------------------------------------- */
970 * search_content:
972 * Search the content_pattern string in the DIRECTORY/FILE.
973 * It will add the found entries to the find listbox.
975 * returns FALSE if do_search should look for another file
976 * TRUE if do_search should exit and proceed to the event handler
979 static gboolean
980 search_content (WDialog * h, const char *directory, const char *filename)
982 struct stat s;
983 char buffer[BUF_4K]; /* raw input buffer */
984 int file_fd;
985 gboolean ret_val = FALSE;
986 vfs_path_t *vpath;
987 struct timeval tv;
988 time_t seconds;
989 suseconds_t useconds;
990 gboolean status_updated = FALSE;
992 vpath = vfs_path_build_filename (directory, filename, (char *) NULL);
994 if (mc_stat (vpath, &s) != 0 || !S_ISREG (s.st_mode))
996 vfs_path_free (vpath);
997 return FALSE;
1000 file_fd = mc_open (vpath, O_RDONLY);
1001 vfs_path_free (vpath);
1003 if (file_fd == -1)
1004 return FALSE;
1006 /* get time elapsed from last refresh */
1007 if (gettimeofday (&tv, NULL) == -1)
1009 tv.tv_sec = 0;
1010 tv.tv_usec = 0;
1011 last_refresh = tv;
1013 seconds = tv.tv_sec - last_refresh.tv_sec;
1014 useconds = tv.tv_usec - last_refresh.tv_usec;
1015 if (useconds < 0)
1017 seconds--;
1018 useconds += G_USEC_PER_SEC;
1021 if (s.st_size >= MIN_REFRESH_FILE_SIZE || seconds > 0 || useconds > MAX_REFRESH_INTERVAL)
1023 g_snprintf (buffer, sizeof (buffer), _("Grepping in %s"), filename);
1024 status_update (str_trunc (buffer, WIDGET (h)->cols - 8));
1025 mc_refresh ();
1026 last_refresh = tv;
1027 status_updated = TRUE;
1030 tty_enable_interrupt_key ();
1031 tty_got_interrupt ();
1034 int line = 1;
1035 int pos = 0;
1036 int n_read = 0;
1037 off_t off = 0; /* file_fd's offset corresponding to strbuf[0] */
1038 gboolean found = FALSE;
1039 gsize found_len;
1040 gsize found_start;
1041 char result[BUF_MEDIUM];
1042 char *strbuf = NULL; /* buffer for fetched string */
1043 int strbuf_size = 0;
1044 int i = -1; /* compensate for a newline we'll add when we first enter the loop */
1046 if (resuming)
1048 /* We've been previously suspended, start from the previous position */
1049 resuming = FALSE;
1050 line = last_line;
1051 pos = last_pos;
1052 off = last_off;
1053 i = last_i;
1056 while (!ret_val)
1058 char ch = '\0';
1059 off += i + 1; /* the previous line, plus a newline character */
1060 i = 0;
1062 /* read to buffer and get line from there */
1063 while (TRUE)
1065 if (pos >= n_read)
1067 pos = 0;
1068 n_read = mc_read (file_fd, buffer, sizeof (buffer));
1069 if (n_read <= 0)
1070 break;
1073 ch = buffer[pos++];
1074 if (ch == '\0')
1076 /* skip possible leading zero(s) */
1077 if (i == 0)
1079 off++;
1080 continue;
1082 break;
1085 if (i >= strbuf_size - 1)
1087 strbuf_size += 128;
1088 strbuf = g_realloc (strbuf, strbuf_size);
1091 /* Strip newline */
1092 if (ch == '\n')
1093 break;
1095 strbuf[i++] = ch;
1098 if (i == 0)
1100 if (ch == '\0')
1101 break;
1103 /* if (ch == '\n'): do not search in empty strings */
1104 goto skip_search;
1107 strbuf[i] = '\0';
1109 if (!found /* Search in binary line once */
1110 && mc_search_run (search_content_handle, (const void *) strbuf, 0, i, &found_len))
1112 if (!status_updated)
1114 /* if we add results for a file, we have to ensure that
1115 name of this file is shown in status bar */
1116 g_snprintf (result, sizeof (result), _("Grepping in %s"), filename);
1117 status_update (str_trunc (result, WIDGET (h)->cols - 8));
1118 mc_refresh ();
1119 last_refresh = tv;
1120 status_updated = TRUE;
1123 g_snprintf (result, sizeof (result), "%d:%s", line, filename);
1124 found_start = off + search_content_handle->normal_offset + 1; /* off by one: ticket 3280 */
1125 find_add_match (directory, result, found_start, found_start + found_len);
1126 found = TRUE;
1129 if (found && options.content_first_hit)
1130 break;
1132 if (ch == '\n')
1134 skip_search:
1135 found = FALSE;
1136 line++;
1139 if ((line & 0xff) == 0)
1141 FindProgressStatus res;
1142 res = check_find_events (h);
1143 switch (res)
1145 case FIND_ABORT:
1146 stop_idle (h);
1147 ret_val = TRUE;
1148 break;
1149 case FIND_SUSPEND:
1150 resuming = TRUE;
1151 last_line = line;
1152 last_pos = pos;
1153 last_off = off;
1154 last_i = i;
1155 ret_val = TRUE;
1156 break;
1157 default:
1158 break;
1163 g_free (strbuf);
1166 tty_disable_interrupt_key ();
1167 mc_close (file_fd);
1168 return ret_val;
1171 /* --------------------------------------------------------------------------------------------- */
1174 If dir is absolute, this means we're within dir and searching file here.
1175 If dir is relative, this means we're going to add dir to the directory stack.
1177 static gboolean
1178 find_ignore_dir_search (const char *dir)
1180 if (find_ignore_dirs != NULL)
1182 const size_t dlen = strlen (dir);
1183 const unsigned char dabs = g_path_is_absolute (dir) ? 1 : 0;
1185 char **ignore_dir;
1187 for (ignore_dir = find_ignore_dirs; *ignore_dir != NULL; ignore_dir++)
1189 const size_t ilen = strlen (*ignore_dir);
1190 const unsigned char iabs = g_path_is_absolute (*ignore_dir) ? 2 : 0;
1192 /* ignore dir is too long -- skip it */
1193 if (dlen < ilen)
1194 continue;
1196 /* handle absolute and relative paths */
1197 switch (iabs | dabs)
1199 case 0: /* both paths are relative */
1200 case 3: /* both paths are abolute */
1201 /* if ignore dir is not a path of dir -- skip it */
1202 if (strncmp (dir, *ignore_dir, ilen) == 0)
1204 /* be sure that ignore dir is not a part of dir like:
1205 ignore dir is "h", dir is "home" */
1206 if (dir[ilen] == '\0' || IS_PATH_SEP (dir[ilen]))
1207 return TRUE;
1209 break;
1210 case 1: /* dir is absolute, ignore_dir is relative */
1212 char *d;
1214 d = strstr (dir, *ignore_dir);
1215 if (d != NULL && IS_PATH_SEP (d[-1])
1216 && (d[ilen] == '\0' || IS_PATH_SEP (d[ilen])))
1217 return TRUE;
1219 break;
1220 case 2: /* dir is relative, ignore_dir is absolute */
1221 /* FIXME: skip this case */
1222 break;
1223 default: /* this cannot occurs */
1224 return FALSE;
1229 return FALSE;
1232 /* --------------------------------------------------------------------------------------------- */
1234 static void
1235 find_rotate_dash (const WDialog * h, gboolean show)
1237 static size_t pos = 0;
1238 static const char rotating_dash[4] = "|/-\\";
1239 const Widget *w = CONST_WIDGET (h);
1241 if (!verbose)
1242 return;
1244 tty_setcolor (h->color[DLG_COLOR_NORMAL]);
1245 widget_move (h, w->lines - 7, w->cols - 4);
1246 tty_print_char (show ? rotating_dash[pos] : ' ');
1247 pos = (pos + 1) % sizeof (rotating_dash);
1248 mc_refresh ();
1251 /* --------------------------------------------------------------------------------------------- */
1253 static int
1254 do_search (WDialog * h)
1256 static struct dirent *dp = NULL;
1257 static DIR *dirp = NULL;
1258 static char *directory = NULL;
1259 struct stat tmp_stat;
1260 gsize bytes_found;
1261 unsigned short count;
1263 if (h == NULL)
1264 { /* someone forces me to close dirp */
1265 if (dirp != NULL)
1267 mc_closedir (dirp);
1268 dirp = NULL;
1270 MC_PTR_FREE (directory);
1271 dp = NULL;
1272 return 1;
1275 for (count = 0; count < 32; count++)
1277 while (dp == NULL)
1279 if (dirp != NULL)
1281 mc_closedir (dirp);
1282 dirp = NULL;
1285 while (dirp == NULL)
1287 vfs_path_t *tmp_vpath = NULL;
1289 tty_setcolor (REVERSE_COLOR);
1291 while (TRUE)
1293 tmp_vpath = pop_directory ();
1294 if (tmp_vpath == NULL)
1296 running = FALSE;
1297 if (ignore_count == 0)
1298 status_update (_("Finished"));
1299 else
1301 char msg[BUF_SMALL];
1302 g_snprintf (msg, sizeof (msg),
1303 ngettext ("Finished (ignored %zd directory)",
1304 "Finished (ignored %zd directories)",
1305 ignore_count), ignore_count);
1306 status_update (msg);
1308 find_rotate_dash (h, FALSE);
1309 stop_idle (h);
1310 return 0;
1313 /* handle absolute ignore dirs here */
1315 gboolean ok;
1317 ok = find_ignore_dir_search (vfs_path_as_str (tmp_vpath));
1318 if (!ok)
1319 break;
1322 vfs_path_free (tmp_vpath);
1323 ignore_count++;
1326 g_free (directory);
1327 directory = g_strdup (vfs_path_as_str (tmp_vpath));
1329 if (verbose)
1331 char buffer[BUF_MEDIUM];
1333 g_snprintf (buffer, sizeof (buffer), _("Searching %s"), directory);
1334 status_update (str_trunc (directory, WIDGET (h)->cols - 8));
1337 dirp = mc_opendir (tmp_vpath);
1338 vfs_path_free (tmp_vpath);
1339 } /* while (!dirp) */
1341 /* skip invalid filenames */
1342 while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1344 } /* while (!dp) */
1346 if (DIR_IS_DOT (dp->d_name) || DIR_IS_DOTDOT (dp->d_name))
1348 /* skip invalid filenames */
1349 while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1352 return 1;
1355 if (!(options.skip_hidden && (dp->d_name[0] == '.')))
1357 gboolean search_ok;
1359 if (options.find_recurs && (directory != NULL))
1360 { /* Can directory be NULL ? */
1361 /* handle relative ignore dirs here */
1362 if (options.ignore_dirs_enable && find_ignore_dir_search (dp->d_name))
1363 ignore_count++;
1364 else
1366 vfs_path_t *tmp_vpath;
1368 tmp_vpath = vfs_path_build_filename (directory, dp->d_name, (char *) NULL);
1370 if (mc_lstat (tmp_vpath, &tmp_stat) == 0 && S_ISDIR (tmp_stat.st_mode))
1371 push_directory (tmp_vpath);
1372 else
1373 vfs_path_free (tmp_vpath);
1377 search_ok = mc_search_run (search_file_handle, dp->d_name,
1378 0, strlen (dp->d_name), &bytes_found);
1380 if (search_ok)
1382 if (content_pattern == NULL)
1383 find_add_match (directory, dp->d_name, 0, 0);
1384 else if (search_content (h, directory, dp->d_name))
1385 return 1;
1389 /* skip invalid filenames */
1390 while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1392 } /* for */
1394 find_rotate_dash (h, TRUE);
1396 return 1;
1399 /* --------------------------------------------------------------------------------------------- */
1401 static void
1402 init_find_vars (void)
1404 MC_PTR_FREE (old_dir);
1405 matches = 0;
1406 ignore_count = 0;
1408 /* Remove all the items from the stack */
1409 clear_stack ();
1411 g_strfreev (find_ignore_dirs);
1412 find_ignore_dirs = NULL;
1415 /* --------------------------------------------------------------------------------------------- */
1417 static void
1418 find_do_view_edit (gboolean unparsed_view, gboolean edit, char *dir, char *file, off_t search_start,
1419 off_t search_end)
1421 char *fullname = NULL;
1422 const char *filename = NULL;
1423 int line;
1424 vfs_path_t *fullname_vpath;
1426 if (content_pattern != NULL)
1428 filename = strchr (file + 4, ':') + 1;
1429 line = atoi (file + 4);
1431 else
1433 filename = file + 4;
1434 line = 0;
1437 fullname_vpath = vfs_path_build_filename (dir, filename, (char *) NULL);
1438 if (edit)
1439 edit_file_at_line (fullname_vpath, use_internal_edit != 0, line);
1440 else
1441 view_file_at_line (fullname_vpath, unparsed_view, use_internal_view != 0, line,
1442 search_start, search_end);
1443 vfs_path_free (fullname_vpath);
1444 g_free (fullname);
1447 /* --------------------------------------------------------------------------------------------- */
1449 static cb_ret_t
1450 view_edit_currently_selected_file (gboolean unparsed_view, gboolean edit)
1452 char *text = NULL;
1453 find_match_location_t *location;
1455 listbox_get_current (find_list, &text, (void **) &location);
1457 if ((text == NULL) || (location == NULL) || (location->dir == NULL))
1458 return MSG_NOT_HANDLED;
1460 find_do_view_edit (unparsed_view, edit, location->dir, text, location->start, location->end);
1461 return MSG_HANDLED;
1464 /* --------------------------------------------------------------------------------------------- */
1466 static void
1467 find_calc_button_locations (const WDialog * h, gboolean all_buttons)
1469 const int cols = CONST_WIDGET (h)->cols;
1471 int l1, l2;
1473 l1 = fbuts[0].len + fbuts[1].len + fbuts[is_start ? 3 : 2].len + fbuts[4].len + 3;
1474 l2 = fbuts[5].len + fbuts[6].len + fbuts[7].len + 2;
1476 fbuts[0].x = (cols - l1) / 2;
1477 fbuts[1].x = fbuts[0].x + fbuts[0].len + 1;
1478 fbuts[2].x = fbuts[1].x + fbuts[1].len + 1;
1479 fbuts[3].x = fbuts[2].x;
1480 fbuts[4].x = fbuts[2].x + fbuts[is_start ? 3 : 2].len + 1;
1482 if (all_buttons)
1484 fbuts[5].x = (cols - l2) / 2;
1485 fbuts[6].x = fbuts[5].x + fbuts[5].len + 1;
1486 fbuts[7].x = fbuts[6].x + fbuts[6].len + 1;
1490 /* --------------------------------------------------------------------------------------------- */
1492 static void
1493 find_relocate_buttons (const WDialog * h, gboolean all_buttons)
1495 size_t i;
1497 find_calc_button_locations (h, all_buttons);
1499 for (i = 0; i < fbuts_num; i++)
1500 fbuts[i].button->x = CONST_WIDGET (h)->x + fbuts[i].x;
1503 /* --------------------------------------------------------------------------------------------- */
1505 static cb_ret_t
1506 find_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
1508 WDialog *h = DIALOG (w);
1510 switch (msg)
1512 case MSG_KEY:
1513 if (parm == KEY_F (3) || parm == KEY_F (13))
1515 gboolean unparsed_view = (parm == KEY_F (13));
1517 return view_edit_currently_selected_file (unparsed_view, FALSE);
1519 if (parm == KEY_F (4))
1520 return view_edit_currently_selected_file (FALSE, TRUE);
1521 return MSG_NOT_HANDLED;
1523 case MSG_RESIZE:
1524 dlg_set_size (h, LINES - 4, COLS - 16);
1525 find_relocate_buttons (h, TRUE);
1526 return MSG_HANDLED;
1528 case MSG_IDLE:
1529 do_search (h);
1530 return MSG_HANDLED;
1532 default:
1533 return dlg_default_callback (w, sender, msg, parm, data);
1537 /* --------------------------------------------------------------------------------------------- */
1538 /** Handles the Stop/Start button in the find window */
1540 static int
1541 start_stop (WButton * button, int action)
1543 Widget *w = WIDGET (button);
1545 (void) action;
1547 running = is_start;
1548 widget_idle (WIDGET (find_dlg), running);
1549 is_start = !is_start;
1551 status_update (is_start ? _("Stopped") : _("Searching"));
1552 button_set_text (button, fbuts[is_start ? 3 : 2].text);
1554 find_relocate_buttons (w->owner, FALSE);
1555 dlg_redraw (w->owner);
1557 return 0;
1560 /* --------------------------------------------------------------------------------------------- */
1561 /** Handle view command, when invoked as a button */
1563 static int
1564 find_do_view_file (WButton * button, int action)
1566 (void) button;
1567 (void) action;
1569 view_edit_currently_selected_file (FALSE, FALSE);
1570 return 0;
1573 /* --------------------------------------------------------------------------------------------- */
1574 /** Handle edit command, when invoked as a button */
1576 static int
1577 find_do_edit_file (WButton * button, int action)
1579 (void) button;
1580 (void) action;
1582 view_edit_currently_selected_file (FALSE, TRUE);
1583 return 0;
1586 /* --------------------------------------------------------------------------------------------- */
1588 static void
1589 setup_gui (void)
1591 size_t i;
1592 int lines, cols;
1593 int y;
1595 static gboolean i18n_flag = FALSE;
1597 if (!i18n_flag)
1599 for (i = 0; i < fbuts_num; i++)
1601 #ifdef ENABLE_NLS
1602 fbuts[i].text = _(fbuts[i].text);
1603 #endif /* ENABLE_NLS */
1604 fbuts[i].len = str_term_width1 (fbuts[i].text) + 3;
1605 if (fbuts[i].flags == DEFPUSH_BUTTON)
1606 fbuts[i].len += 2;
1609 i18n_flag = TRUE;
1612 lines = LINES - 4;
1613 cols = COLS - 16;
1615 find_dlg =
1616 dlg_create (TRUE, 0, 0, lines, cols, dialog_colors, find_callback, NULL, "[Find File]",
1617 _("Find File"), DLG_CENTER);
1619 find_calc_button_locations (find_dlg, TRUE);
1621 y = 2;
1622 find_list = listbox_new (y, 2, lines - 10, cols - 4, FALSE, NULL);
1623 add_widget_autopos (find_dlg, find_list, WPOS_KEEP_ALL, NULL);
1624 y += WIDGET (find_list)->lines;
1626 add_widget_autopos (find_dlg, hline_new (y++, -1, -1), WPOS_KEEP_BOTTOM, NULL);
1628 found_num_label = label_new (y++, 4, "");
1629 add_widget_autopos (find_dlg, found_num_label, WPOS_KEEP_BOTTOM, NULL);
1631 status_label = label_new (y++, 4, _("Searching"));
1632 add_widget_autopos (find_dlg, status_label, WPOS_KEEP_BOTTOM, NULL);
1634 add_widget_autopos (find_dlg, hline_new (y++, -1, -1), WPOS_KEEP_BOTTOM, NULL);
1636 for (i = 0; i < fbuts_num; i++)
1638 if (i == 3)
1639 fbuts[3].button = fbuts[2].button;
1640 else
1642 fbuts[i].button =
1643 WIDGET (button_new
1644 (y, fbuts[i].x, fbuts[i].ret_cmd, fbuts[i].flags, fbuts[i].text,
1645 fbuts[i].callback));
1646 add_widget_autopos (find_dlg, fbuts[i].button, WPOS_KEEP_BOTTOM, NULL);
1649 if (i == quit_button)
1650 y++;
1653 dlg_select_widget (find_list);
1656 /* --------------------------------------------------------------------------------------------- */
1658 static int
1659 run_process (void)
1661 int ret;
1663 search_content_handle = mc_search_new (content_pattern, NULL);
1664 if (search_content_handle)
1666 search_content_handle->search_type =
1667 options.content_regexp ? MC_SEARCH_T_REGEX : MC_SEARCH_T_NORMAL;
1668 search_content_handle->is_case_sensitive = options.content_case_sens;
1669 search_content_handle->whole_words = options.content_whole_words;
1670 #ifdef HAVE_CHARSET
1671 search_content_handle->is_all_charsets = options.content_all_charsets;
1672 #endif
1674 search_file_handle = mc_search_new (find_pattern, NULL);
1675 search_file_handle->search_type = options.file_pattern ? MC_SEARCH_T_GLOB : MC_SEARCH_T_REGEX;
1676 search_file_handle->is_case_sensitive = options.file_case_sens;
1677 #ifdef HAVE_CHARSET
1678 search_file_handle->is_all_charsets = options.file_all_charsets;
1679 #endif
1680 search_file_handle->is_entire_line = options.file_pattern;
1682 resuming = FALSE;
1684 widget_idle (WIDGET (find_dlg), TRUE);
1685 ret = dlg_run (find_dlg);
1687 mc_search_free (search_file_handle);
1688 search_file_handle = NULL;
1689 mc_search_free (search_content_handle);
1690 search_content_handle = NULL;
1692 return ret;
1695 /* --------------------------------------------------------------------------------------------- */
1697 static void
1698 kill_gui (void)
1700 widget_idle (WIDGET (find_dlg), FALSE);
1701 dlg_destroy (find_dlg);
1704 /* --------------------------------------------------------------------------------------------- */
1706 static int
1707 do_find (const char *start_dir, ssize_t start_dir_len, const char *ignore_dirs,
1708 const char *pattern, const char *content, char **dirname, char **filename)
1710 int return_value = 0;
1711 char *dir_tmp = NULL, *file_tmp = NULL;
1713 setup_gui ();
1715 /* FIXME: Need to cleanup this, this ought to be passed non-globaly */
1716 find_pattern = (char *) pattern;
1718 content_pattern = NULL;
1719 if (content != NULL && str_is_valid_string (content))
1720 content_pattern = g_strdup (content);
1722 init_find_vars ();
1723 parse_ignore_dirs (ignore_dirs);
1724 push_directory (vfs_path_from_str (start_dir));
1726 return_value = run_process ();
1728 /* Clear variables */
1729 init_find_vars ();
1731 get_list_info (&file_tmp, &dir_tmp, NULL, NULL);
1733 if (dir_tmp)
1734 *dirname = g_strdup (dir_tmp);
1735 if (file_tmp)
1736 *filename = g_strdup (file_tmp);
1738 if (return_value == B_PANELIZE && *filename)
1740 int link_to_dir, stale_link;
1741 int i;
1742 struct stat st;
1743 GList *entry;
1744 dir_list *list = &current_panel->dir;
1745 char *name = NULL;
1747 dir_list_init (list);
1749 for (i = 0, entry = listbox_get_first_link (find_list); entry != NULL;
1750 i++, entry = g_list_next (entry))
1752 const char *lc_filename = NULL;
1753 WLEntry *le = LENTRY (entry->data);
1754 find_match_location_t *location = le->data;
1755 char *p;
1757 if ((le->text == NULL) || (location == NULL) || (location->dir == NULL))
1758 continue;
1760 if (content_pattern != NULL)
1761 lc_filename = strchr (le->text + 4, ':') + 1;
1762 else
1763 lc_filename = le->text + 4;
1765 name = mc_build_filename (location->dir, lc_filename, (char *) NULL);
1766 /* skip initial start dir */
1767 if (start_dir_len < 0)
1768 p = name;
1769 else
1771 p = name + (size_t) start_dir_len;
1772 if (IS_PATH_SEP (*p))
1773 p++;
1776 if (!handle_path (p, &st, &link_to_dir, &stale_link))
1778 g_free (name);
1779 continue;
1781 /* Need to grow the *list? */
1782 if (list->len == list->size && !dir_list_grow (list, DIR_LIST_RESIZE_STEP))
1784 g_free (name);
1785 break;
1788 /* don't add files more than once to the panel */
1789 if (content_pattern != NULL && list->len != 0
1790 && strcmp (list->list[list->len - 1].fname, p) == 0)
1792 g_free (name);
1793 continue;
1796 if (list->len == 0) /* first turn i.e clean old list */
1797 panel_clean_dir (current_panel);
1798 list->list[list->len].fnamelen = strlen (p);
1799 list->list[list->len].fname = g_strndup (p, list->list[list->len].fnamelen);
1800 list->list[list->len].f.marked = 0;
1801 list->list[list->len].f.link_to_dir = link_to_dir;
1802 list->list[list->len].f.stale_link = stale_link;
1803 list->list[list->len].f.dir_size_computed = 0;
1804 list->list[list->len].st = st;
1805 list->list[list->len].sort_key = NULL;
1806 list->list[list->len].second_sort_key = NULL;
1807 list->len++;
1808 g_free (name);
1809 if ((list->len & 15) == 0)
1810 rotate_dash (TRUE);
1813 if (list->len != 0)
1815 current_panel->is_panelized = TRUE;
1817 /* absolute path */
1818 if (start_dir_len < 0)
1820 int ret;
1821 vfs_path_free (current_panel->cwd_vpath);
1822 current_panel->cwd_vpath = vfs_path_from_str (PATH_SEP_STR);
1823 ret = chdir (PATH_SEP_STR);
1824 (void) ret;
1826 panelize_save_panel (current_panel);
1830 g_free (content_pattern);
1831 kill_gui ();
1832 do_search (NULL); /* force do_search to release resources */
1833 MC_PTR_FREE (old_dir);
1834 rotate_dash (FALSE);
1836 return return_value;
1839 /* --------------------------------------------------------------------------------------------- */
1840 /*** public functions ****************************************************************************/
1841 /* --------------------------------------------------------------------------------------------- */
1843 void
1844 find_file (void)
1846 char *start_dir = NULL, *pattern = NULL, *content = NULL, *ignore_dirs = NULL;
1847 ssize_t start_dir_len;
1849 while (find_parameters (&start_dir, &start_dir_len, &ignore_dirs, &pattern, &content))
1851 char *filename = NULL, *dirname = NULL;
1852 int v = B_CANCEL;
1854 if (pattern[0] != '\0')
1856 last_refresh.tv_sec = 0;
1857 last_refresh.tv_usec = 0;
1859 is_start = FALSE;
1860 v = do_find (start_dir, start_dir_len, ignore_dirs, pattern, content, &dirname,
1861 &filename);
1864 g_free (start_dir);
1865 g_free (ignore_dirs);
1866 g_free (pattern);
1868 if (v == B_ENTER)
1870 if (dirname != NULL)
1872 vfs_path_t *dirname_vpath;
1874 dirname_vpath = vfs_path_from_str (dirname);
1875 do_cd (dirname_vpath, cd_exact);
1876 vfs_path_free (dirname_vpath);
1877 if (filename != NULL)
1878 try_to_select (current_panel,
1879 filename + (content != NULL
1880 ? strchr (filename + 4, ':') - filename + 1 : 4));
1882 else if (filename != NULL)
1884 vfs_path_t *filename_vpath;
1886 filename_vpath = vfs_path_from_str (filename);
1887 do_cd (filename_vpath, cd_exact);
1888 vfs_path_free (filename_vpath);
1892 g_free (content);
1893 g_free (dirname);
1894 g_free (filename);
1896 if (v == B_ENTER || v == B_CANCEL)
1897 break;
1899 if (v == B_PANELIZE)
1901 panel_re_sort (current_panel);
1902 try_to_select (current_panel, NULL);
1903 break;
1908 /* --------------------------------------------------------------------------------------------- */