Join widget_state_t and dlg_state_t.
[midnight-commander.git] / src / filemanager / find.c
blob819f177af0c643d0b6972bf33f0c3b470b3b3f1f
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 /* Don't stop the dialog */
502 widget_set_state (WIDGET (h), WST_ACTIVE, TRUE);
503 message (D_ERROR, MSG_ERROR, _("Malformed regular expression"));
504 dlg_select_widget (in_name);
505 return MSG_HANDLED;
508 /* check content regexp */
509 if ((content_regexp_cbox->state & C_BOOL) && (in_with->buffer[0] != '\0')
510 && !find_check_regexp (in_with->buffer))
512 /* Don't stop the dialog */
513 widget_set_state (WIDGET (h), WST_ACTIVE, TRUE);
514 message (D_ERROR, MSG_ERROR, _("Malformed regular expression"));
515 dlg_select_widget (in_with);
516 return MSG_HANDLED;
519 return MSG_HANDLED;
521 case MSG_POST_KEY:
522 if (h->current->data == in_name)
523 find_toggle_enable_params ();
524 else if (h->current->data == in_with)
525 find_toggle_enable_content ();
526 return MSG_HANDLED;
528 case MSG_DRAW:
529 if (first_draw)
531 find_toggle_enable_params ();
532 find_toggle_enable_content ();
535 first_draw = FALSE;
536 /* fall through to call MSG_DRAW default handler */
538 default:
539 return dlg_default_callback (w, sender, msg, parm, data);
543 /* --------------------------------------------------------------------------------------------- */
545 * find_parameters: gets information from the user
547 * If the return value is TRUE, then the following holds:
549 * start_dir, ignore_dirs, pattern and content contain the information provided by the user.
550 * They are newly allocated strings and must be freed when uneeded.
552 * start_dir_len is -1 when user entered an absolute path, otherwise it is a length
553 * of start_dir (which is absolute). It is used to get a relative pats of find results.
556 static gboolean
557 find_parameters (char **start_dir, ssize_t * start_dir_len,
558 char **ignore_dirs, char **pattern, char **content)
560 /* Size of the find parameters window */
561 #ifdef HAVE_CHARSET
562 const int lines = 18;
563 #else
564 const int lines = 17;
565 #endif
566 int cols = 68;
568 gboolean return_value;
570 /* file name */
571 const char *file_name_label = N_("File name:");
572 const char *file_recurs_label = N_("&Find recursively");
573 const char *file_pattern_label = N_("&Using shell patterns");
574 #ifdef HAVE_CHARSET
575 const char *file_all_charsets_label = N_("&All charsets");
576 #endif
577 const char *file_case_label = N_("Cas&e sensitive");
578 const char *file_skip_hidden_label = N_("S&kip hidden");
580 /* file content */
581 const char *content_content_label = N_("Content:");
582 const char *content_use_label = N_("Sea&rch for content");
583 const char *content_regexp_label = N_("Re&gular expression");
584 const char *content_case_label = N_("Case sens&itive");
585 #ifdef HAVE_CHARSET
586 const char *content_all_charsets_label = N_("A&ll charsets");
587 #endif
588 const char *content_whole_words_label = N_("&Whole words");
589 const char *content_first_hit_label = N_("Fir&st hit");
591 const char *buts[] = { N_("&Tree"), N_("&OK"), N_("&Cancel") };
593 /* button lengths */
594 int b0, b1, b2, b12;
595 int y1, y2, x1, x2;
596 /* column width */
597 int cw;
599 #ifdef ENABLE_NLS
601 size_t i;
603 file_name_label = _(file_name_label);
604 file_recurs_label = _(file_recurs_label);
605 file_pattern_label = _(file_pattern_label);
606 #ifdef HAVE_CHARSET
607 file_all_charsets_label = _(file_all_charsets_label);
608 #endif
609 file_case_label = _(file_case_label);
610 file_skip_hidden_label = _(file_skip_hidden_label);
612 /* file content */
613 content_content_label = _(content_content_label);
614 content_use_label = _(content_use_label);
615 content_regexp_label = _(content_regexp_label);
616 content_case_label = _(content_case_label);
617 #ifdef HAVE_CHARSET
618 content_all_charsets_label = _(content_all_charsets_label);
619 #endif
620 content_whole_words_label = _(content_whole_words_label);
621 content_first_hit_label = _(content_first_hit_label);
623 for (i = 0; i < G_N_ELEMENTS (buts); i++)
624 buts[i] = _(buts[i]);
626 #endif /* ENABLE_NLS */
628 /* caclulate dialog width */
630 /* widget widths */
631 cw = str_term_width1 (file_name_label);
632 cw = max (cw, str_term_width1 (file_recurs_label) + 4);
633 cw = max (cw, str_term_width1 (file_pattern_label) + 4);
634 #ifdef HAVE_CHARSET
635 cw = max (cw, str_term_width1 (file_all_charsets_label) + 4);
636 #endif
637 cw = max (cw, str_term_width1 (file_case_label) + 4);
638 cw = max (cw, str_term_width1 (file_skip_hidden_label) + 4);
640 cw = max (cw, str_term_width1 (content_content_label) + 4);
641 cw = max (cw, str_term_width1 (content_use_label) + 4);
642 cw = max (cw, str_term_width1 (content_regexp_label) + 4);
643 cw = max (cw, str_term_width1 (content_case_label) + 4);
644 #ifdef HAVE_CHARSET
645 cw = max (cw, str_term_width1 (content_all_charsets_label) + 4);
646 #endif
647 cw = max (cw, str_term_width1 (content_whole_words_label) + 4);
648 cw = max (cw, str_term_width1 (content_first_hit_label) + 4);
650 /* button width */
651 b0 = str_term_width1 (buts[0]) + 3;
652 b1 = str_term_width1 (buts[1]) + 5; /* default button */
653 b2 = str_term_width1 (buts[2]) + 3;
654 b12 = b1 + b2 + 1;
656 cols = max (cols, max (b12, cw * 2 + 1) + 6);
658 find_load_options ();
660 if (in_start_dir == NULL)
661 in_start_dir = g_strdup (".");
663 find_dlg =
664 dlg_create (TRUE, 0, 0, lines, cols, dialog_colors, find_parm_callback, NULL, "[Find File]",
665 _("Find File"), DLG_CENTER);
667 x1 = 3;
668 x2 = cols / 2 + 1;
669 cw = (cols - 7) / 2;
670 y1 = 2;
672 add_widget (find_dlg, label_new (y1++, x1, _("Start at:")));
673 in_start =
674 input_new (y1, x1, input_colors, cols - b0 - 7, in_start_dir, "start",
675 INPUT_COMPLETE_CD | INPUT_COMPLETE_FILENAMES);
676 add_widget (find_dlg, in_start);
678 add_widget (find_dlg, button_new (y1++, cols - b0 - 3, B_TREE, NORMAL_BUTTON, buts[0], NULL));
680 ignore_dirs_cbox =
681 check_new (y1++, x1, options.ignore_dirs_enable, _("Ena&ble ignore directories:"));
682 add_widget (find_dlg, ignore_dirs_cbox);
684 in_ignore =
685 input_new (y1++, x1, input_colors, cols - 6,
686 options.ignore_dirs != NULL ? options.ignore_dirs : "", "ignoredirs",
687 INPUT_COMPLETE_CD | INPUT_COMPLETE_FILENAMES);
688 add_widget (find_dlg, in_ignore);
690 add_widget (find_dlg, hline_new (y1++, -1, -1));
692 y2 = y1;
694 /* Start 1st column */
695 add_widget (find_dlg, label_new (y1++, x1, file_name_label));
696 in_name =
697 input_new (y1++, x1, input_colors, cw, INPUT_LAST_TEXT, "name",
698 INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_CD);
699 add_widget (find_dlg, in_name);
701 /* Start 2nd column */
702 content_label = label_new (y2++, x2, content_content_label);
703 add_widget (find_dlg, content_label);
704 in_with =
705 input_new (y2++, x2, input_colors, cw, INPUT_LAST_TEXT,
706 MC_HISTORY_SHARED_SEARCH, INPUT_COMPLETE_NONE);
707 in_with->label = content_label;
708 add_widget (find_dlg, in_with);
710 /* Continue 1st column */
711 recursively_cbox = check_new (y1++, x1, options.find_recurs, file_recurs_label);
712 add_widget (find_dlg, recursively_cbox);
714 file_pattern_cbox = check_new (y1++, x1, options.file_pattern, file_pattern_label);
715 add_widget (find_dlg, file_pattern_cbox);
717 file_case_sens_cbox = check_new (y1++, x1, options.file_case_sens, file_case_label);
718 add_widget (find_dlg, file_case_sens_cbox);
720 #ifdef HAVE_CHARSET
721 file_all_charsets_cbox =
722 check_new (y1++, x1, options.file_all_charsets, file_all_charsets_label);
723 add_widget (find_dlg, file_all_charsets_cbox);
724 #endif
726 skip_hidden_cbox = check_new (y1++, x1, options.skip_hidden, file_skip_hidden_label);
727 add_widget (find_dlg, skip_hidden_cbox);
729 /* Continue 2nd column */
730 content_whole_words_cbox =
731 check_new (y2++, x2, options.content_whole_words, content_whole_words_label);
732 add_widget (find_dlg, content_whole_words_cbox);
734 content_regexp_cbox = check_new (y2++, x2, options.content_regexp, content_regexp_label);
735 add_widget (find_dlg, content_regexp_cbox);
737 content_case_sens_cbox = check_new (y2++, x2, options.content_case_sens, content_case_label);
738 add_widget (find_dlg, content_case_sens_cbox);
740 #ifdef HAVE_CHARSET
741 content_all_charsets_cbox =
742 check_new (y2++, x2, options.content_all_charsets, content_all_charsets_label);
743 add_widget (find_dlg, content_all_charsets_cbox);
744 #endif
746 content_first_hit_cbox =
747 check_new (y2++, x2, options.content_first_hit, content_first_hit_label);
748 add_widget (find_dlg, content_first_hit_cbox);
750 /* buttons */
751 y1 = max (y1, y2);
752 x1 = (cols - b12) / 2;
753 add_widget (find_dlg, hline_new (y1++, -1, -1));
754 add_widget (find_dlg, button_new (y1, x1, B_ENTER, DEFPUSH_BUTTON, buts[1], NULL));
755 add_widget (find_dlg, button_new (y1, x1 + b1 + 1, B_CANCEL, NORMAL_BUTTON, buts[2], NULL));
757 find_par_start:
758 dlg_select_widget (in_name);
760 switch (dlg_run (find_dlg))
762 case B_CANCEL:
763 return_value = FALSE;
764 break;
766 case B_TREE:
768 char *temp_dir;
770 temp_dir = in_start->buffer;
771 if (*temp_dir == '\0' || DIR_IS_DOT (temp_dir))
772 temp_dir = g_strdup (vfs_path_as_str (current_panel->cwd_vpath));
773 else
774 temp_dir = g_strdup (temp_dir);
776 if (in_start_dir != INPUT_LAST_TEXT)
777 g_free (in_start_dir);
778 in_start_dir = tree_box (temp_dir);
779 if (in_start_dir == NULL)
780 in_start_dir = temp_dir;
781 else
782 g_free (temp_dir);
784 input_assign_text (in_start, in_start_dir);
786 /* Warning: Dreadful goto */
787 goto find_par_start;
790 default:
792 char *s;
794 #ifdef HAVE_CHARSET
795 options.file_all_charsets = file_all_charsets_cbox->state & C_BOOL;
796 options.content_all_charsets = content_all_charsets_cbox->state & C_BOOL;
797 #endif
798 options.content_case_sens = content_case_sens_cbox->state & C_BOOL;
799 options.content_regexp = content_regexp_cbox->state & C_BOOL;
800 options.content_first_hit = content_first_hit_cbox->state & C_BOOL;
801 options.content_whole_words = content_whole_words_cbox->state & C_BOOL;
802 options.find_recurs = recursively_cbox->state & C_BOOL;
803 options.file_pattern = file_pattern_cbox->state & C_BOOL;
804 options.file_case_sens = file_case_sens_cbox->state & C_BOOL;
805 options.skip_hidden = skip_hidden_cbox->state & C_BOOL;
806 options.ignore_dirs_enable = ignore_dirs_cbox->state & C_BOOL;
807 g_free (options.ignore_dirs);
808 options.ignore_dirs = g_strdup (in_ignore->buffer);
810 *content = in_with->buffer[0] != '\0' ? g_strdup (in_with->buffer) : NULL;
811 if (in_name->buffer[0] != '\0')
812 *pattern = g_strdup (in_name->buffer);
813 else
814 *pattern = g_strdup (options.file_pattern ? "*" : ".*");
815 *start_dir = !input_is_empty (in_start) ? in_start->buffer : (char *) ".";
816 if (in_start_dir != INPUT_LAST_TEXT)
817 g_free (in_start_dir);
818 in_start_dir = g_strdup (*start_dir);
820 s = tilde_expand (*start_dir);
821 canonicalize_pathname (s);
823 if (DIR_IS_DOT (s))
825 *start_dir = g_strdup (vfs_path_as_str (current_panel->cwd_vpath));
826 /* FIXME: is current_panel->cwd_vpath canonicalized? */
827 /* relative paths will be used in panelization */
828 *start_dir_len = (ssize_t) strlen (*start_dir);
829 g_free (s);
831 else if (g_path_is_absolute (s))
833 *start_dir = s;
834 *start_dir_len = -1;
836 else
838 /* relative paths will be used in panelization */
839 *start_dir =
840 mc_build_filename (vfs_path_as_str (current_panel->cwd_vpath), s,
841 (char *) NULL);
842 *start_dir_len = (ssize_t) strlen (vfs_path_as_str (current_panel->cwd_vpath));
843 g_free (s);
846 if (!options.ignore_dirs_enable || in_ignore->buffer[0] == '\0'
847 || DIR_IS_DOT (in_ignore->buffer))
848 *ignore_dirs = NULL;
849 else
850 *ignore_dirs = g_strdup (in_ignore->buffer);
852 find_save_options ();
854 return_value = TRUE;
858 dlg_destroy (find_dlg);
860 return return_value;
863 /* --------------------------------------------------------------------------------------------- */
865 static inline void
866 push_directory (vfs_path_t * dir)
868 g_queue_push_head (&dir_queue, (void *) dir);
871 /* --------------------------------------------------------------------------------------------- */
873 static inline vfs_path_t *
874 pop_directory (void)
876 return (vfs_path_t *) g_queue_pop_tail (&dir_queue);
879 /* --------------------------------------------------------------------------------------------- */
880 /** Remove all the items from the stack */
882 static void
883 clear_stack (void)
885 g_queue_foreach (&dir_queue, (GFunc) vfs_path_free, NULL);
886 g_queue_clear (&dir_queue);
889 /* --------------------------------------------------------------------------------------------- */
891 static void
892 insert_file (const char *dir, const char *file, gsize start, gsize end)
894 char *tmp_name = NULL;
895 static char *dirname = NULL;
896 find_match_location_t *location;
898 while (IS_PATH_SEP (dir[0]) && IS_PATH_SEP (dir[1]))
899 dir++;
901 if (old_dir)
903 if (strcmp (old_dir, dir))
905 g_free (old_dir);
906 old_dir = g_strdup (dir);
907 dirname = add_to_list (dir, NULL);
910 else
912 old_dir = g_strdup (dir);
913 dirname = add_to_list (dir, NULL);
916 tmp_name = g_strdup_printf (" %s", file);
917 location = g_malloc (sizeof (*location));
918 location->dir = dirname;
919 location->start = start;
920 location->end = end;
921 add_to_list (tmp_name, location);
922 g_free (tmp_name);
925 /* --------------------------------------------------------------------------------------------- */
927 static void
928 find_add_match (const char *dir, const char *file, gsize start, gsize end)
930 insert_file (dir, file, start, end);
932 /* Don't scroll */
933 if (matches == 0)
934 listbox_select_first (find_list);
935 widget_redraw (WIDGET (find_list));
937 matches++;
938 found_num_update ();
941 /* --------------------------------------------------------------------------------------------- */
943 static FindProgressStatus
944 check_find_events (WDialog * h)
946 Gpm_Event event;
947 int c;
949 event.x = -1;
950 c = tty_get_event (&event, h->mouse_status == MOU_REPEAT, FALSE);
951 if (c != EV_NONE)
953 dlg_process_event (h, c, &event);
954 if (h->ret_value == B_ENTER
955 || h->ret_value == B_CANCEL || h->ret_value == B_AGAIN || h->ret_value == B_PANELIZE)
957 /* dialog terminated */
958 return FIND_ABORT;
960 if (!widget_get_state (WIDGET (h), WST_IDLE))
962 /* searching suspended */
963 return FIND_SUSPEND;
967 return FIND_CONT;
970 /* --------------------------------------------------------------------------------------------- */
972 * search_content:
974 * Search the content_pattern string in the DIRECTORY/FILE.
975 * It will add the found entries to the find listbox.
977 * returns FALSE if do_search should look for another file
978 * TRUE if do_search should exit and proceed to the event handler
981 static gboolean
982 search_content (WDialog * h, const char *directory, const char *filename)
984 struct stat s;
985 char buffer[BUF_4K]; /* raw input buffer */
986 int file_fd;
987 gboolean ret_val = FALSE;
988 vfs_path_t *vpath;
989 struct timeval tv;
990 time_t seconds;
991 suseconds_t useconds;
992 gboolean status_updated = FALSE;
994 vpath = vfs_path_build_filename (directory, filename, (char *) NULL);
996 if (mc_stat (vpath, &s) != 0 || !S_ISREG (s.st_mode))
998 vfs_path_free (vpath);
999 return FALSE;
1002 file_fd = mc_open (vpath, O_RDONLY);
1003 vfs_path_free (vpath);
1005 if (file_fd == -1)
1006 return FALSE;
1008 /* get time elapsed from last refresh */
1009 if (gettimeofday (&tv, NULL) == -1)
1011 tv.tv_sec = 0;
1012 tv.tv_usec = 0;
1013 last_refresh = tv;
1015 seconds = tv.tv_sec - last_refresh.tv_sec;
1016 useconds = tv.tv_usec - last_refresh.tv_usec;
1017 if (useconds < 0)
1019 seconds--;
1020 useconds += G_USEC_PER_SEC;
1023 if (s.st_size >= MIN_REFRESH_FILE_SIZE || seconds > 0 || useconds > MAX_REFRESH_INTERVAL)
1025 g_snprintf (buffer, sizeof (buffer), _("Grepping in %s"), filename);
1026 status_update (str_trunc (buffer, WIDGET (h)->cols - 8));
1027 mc_refresh ();
1028 last_refresh = tv;
1029 status_updated = TRUE;
1032 tty_enable_interrupt_key ();
1033 tty_got_interrupt ();
1036 int line = 1;
1037 int pos = 0;
1038 int n_read = 0;
1039 off_t off = 0; /* file_fd's offset corresponding to strbuf[0] */
1040 gboolean found = FALSE;
1041 gsize found_len;
1042 gsize found_start;
1043 char result[BUF_MEDIUM];
1044 char *strbuf = NULL; /* buffer for fetched string */
1045 int strbuf_size = 0;
1046 int i = -1; /* compensate for a newline we'll add when we first enter the loop */
1048 if (resuming)
1050 /* We've been previously suspended, start from the previous position */
1051 resuming = FALSE;
1052 line = last_line;
1053 pos = last_pos;
1054 off = last_off;
1055 i = last_i;
1058 while (!ret_val)
1060 char ch = '\0';
1061 off += i + 1; /* the previous line, plus a newline character */
1062 i = 0;
1064 /* read to buffer and get line from there */
1065 while (TRUE)
1067 if (pos >= n_read)
1069 pos = 0;
1070 n_read = mc_read (file_fd, buffer, sizeof (buffer));
1071 if (n_read <= 0)
1072 break;
1075 ch = buffer[pos++];
1076 if (ch == '\0')
1078 /* skip possible leading zero(s) */
1079 if (i == 0)
1081 off++;
1082 continue;
1084 break;
1087 if (i >= strbuf_size - 1)
1089 strbuf_size += 128;
1090 strbuf = g_realloc (strbuf, strbuf_size);
1093 /* Strip newline */
1094 if (ch == '\n')
1095 break;
1097 strbuf[i++] = ch;
1100 if (i == 0)
1102 if (ch == '\0')
1103 break;
1105 /* if (ch == '\n'): do not search in empty strings */
1106 goto skip_search;
1109 strbuf[i] = '\0';
1111 if (!found /* Search in binary line once */
1112 && mc_search_run (search_content_handle, (const void *) strbuf, 0, i, &found_len))
1114 if (!status_updated)
1116 /* if we add results for a file, we have to ensure that
1117 name of this file is shown in status bar */
1118 g_snprintf (result, sizeof (result), _("Grepping in %s"), filename);
1119 status_update (str_trunc (result, WIDGET (h)->cols - 8));
1120 mc_refresh ();
1121 last_refresh = tv;
1122 status_updated = TRUE;
1125 g_snprintf (result, sizeof (result), "%d:%s", line, filename);
1126 found_start = off + search_content_handle->normal_offset + 1; /* off by one: ticket 3280 */
1127 find_add_match (directory, result, found_start, found_start + found_len);
1128 found = TRUE;
1131 if (found && options.content_first_hit)
1132 break;
1134 if (ch == '\n')
1136 skip_search:
1137 found = FALSE;
1138 line++;
1141 if ((line & 0xff) == 0)
1143 FindProgressStatus res;
1144 res = check_find_events (h);
1145 switch (res)
1147 case FIND_ABORT:
1148 stop_idle (h);
1149 ret_val = TRUE;
1150 break;
1151 case FIND_SUSPEND:
1152 resuming = TRUE;
1153 last_line = line;
1154 last_pos = pos;
1155 last_off = off;
1156 last_i = i;
1157 ret_val = TRUE;
1158 break;
1159 default:
1160 break;
1165 g_free (strbuf);
1168 tty_disable_interrupt_key ();
1169 mc_close (file_fd);
1170 return ret_val;
1173 /* --------------------------------------------------------------------------------------------- */
1176 If dir is absolute, this means we're within dir and searching file here.
1177 If dir is relative, this means we're going to add dir to the directory stack.
1179 static gboolean
1180 find_ignore_dir_search (const char *dir)
1182 if (find_ignore_dirs != NULL)
1184 const size_t dlen = strlen (dir);
1185 const unsigned char dabs = g_path_is_absolute (dir) ? 1 : 0;
1187 char **ignore_dir;
1189 for (ignore_dir = find_ignore_dirs; *ignore_dir != NULL; ignore_dir++)
1191 const size_t ilen = strlen (*ignore_dir);
1192 const unsigned char iabs = g_path_is_absolute (*ignore_dir) ? 2 : 0;
1194 /* ignore dir is too long -- skip it */
1195 if (dlen < ilen)
1196 continue;
1198 /* handle absolute and relative paths */
1199 switch (iabs | dabs)
1201 case 0: /* both paths are relative */
1202 case 3: /* both paths are abolute */
1203 /* if ignore dir is not a path of dir -- skip it */
1204 if (strncmp (dir, *ignore_dir, ilen) == 0)
1206 /* be sure that ignore dir is not a part of dir like:
1207 ignore dir is "h", dir is "home" */
1208 if (dir[ilen] == '\0' || IS_PATH_SEP (dir[ilen]))
1209 return TRUE;
1211 break;
1212 case 1: /* dir is absolute, ignore_dir is relative */
1214 char *d;
1216 d = strstr (dir, *ignore_dir);
1217 if (d != NULL && IS_PATH_SEP (d[-1])
1218 && (d[ilen] == '\0' || IS_PATH_SEP (d[ilen])))
1219 return TRUE;
1221 break;
1222 case 2: /* dir is relative, ignore_dir is absolute */
1223 /* FIXME: skip this case */
1224 break;
1225 default: /* this cannot occurs */
1226 return FALSE;
1231 return FALSE;
1234 /* --------------------------------------------------------------------------------------------- */
1236 static void
1237 find_rotate_dash (const WDialog * h, gboolean show)
1239 static size_t pos = 0;
1240 static const char rotating_dash[4] = "|/-\\";
1241 const Widget *w = CONST_WIDGET (h);
1243 if (!verbose)
1244 return;
1246 tty_setcolor (h->color[DLG_COLOR_NORMAL]);
1247 widget_move (h, w->lines - 7, w->cols - 4);
1248 tty_print_char (show ? rotating_dash[pos] : ' ');
1249 pos = (pos + 1) % sizeof (rotating_dash);
1250 mc_refresh ();
1253 /* --------------------------------------------------------------------------------------------- */
1255 static int
1256 do_search (WDialog * h)
1258 static struct dirent *dp = NULL;
1259 static DIR *dirp = NULL;
1260 static char *directory = NULL;
1261 struct stat tmp_stat;
1262 gsize bytes_found;
1263 unsigned short count;
1265 if (h == NULL)
1266 { /* someone forces me to close dirp */
1267 if (dirp != NULL)
1269 mc_closedir (dirp);
1270 dirp = NULL;
1272 MC_PTR_FREE (directory);
1273 dp = NULL;
1274 return 1;
1277 for (count = 0; count < 32; count++)
1279 while (dp == NULL)
1281 if (dirp != NULL)
1283 mc_closedir (dirp);
1284 dirp = NULL;
1287 while (dirp == NULL)
1289 vfs_path_t *tmp_vpath = NULL;
1291 tty_setcolor (REVERSE_COLOR);
1293 while (TRUE)
1295 tmp_vpath = pop_directory ();
1296 if (tmp_vpath == NULL)
1298 running = FALSE;
1299 if (ignore_count == 0)
1300 status_update (_("Finished"));
1301 else
1303 char msg[BUF_SMALL];
1304 g_snprintf (msg, sizeof (msg),
1305 ngettext ("Finished (ignored %zd directory)",
1306 "Finished (ignored %zd directories)",
1307 ignore_count), ignore_count);
1308 status_update (msg);
1310 find_rotate_dash (h, FALSE);
1311 stop_idle (h);
1312 return 0;
1315 /* handle absolute ignore dirs here */
1317 gboolean ok;
1319 ok = find_ignore_dir_search (vfs_path_as_str (tmp_vpath));
1320 if (!ok)
1321 break;
1324 vfs_path_free (tmp_vpath);
1325 ignore_count++;
1328 g_free (directory);
1329 directory = g_strdup (vfs_path_as_str (tmp_vpath));
1331 if (verbose)
1333 char buffer[BUF_MEDIUM];
1335 g_snprintf (buffer, sizeof (buffer), _("Searching %s"), directory);
1336 status_update (str_trunc (directory, WIDGET (h)->cols - 8));
1339 dirp = mc_opendir (tmp_vpath);
1340 vfs_path_free (tmp_vpath);
1341 } /* while (!dirp) */
1343 /* skip invalid filenames */
1344 while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1346 } /* while (!dp) */
1348 if (DIR_IS_DOT (dp->d_name) || DIR_IS_DOTDOT (dp->d_name))
1350 /* skip invalid filenames */
1351 while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1354 return 1;
1357 if (!(options.skip_hidden && (dp->d_name[0] == '.')))
1359 gboolean search_ok;
1361 if (options.find_recurs && (directory != NULL))
1362 { /* Can directory be NULL ? */
1363 /* handle relative ignore dirs here */
1364 if (options.ignore_dirs_enable && find_ignore_dir_search (dp->d_name))
1365 ignore_count++;
1366 else
1368 vfs_path_t *tmp_vpath;
1370 tmp_vpath = vfs_path_build_filename (directory, dp->d_name, (char *) NULL);
1372 if (mc_lstat (tmp_vpath, &tmp_stat) == 0 && S_ISDIR (tmp_stat.st_mode))
1373 push_directory (tmp_vpath);
1374 else
1375 vfs_path_free (tmp_vpath);
1379 search_ok = mc_search_run (search_file_handle, dp->d_name,
1380 0, strlen (dp->d_name), &bytes_found);
1382 if (search_ok)
1384 if (content_pattern == NULL)
1385 find_add_match (directory, dp->d_name, 0, 0);
1386 else if (search_content (h, directory, dp->d_name))
1387 return 1;
1391 /* skip invalid filenames */
1392 while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1394 } /* for */
1396 find_rotate_dash (h, TRUE);
1398 return 1;
1401 /* --------------------------------------------------------------------------------------------- */
1403 static void
1404 init_find_vars (void)
1406 MC_PTR_FREE (old_dir);
1407 matches = 0;
1408 ignore_count = 0;
1410 /* Remove all the items from the stack */
1411 clear_stack ();
1413 g_strfreev (find_ignore_dirs);
1414 find_ignore_dirs = NULL;
1417 /* --------------------------------------------------------------------------------------------- */
1419 static void
1420 find_do_view_edit (gboolean unparsed_view, gboolean edit, char *dir, char *file, off_t search_start,
1421 off_t search_end)
1423 char *fullname = NULL;
1424 const char *filename = NULL;
1425 int line;
1426 vfs_path_t *fullname_vpath;
1428 if (content_pattern != NULL)
1430 filename = strchr (file + 4, ':') + 1;
1431 line = atoi (file + 4);
1433 else
1435 filename = file + 4;
1436 line = 0;
1439 fullname_vpath = vfs_path_build_filename (dir, filename, (char *) NULL);
1440 if (edit)
1441 edit_file_at_line (fullname_vpath, use_internal_edit != 0, line);
1442 else
1443 view_file_at_line (fullname_vpath, unparsed_view, use_internal_view != 0, line,
1444 search_start, search_end);
1445 vfs_path_free (fullname_vpath);
1446 g_free (fullname);
1449 /* --------------------------------------------------------------------------------------------- */
1451 static cb_ret_t
1452 view_edit_currently_selected_file (gboolean unparsed_view, gboolean edit)
1454 char *text = NULL;
1455 find_match_location_t *location;
1457 listbox_get_current (find_list, &text, (void **) &location);
1459 if ((text == NULL) || (location == NULL) || (location->dir == NULL))
1460 return MSG_NOT_HANDLED;
1462 find_do_view_edit (unparsed_view, edit, location->dir, text, location->start, location->end);
1463 return MSG_HANDLED;
1466 /* --------------------------------------------------------------------------------------------- */
1468 static void
1469 find_calc_button_locations (const WDialog * h, gboolean all_buttons)
1471 const int cols = CONST_WIDGET (h)->cols;
1473 int l1, l2;
1475 l1 = fbuts[0].len + fbuts[1].len + fbuts[is_start ? 3 : 2].len + fbuts[4].len + 3;
1476 l2 = fbuts[5].len + fbuts[6].len + fbuts[7].len + 2;
1478 fbuts[0].x = (cols - l1) / 2;
1479 fbuts[1].x = fbuts[0].x + fbuts[0].len + 1;
1480 fbuts[2].x = fbuts[1].x + fbuts[1].len + 1;
1481 fbuts[3].x = fbuts[2].x;
1482 fbuts[4].x = fbuts[2].x + fbuts[is_start ? 3 : 2].len + 1;
1484 if (all_buttons)
1486 fbuts[5].x = (cols - l2) / 2;
1487 fbuts[6].x = fbuts[5].x + fbuts[5].len + 1;
1488 fbuts[7].x = fbuts[6].x + fbuts[6].len + 1;
1492 /* --------------------------------------------------------------------------------------------- */
1494 static void
1495 find_relocate_buttons (const WDialog * h, gboolean all_buttons)
1497 size_t i;
1499 find_calc_button_locations (h, all_buttons);
1501 for (i = 0; i < fbuts_num; i++)
1502 fbuts[i].button->x = CONST_WIDGET (h)->x + fbuts[i].x;
1505 /* --------------------------------------------------------------------------------------------- */
1507 static cb_ret_t
1508 find_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
1510 WDialog *h = DIALOG (w);
1512 switch (msg)
1514 case MSG_KEY:
1515 if (parm == KEY_F (3) || parm == KEY_F (13))
1517 gboolean unparsed_view = (parm == KEY_F (13));
1519 return view_edit_currently_selected_file (unparsed_view, FALSE);
1521 if (parm == KEY_F (4))
1522 return view_edit_currently_selected_file (FALSE, TRUE);
1523 return MSG_NOT_HANDLED;
1525 case MSG_RESIZE:
1526 dlg_set_size (h, LINES - 4, COLS - 16);
1527 find_relocate_buttons (h, TRUE);
1528 return MSG_HANDLED;
1530 case MSG_IDLE:
1531 do_search (h);
1532 return MSG_HANDLED;
1534 default:
1535 return dlg_default_callback (w, sender, msg, parm, data);
1539 /* --------------------------------------------------------------------------------------------- */
1540 /** Handles the Stop/Start button in the find window */
1542 static int
1543 start_stop (WButton * button, int action)
1545 Widget *w = WIDGET (button);
1547 (void) action;
1549 running = is_start;
1550 widget_idle (WIDGET (find_dlg), running);
1551 is_start = !is_start;
1553 status_update (is_start ? _("Stopped") : _("Searching"));
1554 button_set_text (button, fbuts[is_start ? 3 : 2].text);
1556 find_relocate_buttons (w->owner, FALSE);
1557 dlg_redraw (w->owner);
1559 return 0;
1562 /* --------------------------------------------------------------------------------------------- */
1563 /** Handle view command, when invoked as a button */
1565 static int
1566 find_do_view_file (WButton * button, int action)
1568 (void) button;
1569 (void) action;
1571 view_edit_currently_selected_file (FALSE, FALSE);
1572 return 0;
1575 /* --------------------------------------------------------------------------------------------- */
1576 /** Handle edit command, when invoked as a button */
1578 static int
1579 find_do_edit_file (WButton * button, int action)
1581 (void) button;
1582 (void) action;
1584 view_edit_currently_selected_file (FALSE, TRUE);
1585 return 0;
1588 /* --------------------------------------------------------------------------------------------- */
1590 static void
1591 setup_gui (void)
1593 size_t i;
1594 int lines, cols;
1595 int y;
1597 static gboolean i18n_flag = FALSE;
1599 if (!i18n_flag)
1601 for (i = 0; i < fbuts_num; i++)
1603 #ifdef ENABLE_NLS
1604 fbuts[i].text = _(fbuts[i].text);
1605 #endif /* ENABLE_NLS */
1606 fbuts[i].len = str_term_width1 (fbuts[i].text) + 3;
1607 if (fbuts[i].flags == DEFPUSH_BUTTON)
1608 fbuts[i].len += 2;
1611 i18n_flag = TRUE;
1614 lines = LINES - 4;
1615 cols = COLS - 16;
1617 find_dlg =
1618 dlg_create (TRUE, 0, 0, lines, cols, dialog_colors, find_callback, NULL, "[Find File]",
1619 _("Find File"), DLG_CENTER);
1621 find_calc_button_locations (find_dlg, TRUE);
1623 y = 2;
1624 find_list = listbox_new (y, 2, lines - 10, cols - 4, FALSE, NULL);
1625 add_widget_autopos (find_dlg, find_list, WPOS_KEEP_ALL, NULL);
1626 y += WIDGET (find_list)->lines;
1628 add_widget_autopos (find_dlg, hline_new (y++, -1, -1), WPOS_KEEP_BOTTOM, NULL);
1630 found_num_label = label_new (y++, 4, "");
1631 add_widget_autopos (find_dlg, found_num_label, WPOS_KEEP_BOTTOM, NULL);
1633 status_label = label_new (y++, 4, _("Searching"));
1634 add_widget_autopos (find_dlg, status_label, WPOS_KEEP_BOTTOM, NULL);
1636 add_widget_autopos (find_dlg, hline_new (y++, -1, -1), WPOS_KEEP_BOTTOM, NULL);
1638 for (i = 0; i < fbuts_num; i++)
1640 if (i == 3)
1641 fbuts[3].button = fbuts[2].button;
1642 else
1644 fbuts[i].button =
1645 WIDGET (button_new
1646 (y, fbuts[i].x, fbuts[i].ret_cmd, fbuts[i].flags, fbuts[i].text,
1647 fbuts[i].callback));
1648 add_widget_autopos (find_dlg, fbuts[i].button, WPOS_KEEP_BOTTOM, NULL);
1651 if (i == quit_button)
1652 y++;
1655 dlg_select_widget (find_list);
1658 /* --------------------------------------------------------------------------------------------- */
1660 static int
1661 run_process (void)
1663 int ret;
1665 search_content_handle = mc_search_new (content_pattern, NULL);
1666 if (search_content_handle)
1668 search_content_handle->search_type =
1669 options.content_regexp ? MC_SEARCH_T_REGEX : MC_SEARCH_T_NORMAL;
1670 search_content_handle->is_case_sensitive = options.content_case_sens;
1671 search_content_handle->whole_words = options.content_whole_words;
1672 #ifdef HAVE_CHARSET
1673 search_content_handle->is_all_charsets = options.content_all_charsets;
1674 #endif
1676 search_file_handle = mc_search_new (find_pattern, NULL);
1677 search_file_handle->search_type = options.file_pattern ? MC_SEARCH_T_GLOB : MC_SEARCH_T_REGEX;
1678 search_file_handle->is_case_sensitive = options.file_case_sens;
1679 #ifdef HAVE_CHARSET
1680 search_file_handle->is_all_charsets = options.file_all_charsets;
1681 #endif
1682 search_file_handle->is_entire_line = options.file_pattern;
1684 resuming = FALSE;
1686 widget_idle (WIDGET (find_dlg), TRUE);
1687 ret = dlg_run (find_dlg);
1689 mc_search_free (search_file_handle);
1690 search_file_handle = NULL;
1691 mc_search_free (search_content_handle);
1692 search_content_handle = NULL;
1694 return ret;
1697 /* --------------------------------------------------------------------------------------------- */
1699 static void
1700 kill_gui (void)
1702 widget_idle (WIDGET (find_dlg), FALSE);
1703 dlg_destroy (find_dlg);
1706 /* --------------------------------------------------------------------------------------------- */
1708 static int
1709 do_find (const char *start_dir, ssize_t start_dir_len, const char *ignore_dirs,
1710 const char *pattern, const char *content, char **dirname, char **filename)
1712 int return_value = 0;
1713 char *dir_tmp = NULL, *file_tmp = NULL;
1715 setup_gui ();
1717 /* FIXME: Need to cleanup this, this ought to be passed non-globaly */
1718 find_pattern = (char *) pattern;
1720 content_pattern = NULL;
1721 if (content != NULL && str_is_valid_string (content))
1722 content_pattern = g_strdup (content);
1724 init_find_vars ();
1725 parse_ignore_dirs (ignore_dirs);
1726 push_directory (vfs_path_from_str (start_dir));
1728 return_value = run_process ();
1730 /* Clear variables */
1731 init_find_vars ();
1733 get_list_info (&file_tmp, &dir_tmp, NULL, NULL);
1735 if (dir_tmp)
1736 *dirname = g_strdup (dir_tmp);
1737 if (file_tmp)
1738 *filename = g_strdup (file_tmp);
1740 if (return_value == B_PANELIZE && *filename)
1742 int link_to_dir, stale_link;
1743 int i;
1744 struct stat st;
1745 GList *entry;
1746 dir_list *list = &current_panel->dir;
1747 char *name = NULL;
1749 dir_list_init (list);
1751 for (i = 0, entry = listbox_get_first_link (find_list); entry != NULL;
1752 i++, entry = g_list_next (entry))
1754 const char *lc_filename = NULL;
1755 WLEntry *le = LENTRY (entry->data);
1756 find_match_location_t *location = le->data;
1757 char *p;
1759 if ((le->text == NULL) || (location == NULL) || (location->dir == NULL))
1760 continue;
1762 if (content_pattern != NULL)
1763 lc_filename = strchr (le->text + 4, ':') + 1;
1764 else
1765 lc_filename = le->text + 4;
1767 name = mc_build_filename (location->dir, lc_filename, (char *) NULL);
1768 /* skip initial start dir */
1769 if (start_dir_len < 0)
1770 p = name;
1771 else
1773 p = name + (size_t) start_dir_len;
1774 if (IS_PATH_SEP (*p))
1775 p++;
1778 if (!handle_path (p, &st, &link_to_dir, &stale_link))
1780 g_free (name);
1781 continue;
1783 /* Need to grow the *list? */
1784 if (list->len == list->size && !dir_list_grow (list, DIR_LIST_RESIZE_STEP))
1786 g_free (name);
1787 break;
1790 /* don't add files more than once to the panel */
1791 if (content_pattern != NULL && list->len != 0
1792 && strcmp (list->list[list->len - 1].fname, p) == 0)
1794 g_free (name);
1795 continue;
1798 if (list->len == 0) /* first turn i.e clean old list */
1799 panel_clean_dir (current_panel);
1800 list->list[list->len].fnamelen = strlen (p);
1801 list->list[list->len].fname = g_strndup (p, list->list[list->len].fnamelen);
1802 list->list[list->len].f.marked = 0;
1803 list->list[list->len].f.link_to_dir = link_to_dir;
1804 list->list[list->len].f.stale_link = stale_link;
1805 list->list[list->len].f.dir_size_computed = 0;
1806 list->list[list->len].st = st;
1807 list->list[list->len].sort_key = NULL;
1808 list->list[list->len].second_sort_key = NULL;
1809 list->len++;
1810 g_free (name);
1811 if ((list->len & 15) == 0)
1812 rotate_dash (TRUE);
1815 if (list->len != 0)
1817 current_panel->is_panelized = TRUE;
1819 /* absolute path */
1820 if (start_dir_len < 0)
1822 int ret;
1823 vfs_path_free (current_panel->cwd_vpath);
1824 current_panel->cwd_vpath = vfs_path_from_str (PATH_SEP_STR);
1825 ret = chdir (PATH_SEP_STR);
1826 (void) ret;
1828 panelize_save_panel (current_panel);
1832 g_free (content_pattern);
1833 kill_gui ();
1834 do_search (NULL); /* force do_search to release resources */
1835 MC_PTR_FREE (old_dir);
1836 rotate_dash (FALSE);
1838 return return_value;
1841 /* --------------------------------------------------------------------------------------------- */
1842 /*** public functions ****************************************************************************/
1843 /* --------------------------------------------------------------------------------------------- */
1845 void
1846 find_file (void)
1848 char *start_dir = NULL, *pattern = NULL, *content = NULL, *ignore_dirs = NULL;
1849 ssize_t start_dir_len;
1851 while (find_parameters (&start_dir, &start_dir_len, &ignore_dirs, &pattern, &content))
1853 char *filename = NULL, *dirname = NULL;
1854 int v = B_CANCEL;
1856 if (pattern[0] != '\0')
1858 last_refresh.tv_sec = 0;
1859 last_refresh.tv_usec = 0;
1861 is_start = FALSE;
1862 v = do_find (start_dir, start_dir_len, ignore_dirs, pattern, content, &dirname,
1863 &filename);
1866 g_free (start_dir);
1867 g_free (ignore_dirs);
1868 g_free (pattern);
1870 if (v == B_ENTER)
1872 if (dirname != NULL)
1874 vfs_path_t *dirname_vpath;
1876 dirname_vpath = vfs_path_from_str (dirname);
1877 do_cd (dirname_vpath, cd_exact);
1878 vfs_path_free (dirname_vpath);
1879 if (filename != NULL)
1880 try_to_select (current_panel,
1881 filename + (content != NULL
1882 ? strchr (filename + 4, ':') - filename + 1 : 4));
1884 else if (filename != NULL)
1886 vfs_path_t *filename_vpath;
1888 filename_vpath = vfs_path_from_str (filename);
1889 do_cd (filename_vpath, cd_exact);
1890 vfs_path_free (filename_vpath);
1894 g_free (content);
1895 g_free (dirname);
1896 g_free (filename);
1898 if (v == B_ENTER || v == B_CANCEL)
1899 break;
1901 if (v == B_PANELIZE)
1903 panel_re_sort (current_panel);
1904 try_to_select (current_panel, NULL);
1905 break;
1910 /* --------------------------------------------------------------------------------------------- */