Associate label with input line and enable/disable them as pair.
[midnight-commander.git] / src / filemanager / find.c
blobece4c9fd06cbf15034018a5f25ea2b6827ab0b97
1 /*
2 Find file command for the Midnight Commander
4 Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
5 2006, 2007, 2011
6 The Free Software Foundation, Inc.
8 Written by:
9 Miguel de Icaza, 1995
11 This file is part of the Midnight Commander.
13 The Midnight Commander is free software: you can redistribute it
14 and/or modify it under the terms of the GNU General Public License as
15 published by the Free Software Foundation, either version 3 of the License,
16 or (at your option) any later version.
18 The Midnight Commander is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
23 You should have received a copy of the GNU General Public License
24 along with this program. If not, see <http://www.gnu.org/licenses/>.
27 /** \file find.c
28 * \brief Source: Find file command
31 #include <config.h>
33 #include <ctype.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <fcntl.h>
38 #include <sys/stat.h>
40 #include "lib/global.h"
42 #include "lib/tty/tty.h"
43 #include "lib/tty/key.h"
44 #include "lib/skin.h"
45 #include "lib/search.h"
46 #include "lib/mcconfig.h"
47 #include "lib/vfs/vfs.h"
48 #include "lib/strutil.h"
49 #include "lib/widget.h"
50 #include "lib/util.h" /* canonicalize_pathname() */
52 #include "src/setup.h" /* verbose */
53 #include "src/history.h" /* MC_HISTORY_SHARED_SEARCH */
55 #include "dir.h"
56 #include "cmd.h" /* view_file_at_line */
57 #include "midnight.h" /* current_panel */
58 #include "boxes.h"
59 #include "panelize.h"
61 #include "find.h"
63 /*** global variables ****************************************************************************/
65 /*** file scope macro definitions ****************************************************************/
67 /* Size of the find window */
68 #define FIND2_Y (LINES - 4)
70 #define FIND2_X_USE (FIND2_X - 20)
72 /*** file scope type declarations ****************************************************************/
74 /* A couple of extra messages we need */
75 enum
77 B_STOP = B_USER + 1,
78 B_AGAIN,
79 B_PANELIZE,
80 B_TREE,
81 B_VIEW
84 typedef enum
86 FIND_CONT = 0,
87 FIND_SUSPEND,
88 FIND_ABORT
89 } FindProgressStatus;
91 /* find file options */
92 typedef struct
94 /* file name options */
95 gboolean file_case_sens;
96 gboolean file_pattern;
97 gboolean find_recurs;
98 gboolean skip_hidden;
99 gboolean file_all_charsets;
101 /* file content options */
102 gboolean content_use;
103 gboolean content_case_sens;
104 gboolean content_regexp;
105 gboolean content_first_hit;
106 gboolean content_whole_words;
107 gboolean content_all_charsets;
109 /* whether use ignore dirs or not */
110 gboolean ignore_dirs_enable;
111 /* list of directories to be ignored, separated by ':' */
112 char *ignore_dirs;
113 } find_file_options_t;
115 /*** file scope variables ************************************************************************/
117 /* Parsed ignore dirs */
118 static char **find_ignore_dirs = NULL;
120 /* Size of the find parameters window */
121 #ifdef HAVE_CHARSET
122 static int FIND_Y = 19;
123 #else
124 static int FIND_Y = 18;
125 #endif
126 static int FIND_X = 68;
128 static int FIND2_X = 64;
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_use_cbox; /* Take into account the Content field */
141 static WCheck *content_case_sens_cbox; /* "case sensitive" checkbox */
142 static WCheck *content_regexp_cbox; /* "find regular expression" checkbox */
143 static WCheck *content_first_hit_cbox; /* "First hit" checkbox" */
144 static WCheck *content_whole_words_cbox; /* "whole words" checkbox */
145 #ifdef HAVE_CHARSET
146 static WCheck *file_all_charsets_cbox;
147 static WCheck *content_all_charsets_cbox;
148 #endif
149 static WCheck *ignore_dirs_cbox;
151 static gboolean running = FALSE; /* nice flag */
152 static char *find_pattern = NULL; /* Pattern to search */
153 static char *content_pattern = NULL; /* pattern to search inside files; if
154 content_regexp_flag is true, it contains the
155 regex pattern, else the search string. */
156 static unsigned long matches; /* Number of matches */
157 static gboolean is_start = FALSE; /* Status of the start/stop toggle button */
158 static char *old_dir = NULL;
160 /* Where did we stop */
161 static int resuming;
162 static int last_line;
163 static int last_pos;
165 static size_t ignore_count = 0;
167 static Dlg_head *find_dlg; /* The dialog */
168 static WButton *stop_button; /* pointer to the stop button */
169 static WLabel *status_label; /* Finished, Searching etc. */
170 static WLabel *found_num_label; /* Number of found items */
171 static WListbox *find_list; /* Listbox with the file list */
173 /* This keeps track of the directory stack */
174 #if GLIB_CHECK_VERSION (2, 14, 0)
175 static GQueue dir_queue = G_QUEUE_INIT;
176 #else
177 typedef struct dir_stack
179 vfs_path_t *name;
180 struct dir_stack *prev;
181 } dir_stack;
183 static dir_stack *dir_stack_base = 0;
184 #endif /* GLIB_CHECK_VERSION */
186 /* *INDENT-OFF* */
187 static struct
189 const char *text;
190 int len; /* length including space and brackets */
191 int x;
192 } fbuts[] =
194 {N_("&Suspend"), 11, 29},
195 {N_("Con&tinue"), 12, 29},
196 {N_("&Chdir"), 11, 3},
197 {N_("&Again"), 9, 17},
198 {N_("&Quit"), 8, 43},
199 {N_("Pane&lize"), 12, 3},
200 {N_("&View - F3"), 13, 20},
201 {N_("&Edit - F4"), 13, 38}
203 /* *INDENT-ON* */
205 static find_file_options_t options = {
206 TRUE, TRUE, TRUE, FALSE, FALSE,
207 FALSE, TRUE, FALSE, FALSE, FALSE, FALSE
210 static char *in_start_dir = INPUT_LAST_TEXT;
212 static mc_search_t *search_file_handle = NULL;
213 static mc_search_t *search_content_handle = NULL;
215 /*** file scope functions ************************************************************************/
217 static void
218 parse_ignore_dirs (const char *ignore_dirs)
220 size_t r = 0, w = 0; /* read and write iterators */
222 if (!options.ignore_dirs_enable || ignore_dirs == NULL || ignore_dirs[0] == '\0')
223 return;
225 find_ignore_dirs = g_strsplit (ignore_dirs, ":", -1);
227 /* Values like '/foo::/bar: produce holes in list.
228 * Find and remove them */
229 for (; find_ignore_dirs[r] != NULL; r++)
231 if (find_ignore_dirs[r][0] == '\0')
233 /* empty entry -- skip it */
234 g_free (find_ignore_dirs[r]);
235 find_ignore_dirs[r] = NULL;
236 continue;
239 if (r != w)
241 /* copy entry to the previous free array cell */
242 find_ignore_dirs[w] = find_ignore_dirs[r];
243 find_ignore_dirs[r] = NULL;
246 canonicalize_pathname (find_ignore_dirs[w]);
247 if (find_ignore_dirs[w][0] != '\0')
248 w++;
249 else
251 g_free (find_ignore_dirs[w]);
252 find_ignore_dirs[w] = NULL;
256 if (find_ignore_dirs[0] == NULL)
258 g_strfreev (find_ignore_dirs);
259 find_ignore_dirs = NULL;
263 /* --------------------------------------------------------------------------------------------- */
265 static void
266 find_load_options (void)
268 static gboolean loaded = FALSE;
270 if (loaded)
271 return;
273 loaded = TRUE;
275 options.file_case_sens =
276 mc_config_get_bool (mc_main_config, "FindFile", "file_case_sens", TRUE);
277 options.file_pattern =
278 mc_config_get_bool (mc_main_config, "FindFile", "file_shell_pattern", TRUE);
279 options.find_recurs = mc_config_get_bool (mc_main_config, "FindFile", "file_find_recurs", TRUE);
280 options.skip_hidden =
281 mc_config_get_bool (mc_main_config, "FindFile", "file_skip_hidden", FALSE);
282 options.file_all_charsets =
283 mc_config_get_bool (mc_main_config, "FindFile", "file_all_charsets", FALSE);
284 options.content_use = mc_config_get_bool (mc_main_config, "FindFile", "content_use", TRUE);
285 options.content_case_sens =
286 mc_config_get_bool (mc_main_config, "FindFile", "content_case_sens", TRUE);
287 options.content_regexp =
288 mc_config_get_bool (mc_main_config, "FindFile", "content_regexp", FALSE);
289 options.content_first_hit =
290 mc_config_get_bool (mc_main_config, "FindFile", "content_first_hit", FALSE);
291 options.content_whole_words =
292 mc_config_get_bool (mc_main_config, "FindFile", "content_whole_words", FALSE);
293 options.content_all_charsets =
294 mc_config_get_bool (mc_main_config, "FindFile", "content_all_charsets", FALSE);
295 options.ignore_dirs_enable =
296 mc_config_get_bool (mc_main_config, "FindFile", "ignore_dirs_enable", TRUE);
297 options.ignore_dirs = mc_config_get_string (mc_main_config, "FindFile", "ignore_dirs", "");
299 if (options.ignore_dirs[0] == '\0')
301 g_free (options.ignore_dirs);
302 options.ignore_dirs = NULL;
306 /* --------------------------------------------------------------------------------------------- */
308 static void
309 find_save_options (void)
311 mc_config_set_bool (mc_main_config, "FindFile", "file_case_sens", options.file_case_sens);
312 mc_config_set_bool (mc_main_config, "FindFile", "file_shell_pattern", options.file_pattern);
313 mc_config_set_bool (mc_main_config, "FindFile", "file_find_recurs", options.find_recurs);
314 mc_config_set_bool (mc_main_config, "FindFile", "file_skip_hidden", options.skip_hidden);
315 mc_config_set_bool (mc_main_config, "FindFile", "file_all_charsets", options.file_all_charsets);
316 mc_config_set_bool (mc_main_config, "FindFile", "content_use", options.content_use);
317 mc_config_set_bool (mc_main_config, "FindFile", "content_case_sens", options.content_case_sens);
318 mc_config_set_bool (mc_main_config, "FindFile", "content_regexp", options.content_regexp);
319 mc_config_set_bool (mc_main_config, "FindFile", "content_first_hit", options.content_first_hit);
320 mc_config_set_bool (mc_main_config, "FindFile", "content_whole_words",
321 options.content_whole_words);
322 mc_config_set_bool (mc_main_config, "FindFile", "content_all_charsets",
323 options.content_all_charsets);
324 mc_config_set_bool (mc_main_config, "FindFile", "ignore_dirs_enable",
325 options.ignore_dirs_enable);
326 mc_config_set_string (mc_main_config, "FindFile", "ignore_dirs", options.ignore_dirs);
329 /* --------------------------------------------------------------------------------------------- */
331 static inline char *
332 add_to_list (const char *text, void *data)
334 return listbox_add_item (find_list, LISTBOX_APPEND_AT_END, 0, text, data);
337 /* --------------------------------------------------------------------------------------------- */
339 static inline void
340 stop_idle (void *data)
342 set_idle_proc (data, 0);
345 /* --------------------------------------------------------------------------------------------- */
347 static inline void
348 status_update (const char *text)
350 label_set_text (status_label, text);
353 /* --------------------------------------------------------------------------------------------- */
355 static void
356 found_num_update (void)
358 char buffer[BUF_TINY];
359 g_snprintf (buffer, sizeof (buffer), _("Found: %ld"), matches);
360 label_set_text (found_num_label, buffer);
363 /* --------------------------------------------------------------------------------------------- */
365 static void
366 get_list_info (char **file, char **dir)
368 listbox_get_current (find_list, file, (void **) dir);
371 /* --------------------------------------------------------------------------------------------- */
372 /** check regular expression */
374 static gboolean
375 find_check_regexp (const char *r)
377 mc_search_t *search;
378 gboolean regexp_ok = FALSE;
380 search = mc_search_new (r, -1);
382 if (search != NULL)
384 search->search_type = MC_SEARCH_T_REGEX;
385 regexp_ok = mc_search_prepare (search);
386 mc_search_free (search);
389 return regexp_ok;
392 /* --------------------------------------------------------------------------------------------- */
394 * Callback for the parameter dialog.
395 * Validate regex, prevent closing the dialog if it's invalid.
398 static cb_ret_t
399 find_parm_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
401 switch (msg)
403 case DLG_ACTION:
404 if (sender == WIDGET (content_use_cbox))
406 gboolean disable = !(content_use_cbox->state & C_BOOL);
408 widget_disable (WIDGET (in_with), disable);
409 widget_disable (WIDGET (content_first_hit_cbox), disable);
410 widget_disable (WIDGET (content_regexp_cbox), disable);
411 widget_disable (WIDGET (content_case_sens_cbox), disable);
412 #ifdef HAVE_CHARSET
413 widget_disable (WIDGET (content_all_charsets_cbox), disable);
414 #endif
415 widget_disable (WIDGET (content_whole_words_cbox), disable);
417 return MSG_HANDLED;
420 if (sender == WIDGET (ignore_dirs_cbox))
422 gboolean disable = !(ignore_dirs_cbox->state & C_BOOL);
424 widget_disable (WIDGET (in_ignore), disable);
426 return MSG_HANDLED;
429 return MSG_NOT_HANDLED;
432 case DLG_VALIDATE:
433 if (h->ret_value != B_ENTER)
434 return MSG_HANDLED;
436 /* check filename regexp */
437 if (!(file_pattern_cbox->state & C_BOOL)
438 && (in_name->buffer[0] != '\0') && !find_check_regexp (in_name->buffer))
440 h->state = DLG_ACTIVE; /* Don't stop the dialog */
441 message (D_ERROR, MSG_ERROR, _("Malformed regular expression"));
442 dlg_select_widget (in_name);
443 return MSG_HANDLED;
446 /* check content regexp */
447 if ((content_use_cbox->state & C_BOOL) && (content_regexp_cbox->state & C_BOOL)
448 && (in_with->buffer[0] != '\0') && !find_check_regexp (in_with->buffer))
450 h->state = DLG_ACTIVE; /* Don't stop the dialog */
451 message (D_ERROR, MSG_ERROR, _("Malformed regular expression"));
452 dlg_select_widget (in_with);
453 return MSG_HANDLED;
456 return MSG_HANDLED;
458 default:
459 return default_dlg_callback (h, sender, msg, parm, data);
463 /* --------------------------------------------------------------------------------------------- */
465 * find_parameters: gets information from the user
467 * If the return value is TRUE, then the following holds:
469 * start_dir, ignore_dirs, pattern and content contain the information provided by the user.
470 * They are newly allocated strings and must be freed when uneeded.
472 * start_dir_len is -1 when user entered an absolute path, otherwise it is a length
473 * of start_dir (which is absolute). It is used to get a relative pats of find results.
476 static gboolean
477 find_parameters (char **start_dir, ssize_t * start_dir_len,
478 char **ignore_dirs, char **pattern, char **content)
480 gboolean return_value;
482 /* file name */
483 const char *file_case_label = N_("Cas&e sensitive");
484 const char *file_pattern_label = N_("&Using shell patterns");
485 const char *file_recurs_label = N_("&Find recursively");
486 const char *file_skip_hidden_label = N_("S&kip hidden");
487 #ifdef HAVE_CHARSET
488 const char *file_all_charsets_label = N_("&All charsets");
489 #endif
491 /* file content */
492 const char *content_use_label = N_("Sea&rch for content");
493 const char *content_case_label = N_("Case sens&itive");
494 const char *content_regexp_label = N_("Re&gular expression");
495 const char *content_first_hit_label = N_("Fir&st hit");
496 const char *content_whole_words_label = N_("&Whole words");
497 #ifdef HAVE_CHARSET
498 const char *content_all_charsets_label = N_("A&ll charsets");
499 #endif
501 const char *buts[] = { N_("&OK"), N_("&Cancel"), N_("&Tree") };
503 int b0, b1, b2;
505 int cbox_position;
506 gboolean disable;
508 #ifdef ENABLE_NLS
510 int i = sizeof (buts) / sizeof (buts[0]);
511 while (i-- != 0)
512 buts[i] = _(buts[i]);
514 file_case_label = _(file_case_label);
515 file_pattern_label = _(file_pattern_label);
516 file_recurs_label = _(file_recurs_label);
517 file_skip_hidden_label = _(file_skip_hidden_label);
518 #ifdef HAVE_CHARSET
519 file_all_charsets_label = _(file_all_charsets_label);
520 content_all_charsets_label = _(content_all_charsets_label);
521 #endif
522 content_use_label = _(content_use_label);
523 content_case_label = _(content_case_label);
524 content_regexp_label = _(content_regexp_label);
525 content_first_hit_label = _(content_first_hit_label);
526 content_whole_words_label = _(content_whole_words_label);
528 #endif /* ENABLE_NLS */
530 b0 = str_term_width1 (buts[0]) + 6; /* default button */
531 b1 = str_term_width1 (buts[1]) + 4;
532 b2 = str_term_width1 (buts[2]) + 4;
534 find_load_options ();
536 if (in_start_dir == NULL)
537 in_start_dir = g_strdup (".");
539 disable = !options.content_use;
541 find_dlg =
542 create_dlg (TRUE, 0, 0, FIND_Y, FIND_X, dialog_colors,
543 find_parm_callback, NULL, "[Find File]", _("Find File"),
544 DLG_CENTER | DLG_REVERSE);
546 add_widget (find_dlg,
547 button_new (FIND_Y - 3, FIND_X * 3 / 4 - b1 / 2, B_CANCEL, NORMAL_BUTTON, buts[1],
548 0));
549 add_widget (find_dlg,
550 button_new (FIND_Y - 3, FIND_X / 4 - b0 / 2, B_ENTER, DEFPUSH_BUTTON, buts[0], 0));
552 cbox_position = FIND_Y - 5;
554 content_first_hit_cbox =
555 check_new (cbox_position--, FIND_X / 2 + 1, options.content_first_hit,
556 content_first_hit_label);
557 widget_disable (WIDGET (content_first_hit_cbox), disable);
558 add_widget (find_dlg, content_first_hit_cbox);
560 content_whole_words_cbox =
561 check_new (cbox_position--, FIND_X / 2 + 1, options.content_whole_words,
562 content_whole_words_label);
563 widget_disable (WIDGET (content_whole_words_cbox), disable);
564 add_widget (find_dlg, content_whole_words_cbox);
566 #ifdef HAVE_CHARSET
567 content_all_charsets_cbox = check_new (cbox_position--, FIND_X / 2 + 1,
568 options.content_all_charsets,
569 content_all_charsets_label);
570 widget_disable (WIDGET (content_all_charsets_cbox), disable);
571 add_widget (find_dlg, content_all_charsets_cbox);
572 #endif
574 content_case_sens_cbox =
575 check_new (cbox_position--, FIND_X / 2 + 1, options.content_case_sens, content_case_label);
576 widget_disable (WIDGET (content_case_sens_cbox), disable);
577 add_widget (find_dlg, content_case_sens_cbox);
579 content_regexp_cbox =
580 check_new (cbox_position--, FIND_X / 2 + 1, options.content_regexp, content_regexp_label);
581 widget_disable (WIDGET (content_regexp_cbox), disable);
582 add_widget (find_dlg, content_regexp_cbox);
584 cbox_position = FIND_Y - 6;
586 skip_hidden_cbox = check_new (cbox_position--, 3, options.skip_hidden, file_skip_hidden_label);
587 add_widget (find_dlg, skip_hidden_cbox);
589 #ifdef HAVE_CHARSET
590 file_all_charsets_cbox =
591 check_new (cbox_position--, 3, options.file_all_charsets, file_all_charsets_label);
592 add_widget (find_dlg, file_all_charsets_cbox);
593 #endif
595 file_case_sens_cbox = check_new (cbox_position--, 3, options.file_case_sens, file_case_label);
596 add_widget (find_dlg, file_case_sens_cbox);
598 file_pattern_cbox = check_new (cbox_position--, 3, options.file_pattern, file_pattern_label);
599 add_widget (find_dlg, file_pattern_cbox);
601 recursively_cbox = check_new (cbox_position, 3, options.find_recurs, file_recurs_label);
602 add_widget (find_dlg, recursively_cbox);
604 /* This checkbox is located in the second column */
605 content_use_cbox =
606 check_new (cbox_position, FIND_X / 2 + 1, options.content_use, content_use_label);
607 add_widget (find_dlg, content_use_cbox);
609 in_with =
610 input_new (8, FIND_X / 2 + 1, input_get_default_colors (), FIND_X / 2 - 4, INPUT_LAST_TEXT,
611 MC_HISTORY_SHARED_SEARCH, INPUT_COMPLETE_DEFAULT);
612 content_label = label_new (7, FIND_X / 2 + 1, _("Content:"));
613 in_with->label = content_label;
614 widget_disable (WIDGET (in_with), disable);
615 add_widget (find_dlg, in_with);
616 add_widget (find_dlg, content_label);
618 in_name = input_new (8, 3, input_get_default_colors (),
619 FIND_X / 2 - 4, INPUT_LAST_TEXT, "name", INPUT_COMPLETE_DEFAULT);
620 add_widget (find_dlg, in_name);
621 add_widget (find_dlg, label_new (7, 3, _("File name:")));
623 in_ignore = input_new (5, 3, input_get_default_colors (), FIND_X - 6,
624 options.ignore_dirs != NULL ? options.ignore_dirs : "",
625 "ignoredirs", INPUT_COMPLETE_DEFAULT);
626 widget_disable (WIDGET (in_ignore), !options.ignore_dirs_enable);
627 add_widget (find_dlg, in_ignore);
629 ignore_dirs_cbox =
630 check_new (4, 3, options.ignore_dirs_enable, _("Ena&ble ignore directories:"));
631 add_widget (find_dlg, ignore_dirs_cbox);
633 add_widget (find_dlg, button_new (3, FIND_X - b2 - 2, B_TREE, NORMAL_BUTTON, buts[2], 0));
635 in_start = input_new (3, 3, input_get_default_colors (),
636 FIND_X - b2 - 6, in_start_dir, "start", INPUT_COMPLETE_DEFAULT);
637 add_widget (find_dlg, in_start);
638 add_widget (find_dlg, label_new (2, 3, _("Start at:")));
640 find_par_start:
641 dlg_select_widget (in_name);
643 switch (run_dlg (find_dlg))
645 case B_CANCEL:
646 return_value = FALSE;
647 break;
649 case B_TREE:
651 char *temp_dir;
653 temp_dir = in_start->buffer;
654 if ((temp_dir[0] == '\0') || ((temp_dir[0] == '.') && (temp_dir[1] == '\0')))
655 temp_dir = vfs_path_to_str (current_panel->cwd_vpath);
656 else
657 temp_dir = g_strdup (temp_dir);
659 if (in_start_dir != INPUT_LAST_TEXT)
660 g_free (in_start_dir);
661 in_start_dir = tree_box (temp_dir);
662 if (in_start_dir == NULL)
663 in_start_dir = temp_dir;
664 else
665 g_free (temp_dir);
667 input_assign_text (in_start, in_start_dir);
669 /* Warning: Dreadful goto */
670 goto find_par_start;
673 default:
675 char *s;
677 #ifdef HAVE_CHARSET
678 options.file_all_charsets = file_all_charsets_cbox->state & C_BOOL;
679 options.content_all_charsets = content_all_charsets_cbox->state & C_BOOL;
680 #endif
681 options.content_use = content_use_cbox->state & C_BOOL;
682 options.content_case_sens = content_case_sens_cbox->state & C_BOOL;
683 options.content_regexp = content_regexp_cbox->state & C_BOOL;
684 options.content_first_hit = content_first_hit_cbox->state & C_BOOL;
685 options.content_whole_words = content_whole_words_cbox->state & C_BOOL;
686 options.find_recurs = recursively_cbox->state & C_BOOL;
687 options.file_pattern = file_pattern_cbox->state & C_BOOL;
688 options.file_case_sens = file_case_sens_cbox->state & C_BOOL;
689 options.skip_hidden = skip_hidden_cbox->state & C_BOOL;
690 options.ignore_dirs_enable = ignore_dirs_cbox->state & C_BOOL;
691 g_free (options.ignore_dirs);
692 options.ignore_dirs = g_strdup (in_ignore->buffer);
694 *content = (options.content_use && in_with->buffer[0] != '\0')
695 ? g_strdup (in_with->buffer) : NULL;
696 *start_dir = in_start->buffer[0] != '\0' ? in_start->buffer : (char *) ".";
697 *pattern = g_strdup (in_name->buffer);
698 if (in_start_dir != INPUT_LAST_TEXT)
699 g_free (in_start_dir);
700 in_start_dir = g_strdup (*start_dir);
702 s = tilde_expand (*start_dir);
703 canonicalize_pathname (s);
705 if (s[0] == '.' && s[1] == '\0')
707 *start_dir = vfs_path_to_str (current_panel->cwd_vpath);
708 /* FIXME: is current_panel->cwd_vpath canonicalized? */
709 /* relative paths will be used in panelization */
710 *start_dir_len = (ssize_t) strlen (*start_dir);
711 g_free (s);
713 else if (g_path_is_absolute (s))
715 *start_dir = s;
716 *start_dir_len = -1;
718 else
720 /* relative paths will be used in panelization */
721 char *cwd_str;
723 cwd_str = vfs_path_to_str (current_panel->cwd_vpath);
724 *start_dir = mc_build_filename (cwd_str, s, (char *) NULL);
725 *start_dir_len = (ssize_t) strlen (cwd_str);
726 g_free (cwd_str);
727 g_free (s);
730 if (!options.ignore_dirs_enable || in_ignore->buffer[0] == '\0'
731 || (in_ignore->buffer[0] == '.' && in_ignore->buffer[1] == '\0'))
732 *ignore_dirs = NULL;
733 else
734 *ignore_dirs = g_strdup (in_ignore->buffer);
736 find_save_options ();
738 return_value = TRUE;
742 destroy_dlg (find_dlg);
744 return return_value;
747 /* --------------------------------------------------------------------------------------------- */
749 #if GLIB_CHECK_VERSION (2, 14, 0)
750 static inline void
751 push_directory (const vfs_path_t * dir)
753 g_queue_push_head (&dir_queue, (void *) dir);
756 /* --------------------------------------------------------------------------------------------- */
758 static inline vfs_path_t *
759 pop_directory (void)
761 return (vfs_path_t *) g_queue_pop_tail (&dir_queue);
764 /* --------------------------------------------------------------------------------------------- */
765 /** Remove all the items from the stack */
767 static void
768 clear_stack (void)
770 g_queue_foreach (&dir_queue, (GFunc) vfs_path_free, NULL);
771 g_queue_clear (&dir_queue);
774 /* --------------------------------------------------------------------------------------------- */
776 #else /* GLIB_CHECK_VERSION */
777 static void
778 push_directory (const vfs_path_t * dir)
780 dir_stack *new;
782 new = g_new (dir_stack, 1);
783 new->name = (vfs_path_t *) dir;
784 new->prev = dir_stack_base;
785 dir_stack_base = new;
788 /* --------------------------------------------------------------------------------------------- */
790 static vfs_path_t *
791 pop_directory (void)
793 vfs_path_t *name = NULL;
795 if (dir_stack_base != NULL)
797 dir_stack *next;
798 name = dir_stack_base->name;
799 next = dir_stack_base->prev;
800 g_free (dir_stack_base);
801 dir_stack_base = next;
804 return name;
807 /* --------------------------------------------------------------------------------------------- */
808 /** Remove all the items from the stack */
810 static void
811 clear_stack (void)
813 vfs_path_t *dir = NULL;
815 while ((dir = pop_directory ()) != NULL)
816 vfs_path_free (dir);
818 #endif /* GLIB_CHECK_VERSION */
820 /* --------------------------------------------------------------------------------------------- */
822 static void
823 insert_file (const char *dir, const char *file)
825 char *tmp_name = NULL;
826 static char *dirname = NULL;
828 while (dir[0] == PATH_SEP && dir[1] == PATH_SEP)
829 dir++;
831 if (old_dir)
833 if (strcmp (old_dir, dir))
835 g_free (old_dir);
836 old_dir = g_strdup (dir);
837 dirname = add_to_list (dir, NULL);
840 else
842 old_dir = g_strdup (dir);
843 dirname = add_to_list (dir, NULL);
846 tmp_name = g_strdup_printf (" %s", file);
847 add_to_list (tmp_name, dirname);
848 g_free (tmp_name);
851 /* --------------------------------------------------------------------------------------------- */
853 static void
854 find_add_match (const char *dir, const char *file)
856 insert_file (dir, file);
858 /* Don't scroll */
859 if (matches == 0)
860 listbox_select_first (find_list);
861 send_message (WIDGET (find_list), NULL, WIDGET_DRAW, 0, NULL);
863 matches++;
864 found_num_update ();
867 /* --------------------------------------------------------------------------------------------- */
869 * get_line_at:
871 * Returns malloced null-terminated line from file file_fd.
872 * Input is buffered in buf_size long buffer.
873 * Current pos in buf is stored in pos.
874 * n_read - number of read chars.
875 * has_newline - is there newline ?
878 static char *
879 get_line_at (int file_fd, char *buf, int buf_size, int *pos, int *n_read, gboolean * has_newline)
881 char *buffer = NULL;
882 int buffer_size = 0;
883 char ch = 0;
884 int i = 0;
886 while (TRUE)
888 if (*pos >= *n_read)
890 *pos = 0;
891 *n_read = mc_read (file_fd, buf, buf_size);
892 if (*n_read <= 0)
893 break;
896 ch = buf[(*pos)++];
897 if (ch == '\0')
899 /* skip possible leading zero(s) */
900 if (i == 0)
901 continue;
902 break;
905 if (i >= buffer_size - 1)
906 buffer = g_realloc (buffer, buffer_size += 80);
908 /* Strip newline */
909 if (ch == '\n')
910 break;
912 buffer[i++] = ch;
915 *has_newline = (ch != '\0');
917 if (buffer != NULL)
918 buffer[i] = '\0';
920 return buffer;
923 /* --------------------------------------------------------------------------------------------- */
925 static FindProgressStatus
926 check_find_events (Dlg_head * h)
928 Gpm_Event event;
929 int c;
931 event.x = -1;
932 c = tty_get_event (&event, h->mouse_status == MOU_REPEAT, FALSE);
933 if (c != EV_NONE)
935 dlg_process_event (h, c, &event);
936 if (h->ret_value == B_ENTER
937 || h->ret_value == B_CANCEL || h->ret_value == B_AGAIN || h->ret_value == B_PANELIZE)
939 /* dialog terminated */
940 return FIND_ABORT;
942 if (!(h->flags & DLG_WANT_IDLE))
944 /* searching suspended */
945 return FIND_SUSPEND;
949 return FIND_CONT;
952 /* --------------------------------------------------------------------------------------------- */
954 * search_content:
956 * Search the content_pattern string in the DIRECTORY/FILE.
957 * It will add the found entries to the find listbox.
959 * returns FALSE if do_search should look for another file
960 * TRUE if do_search should exit and proceed to the event handler
963 static gboolean
964 search_content (Dlg_head * h, const char *directory, const char *filename)
966 struct stat s;
967 char buffer[BUF_4K];
968 int file_fd;
969 gboolean ret_val = FALSE;
970 vfs_path_t *vpath;
972 vpath = vfs_path_build_filename (directory, filename, (char *) NULL);
974 if (mc_stat (vpath, &s) != 0 || !S_ISREG (s.st_mode))
976 vfs_path_free (vpath);
977 return FALSE;
980 file_fd = mc_open (vpath, O_RDONLY);
981 vfs_path_free (vpath);
983 if (file_fd == -1)
984 return FALSE;
986 g_snprintf (buffer, sizeof (buffer), _("Grepping in %s"), str_trunc (filename, FIND2_X_USE));
988 status_update (buffer);
989 mc_refresh ();
991 tty_enable_interrupt_key ();
992 tty_got_interrupt ();
995 int line = 1;
996 int pos = 0;
997 int n_read = 0;
998 gboolean has_newline;
999 char *p = NULL;
1000 gboolean found = FALSE;
1001 gsize found_len;
1002 char result[BUF_MEDIUM];
1004 if (resuming)
1006 /* We've been previously suspended, start from the previous position */
1007 resuming = 0;
1008 line = last_line;
1009 pos = last_pos;
1011 while (!ret_val
1012 && (p = get_line_at (file_fd, buffer, sizeof (buffer),
1013 &pos, &n_read, &has_newline)) != NULL)
1015 if (!found /* Search in binary line once */
1016 && mc_search_run (search_content_handle,
1017 (const void *) p, 0, strlen (p), &found_len))
1019 g_snprintf (result, sizeof (result), "%d:%s", line, filename);
1020 find_add_match (directory, result);
1021 found = TRUE;
1023 g_free (p);
1025 if (found && options.content_first_hit)
1026 break;
1028 if (has_newline)
1030 found = FALSE;
1031 line++;
1034 if ((line & 0xff) == 0)
1036 FindProgressStatus res;
1037 res = check_find_events (h);
1038 switch (res)
1040 case FIND_ABORT:
1041 stop_idle (h);
1042 ret_val = TRUE;
1043 break;
1044 case FIND_SUSPEND:
1045 resuming = 1;
1046 last_line = line;
1047 last_pos = pos;
1048 ret_val = TRUE;
1049 break;
1050 default:
1051 break;
1056 tty_disable_interrupt_key ();
1057 mc_close (file_fd);
1058 return ret_val;
1061 /* --------------------------------------------------------------------------------------------- */
1064 If dir is absolute, this means we're within dir and searching file here.
1065 If dir is relative, this means we're going to add dir to the directory stack.
1067 static gboolean
1068 find_ignore_dir_search (const char *dir)
1070 if (find_ignore_dirs != NULL)
1072 const size_t dlen = strlen (dir);
1073 const unsigned char dabs = g_path_is_absolute (dir) ? 1 : 0;
1075 char **ignore_dir;
1077 for (ignore_dir = find_ignore_dirs; *ignore_dir != NULL; ignore_dir++)
1079 const size_t ilen = strlen (*ignore_dir);
1080 const unsigned char iabs = g_path_is_absolute (*ignore_dir) ? 2 : 0;
1082 /* ignore dir is too long -- skip it */
1083 if (dlen < ilen)
1084 continue;
1086 /* handle absolute and relative paths */
1087 switch (iabs | dabs)
1089 case 0: /* both paths are relative */
1090 case 3: /* both paths are abolute */
1091 /* if ignore dir is not a path of dir -- skip it */
1092 if (strncmp (dir, *ignore_dir, ilen) == 0)
1094 /* be sure that ignore dir is not a part of dir like:
1095 ignore dir is "h", dir is "home" */
1096 if (dir[ilen] == '\0' || dir[ilen] == PATH_SEP)
1097 return TRUE;
1099 break;
1100 case 1: /* dir is absolute, ignore_dir is relative */
1102 char *d;
1104 d = strstr (dir, *ignore_dir);
1105 if (d != NULL && d[-1] == PATH_SEP && (d[ilen] == '\0' || d[ilen] == PATH_SEP))
1106 return TRUE;
1108 break;
1109 case 2: /* dir is relative, ignore_dir is absolute */
1110 /* FIXME: skip this case */
1111 break;
1112 default: /* this cannot occurs */
1113 return FALSE;
1118 return FALSE;
1121 /* --------------------------------------------------------------------------------------------- */
1123 static void
1124 find_rotate_dash (const Dlg_head * h, gboolean finish)
1126 static const char rotating_dash[] = "|/-\\";
1127 static unsigned int pos = 0;
1129 if (verbose)
1131 pos = (pos + 1) % 4;
1132 tty_setcolor (h->color[DLG_COLOR_NORMAL]);
1133 widget_move (h, FIND2_Y - 7, FIND2_X - 4);
1134 tty_print_char (finish ? ' ' : rotating_dash[pos]);
1135 mc_refresh ();
1139 /* --------------------------------------------------------------------------------------------- */
1141 static int
1142 do_search (Dlg_head * h)
1144 static struct dirent *dp = NULL;
1145 static DIR *dirp = NULL;
1146 static char *directory = NULL;
1147 struct stat tmp_stat;
1148 static int subdirs_left = 0;
1149 gsize bytes_found;
1150 unsigned short count;
1152 if (h == NULL)
1153 { /* someone forces me to close dirp */
1154 if (dirp != NULL)
1156 mc_closedir (dirp);
1157 dirp = NULL;
1159 g_free (directory);
1160 directory = NULL;
1161 dp = NULL;
1162 return 1;
1165 for (count = 0; count < 32; count++)
1167 while (dp == NULL)
1169 if (dirp != NULL)
1171 mc_closedir (dirp);
1172 dirp = NULL;
1175 while (dirp == NULL)
1177 vfs_path_t *tmp_vpath = NULL;
1179 tty_setcolor (REVERSE_COLOR);
1181 while (TRUE)
1183 tmp_vpath = pop_directory ();
1184 if (tmp_vpath == NULL)
1186 running = FALSE;
1187 if (ignore_count == 0)
1188 status_update (_("Finished"));
1189 else
1191 char msg[BUF_SMALL];
1192 g_snprintf (msg, sizeof (msg),
1193 ngettext ("Finished (ignored %zd directory)",
1194 "Finished (ignored %zd directories)",
1195 ignore_count), ignore_count);
1196 status_update (msg);
1198 find_rotate_dash (h, TRUE);
1199 stop_idle (h);
1200 return 0;
1203 /* handle absolute ignore dirs here */
1205 char *tmp;
1206 gboolean ok;
1208 tmp = vfs_path_to_str (tmp_vpath);
1209 ok = find_ignore_dir_search (tmp);
1210 g_free (tmp);
1211 if (!ok)
1212 break;
1215 vfs_path_free (tmp_vpath);
1216 ignore_count++;
1219 g_free (directory);
1220 directory = vfs_path_to_str (tmp_vpath);
1222 if (verbose)
1224 char buffer[BUF_SMALL];
1226 g_snprintf (buffer, sizeof (buffer), _("Searching %s"),
1227 str_trunc (directory, FIND2_X_USE));
1228 status_update (buffer);
1230 /* mc_stat should not be called after mc_opendir
1231 because vfs_s_opendir modifies the st_nlink
1233 if (mc_stat (tmp_vpath, &tmp_stat) == 0)
1234 subdirs_left = tmp_stat.st_nlink - 2;
1235 else
1236 subdirs_left = 0;
1238 dirp = mc_opendir (tmp_vpath);
1239 vfs_path_free (tmp_vpath);
1240 } /* while (!dirp) */
1242 /* skip invalid filenames */
1243 while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1245 } /* while (!dp) */
1247 if (strcmp (dp->d_name, ".") == 0 || strcmp (dp->d_name, "..") == 0)
1249 /* skip invalid filenames */
1250 while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1253 return 1;
1256 if (!(options.skip_hidden && (dp->d_name[0] == '.')))
1258 gboolean search_ok;
1260 if ((subdirs_left != 0) && options.find_recurs && (directory != NULL))
1261 { /* Can directory be NULL ? */
1262 /* handle relative ignore dirs here */
1263 if (options.ignore_dirs_enable && find_ignore_dir_search (dp->d_name))
1264 ignore_count++;
1265 else
1267 vfs_path_t *tmp_vpath;
1269 tmp_vpath = vfs_path_build_filename (directory, dp->d_name, (char *) NULL);
1271 if (mc_lstat (tmp_vpath, &tmp_stat) == 0 && S_ISDIR (tmp_stat.st_mode))
1273 push_directory (tmp_vpath);
1274 subdirs_left--;
1276 else
1277 vfs_path_free (tmp_vpath);
1281 search_ok = mc_search_run (search_file_handle, dp->d_name,
1282 0, strlen (dp->d_name), &bytes_found);
1284 if (search_ok)
1286 if (content_pattern == NULL)
1287 find_add_match (directory, dp->d_name);
1288 else if (search_content (h, directory, dp->d_name))
1289 return 1;
1293 /* skip invalid filenames */
1294 while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1296 } /* for */
1298 find_rotate_dash (h, FALSE);
1300 return 1;
1303 /* --------------------------------------------------------------------------------------------- */
1305 static void
1306 init_find_vars (void)
1308 g_free (old_dir);
1309 old_dir = NULL;
1310 matches = 0;
1311 ignore_count = 0;
1313 /* Remove all the items from the stack */
1314 clear_stack ();
1316 g_strfreev (find_ignore_dirs);
1317 find_ignore_dirs = NULL;
1320 /* --------------------------------------------------------------------------------------------- */
1322 static void
1323 find_do_view_edit (int unparsed_view, int edit, char *dir, char *file)
1325 char *fullname = NULL;
1326 const char *filename = NULL;
1327 int line;
1328 vfs_path_t *fullname_vpath;
1330 if (content_pattern != NULL)
1332 filename = strchr (file + 4, ':') + 1;
1333 line = atoi (file + 4);
1335 else
1337 filename = file + 4;
1338 line = 0;
1341 fullname_vpath = vfs_path_build_filename (dir, filename, (char *) NULL);
1342 if (edit)
1343 do_edit_at_line (fullname_vpath, use_internal_edit, line);
1344 else
1345 view_file_at_line (fullname_vpath, unparsed_view, use_internal_view, line);
1346 vfs_path_free (fullname_vpath);
1347 g_free (fullname);
1350 /* --------------------------------------------------------------------------------------------- */
1352 static cb_ret_t
1353 view_edit_currently_selected_file (int unparsed_view, int edit)
1355 char *dir = NULL;
1356 char *text = NULL;
1358 listbox_get_current (find_list, &text, (void **) &dir);
1360 if ((text == NULL) || (dir == NULL))
1361 return MSG_NOT_HANDLED;
1363 find_do_view_edit (unparsed_view, edit, dir, text);
1364 return MSG_HANDLED;
1367 /* --------------------------------------------------------------------------------------------- */
1369 static cb_ret_t
1370 find_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
1372 switch (msg)
1374 case DLG_KEY:
1375 if (parm == KEY_F (3) || parm == KEY_F (13))
1377 int unparsed_view = (parm == KEY_F (13));
1378 return view_edit_currently_selected_file (unparsed_view, 0);
1380 if (parm == KEY_F (4))
1382 return view_edit_currently_selected_file (0, 1);
1384 return MSG_NOT_HANDLED;
1386 case DLG_IDLE:
1387 do_search (h);
1388 return MSG_HANDLED;
1390 default:
1391 return default_dlg_callback (h, sender, msg, parm, data);
1395 /* --------------------------------------------------------------------------------------------- */
1396 /** Handles the Stop/Start button in the find window */
1398 static int
1399 start_stop (WButton * button, int action)
1401 (void) button;
1402 (void) action;
1404 running = is_start;
1405 set_idle_proc (find_dlg, running);
1406 is_start = !is_start;
1408 status_update (is_start ? _("Stopped") : _("Searching"));
1409 button_set_text (stop_button, fbuts[is_start ? 1 : 0].text);
1411 return 0;
1414 /* --------------------------------------------------------------------------------------------- */
1415 /** Handle view command, when invoked as a button */
1417 static int
1418 find_do_view_file (WButton * button, int action)
1420 (void) button;
1421 (void) action;
1423 view_edit_currently_selected_file (0, 0);
1424 return 0;
1427 /* --------------------------------------------------------------------------------------------- */
1428 /** Handle edit command, when invoked as a button */
1430 static int
1431 find_do_edit_file (WButton * button, int action)
1433 (void) button;
1434 (void) action;
1436 view_edit_currently_selected_file (0, 1);
1437 return 0;
1440 /* --------------------------------------------------------------------------------------------- */
1442 static void
1443 setup_gui (void)
1445 #ifdef ENABLE_NLS
1446 static gboolean i18n_flag = FALSE;
1448 if (!i18n_flag)
1450 int i = sizeof (fbuts) / sizeof (fbuts[0]);
1451 while (i-- != 0)
1453 fbuts[i].text = _(fbuts[i].text);
1454 fbuts[i].len = str_term_width1 (fbuts[i].text) + 3;
1457 fbuts[2].len += 2; /* DEFPUSH_BUTTON */
1458 i18n_flag = TRUE;
1460 #endif /* ENABLE_NLS */
1463 * Dynamically place buttons centered within current window size
1466 int l0 = max (fbuts[0].len, fbuts[1].len);
1467 int l1 = fbuts[2].len + fbuts[3].len + l0 + fbuts[4].len;
1468 int l2 = fbuts[5].len + fbuts[6].len + fbuts[7].len;
1469 int r1, r2;
1471 /* Check, if both button rows fit within FIND2_X */
1472 FIND2_X = max (l1 + 9, COLS - 16);
1473 FIND2_X = max (l2 + 8, FIND2_X);
1475 /* compute amount of space between buttons for each row */
1476 r1 = (FIND2_X - 4 - l1) % 5;
1477 l1 = (FIND2_X - 4 - l1) / 5;
1478 r2 = (FIND2_X - 4 - l2) % 4;
1479 l2 = (FIND2_X - 4 - l2) / 4;
1481 /* ...and finally, place buttons */
1482 fbuts[2].x = 2 + r1 / 2 + l1;
1483 fbuts[3].x = fbuts[2].x + fbuts[2].len + l1;
1484 fbuts[0].x = fbuts[3].x + fbuts[3].len + l1;
1485 fbuts[4].x = fbuts[0].x + l0 + l1;
1486 fbuts[5].x = 2 + r2 / 2 + l2;
1487 fbuts[6].x = fbuts[5].x + fbuts[5].len + l2;
1488 fbuts[7].x = fbuts[6].x + fbuts[6].len + l2;
1491 find_dlg =
1492 create_dlg (TRUE, 0, 0, FIND2_Y, FIND2_X, dialog_colors, find_callback, NULL,
1493 "[Find File]", _("Find File"), DLG_CENTER | DLG_REVERSE);
1495 add_widget (find_dlg,
1496 button_new (FIND2_Y - 3, fbuts[7].x, B_VIEW, NORMAL_BUTTON,
1497 fbuts[7].text, find_do_edit_file));
1498 add_widget (find_dlg,
1499 button_new (FIND2_Y - 3, fbuts[6].x, B_VIEW, NORMAL_BUTTON,
1500 fbuts[6].text, find_do_view_file));
1501 add_widget (find_dlg,
1502 button_new (FIND2_Y - 3, fbuts[5].x, B_PANELIZE, NORMAL_BUTTON, fbuts[5].text,
1503 NULL));
1505 add_widget (find_dlg,
1506 button_new (FIND2_Y - 4, fbuts[4].x, B_CANCEL, NORMAL_BUTTON, fbuts[4].text, NULL));
1507 stop_button =
1508 button_new (FIND2_Y - 4, fbuts[0].x, B_STOP, NORMAL_BUTTON, fbuts[0].text, start_stop);
1509 add_widget (find_dlg, stop_button);
1510 add_widget (find_dlg,
1511 button_new (FIND2_Y - 4, fbuts[3].x, B_AGAIN, NORMAL_BUTTON, fbuts[3].text, NULL));
1512 add_widget (find_dlg,
1513 button_new (FIND2_Y - 4, fbuts[2].x, B_ENTER, DEFPUSH_BUTTON, fbuts[2].text, NULL));
1515 status_label = label_new (FIND2_Y - 7, 4, _("Searching"));
1516 add_widget (find_dlg, status_label);
1518 found_num_label = label_new (FIND2_Y - 6, 4, "");
1519 add_widget (find_dlg, found_num_label);
1521 find_list = listbox_new (2, 2, FIND2_Y - 10, FIND2_X - 4, FALSE, NULL);
1522 add_widget (find_dlg, find_list);
1525 /* --------------------------------------------------------------------------------------------- */
1527 static int
1528 run_process (void)
1530 int ret;
1532 search_content_handle = mc_search_new (content_pattern, -1);
1533 if (search_content_handle)
1535 search_content_handle->search_type =
1536 options.content_regexp ? MC_SEARCH_T_REGEX : MC_SEARCH_T_NORMAL;
1537 search_content_handle->is_case_sensitive = options.content_case_sens;
1538 search_content_handle->whole_words = options.content_whole_words;
1539 search_content_handle->is_all_charsets = options.content_all_charsets;
1541 search_file_handle = mc_search_new (find_pattern, -1);
1542 search_file_handle->search_type = options.file_pattern ? MC_SEARCH_T_GLOB : MC_SEARCH_T_REGEX;
1543 search_file_handle->is_case_sensitive = options.file_case_sens;
1544 search_file_handle->is_all_charsets = options.file_all_charsets;
1545 search_file_handle->is_entire_line = options.file_pattern;
1547 resuming = 0;
1549 set_idle_proc (find_dlg, 1);
1550 ret = run_dlg (find_dlg);
1552 mc_search_free (search_file_handle);
1553 search_file_handle = NULL;
1554 mc_search_free (search_content_handle);
1555 search_content_handle = NULL;
1557 return ret;
1560 /* --------------------------------------------------------------------------------------------- */
1562 static void
1563 kill_gui (void)
1565 set_idle_proc (find_dlg, 0);
1566 destroy_dlg (find_dlg);
1569 /* --------------------------------------------------------------------------------------------- */
1571 static int
1572 do_find (const char *start_dir, ssize_t start_dir_len, const char *ignore_dirs,
1573 const char *pattern, const char *content, char **dirname, char **filename)
1575 int return_value = 0;
1576 char *dir_tmp = NULL, *file_tmp = NULL;
1578 setup_gui ();
1580 /* FIXME: Need to cleanup this, this ought to be passed non-globaly */
1581 find_pattern = (char *) pattern;
1583 content_pattern = NULL;
1584 if (options.content_use && content != NULL && str_is_valid_string (content))
1585 content_pattern = g_strdup (content);
1587 init_find_vars ();
1588 parse_ignore_dirs (ignore_dirs);
1589 push_directory (vfs_path_from_str (start_dir));
1591 return_value = run_process ();
1593 /* Clear variables */
1594 init_find_vars ();
1596 get_list_info (&file_tmp, &dir_tmp);
1598 if (dir_tmp)
1599 *dirname = g_strdup (dir_tmp);
1600 if (file_tmp)
1601 *filename = g_strdup (file_tmp);
1603 if (return_value == B_PANELIZE && *filename)
1605 int status, link_to_dir, stale_link;
1606 int next_free = 0;
1607 int i;
1608 struct stat st;
1609 GList *entry;
1610 dir_list *list = &current_panel->dir;
1611 char *name = NULL;
1613 if (set_zero_dir (list))
1614 next_free++;
1616 for (i = 0, entry = find_list->list; entry != NULL; i++, entry = g_list_next (entry))
1618 const char *lc_filename = NULL;
1619 WLEntry *le = (WLEntry *) entry->data;
1620 char *p;
1622 if ((le->text == NULL) || (le->data == NULL))
1623 continue;
1625 if (content_pattern != NULL)
1626 lc_filename = strchr (le->text + 4, ':') + 1;
1627 else
1628 lc_filename = le->text + 4;
1630 name = mc_build_filename (le->data, lc_filename, (char *) NULL);
1631 /* skip initial start dir */
1632 if (start_dir_len < 0)
1633 p = name;
1634 else
1636 p = name + (size_t) start_dir_len;
1637 if (*p == PATH_SEP)
1638 p++;
1641 status = handle_path (list, p, &st, next_free, &link_to_dir, &stale_link);
1642 if (status == 0)
1644 g_free (name);
1645 continue;
1647 if (status == -1)
1649 g_free (name);
1650 break;
1653 /* don't add files more than once to the panel */
1654 if (content_pattern != NULL && next_free > 0
1655 && strcmp (list->list[next_free - 1].fname, p) == 0)
1657 g_free (name);
1658 continue;
1661 if (next_free == 0) /* first turn i.e clean old list */
1662 panel_clean_dir (current_panel);
1663 list->list[next_free].fnamelen = strlen (p);
1664 list->list[next_free].fname = g_strndup (p, list->list[next_free].fnamelen);
1665 list->list[next_free].f.marked = 0;
1666 list->list[next_free].f.link_to_dir = link_to_dir;
1667 list->list[next_free].f.stale_link = stale_link;
1668 list->list[next_free].f.dir_size_computed = 0;
1669 list->list[next_free].st = st;
1670 list->list[next_free].sort_key = NULL;
1671 list->list[next_free].second_sort_key = NULL;
1672 next_free++;
1673 g_free (name);
1674 if (!(next_free & 15))
1675 rotate_dash ();
1678 if (next_free)
1680 current_panel->count = next_free;
1681 current_panel->is_panelized = TRUE;
1683 /* absolute path */
1684 if (start_dir_len < 0)
1686 int ret;
1687 vfs_path_free (current_panel->cwd_vpath);
1688 current_panel->cwd_vpath = vfs_path_from_str (PATH_SEP_STR);
1689 ret = chdir (PATH_SEP_STR);
1691 panelize_save_panel (current_panel);
1695 g_free (content_pattern);
1696 kill_gui ();
1697 do_search (NULL); /* force do_search to release resources */
1698 g_free (old_dir);
1699 old_dir = NULL;
1701 return return_value;
1704 /* --------------------------------------------------------------------------------------------- */
1705 /*** public functions ****************************************************************************/
1706 /* --------------------------------------------------------------------------------------------- */
1708 void
1709 find_file (void)
1711 char *start_dir = NULL, *pattern = NULL, *content = NULL, *ignore_dirs = NULL;
1712 ssize_t start_dir_len;
1713 char *filename = NULL, *dirname = NULL;
1714 int v;
1716 while (find_parameters (&start_dir, &start_dir_len, &ignore_dirs, &pattern, &content))
1718 if (pattern[0] == '\0')
1719 break; /* nothing search */
1721 dirname = filename = NULL;
1722 is_start = FALSE;
1723 v = do_find (start_dir, start_dir_len, ignore_dirs, pattern, content, &dirname, &filename);
1724 g_free (ignore_dirs);
1725 g_free (pattern);
1727 if (v == B_ENTER)
1729 if (dirname != NULL)
1731 vfs_path_t *dirname_vpath;
1733 dirname_vpath = vfs_path_from_str (dirname);
1734 do_cd (dirname_vpath, cd_exact);
1735 vfs_path_free (dirname_vpath);
1736 if (filename != NULL)
1737 try_to_select (current_panel,
1738 filename + (content != NULL
1739 ? strchr (filename + 4, ':') - filename + 1 : 4));
1741 else if (filename != NULL)
1743 vfs_path_t *filename_vpath;
1745 filename_vpath = vfs_path_from_str (filename);
1746 do_cd (filename_vpath, cd_exact);
1747 vfs_path_free (filename_vpath);
1750 g_free (dirname);
1751 g_free (filename);
1752 break;
1755 g_free (content);
1756 g_free (dirname);
1757 g_free (filename);
1759 if (v == B_CANCEL)
1760 break;
1762 if (v == B_PANELIZE)
1764 panel_re_sort (current_panel);
1765 try_to_select (current_panel, NULL);
1766 break;
1771 /* --------------------------------------------------------------------------------------------- */