(widget_set_options): new function to set/reset widget options.
[midnight-commander.git] / src / filemanager / find.c
blob75006d703244cfe528915d80418c65d8c613f325
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 (content_label), disable);
409 widget_disable (WIDGET (in_with), disable);
410 widget_disable (WIDGET (content_first_hit_cbox), disable);
411 widget_disable (WIDGET (content_regexp_cbox), disable);
412 widget_disable (WIDGET (content_case_sens_cbox), disable);
413 #ifdef HAVE_CHARSET
414 widget_disable (WIDGET (content_all_charsets_cbox), disable);
415 #endif
416 widget_disable (WIDGET (content_whole_words_cbox), disable);
418 return MSG_HANDLED;
421 if (sender == WIDGET (ignore_dirs_cbox))
423 gboolean disable = !(ignore_dirs_cbox->state & C_BOOL);
425 widget_disable (WIDGET (in_ignore), disable);
427 return MSG_HANDLED;
430 return MSG_NOT_HANDLED;
433 case DLG_VALIDATE:
434 if (h->ret_value != B_ENTER)
435 return MSG_HANDLED;
437 /* check filename regexp */
438 if (!(file_pattern_cbox->state & C_BOOL)
439 && (in_name->buffer[0] != '\0') && !find_check_regexp (in_name->buffer))
441 h->state = DLG_ACTIVE; /* Don't stop the dialog */
442 message (D_ERROR, MSG_ERROR, _("Malformed regular expression"));
443 dlg_select_widget (in_name);
444 return MSG_HANDLED;
447 /* check content regexp */
448 if ((content_use_cbox->state & C_BOOL) && (content_regexp_cbox->state & C_BOOL)
449 && (in_with->buffer[0] != '\0') && !find_check_regexp (in_with->buffer))
451 h->state = DLG_ACTIVE; /* Don't stop the dialog */
452 message (D_ERROR, MSG_ERROR, _("Malformed regular expression"));
453 dlg_select_widget (in_with);
454 return MSG_HANDLED;
457 return MSG_HANDLED;
459 default:
460 return default_dlg_callback (h, sender, msg, parm, data);
464 /* --------------------------------------------------------------------------------------------- */
466 * find_parameters: gets information from the user
468 * If the return value is TRUE, then the following holds:
470 * start_dir, ignore_dirs, pattern and content contain the information provided by the user.
471 * They are newly allocated strings and must be freed when uneeded.
473 * start_dir_len is -1 when user entered an absolute path, otherwise it is a length
474 * of start_dir (which is absolute). It is used to get a relative pats of find results.
477 static gboolean
478 find_parameters (char **start_dir, ssize_t * start_dir_len,
479 char **ignore_dirs, char **pattern, char **content)
481 gboolean return_value;
483 /* file name */
484 const char *file_case_label = N_("Cas&e sensitive");
485 const char *file_pattern_label = N_("&Using shell patterns");
486 const char *file_recurs_label = N_("&Find recursively");
487 const char *file_skip_hidden_label = N_("S&kip hidden");
488 #ifdef HAVE_CHARSET
489 const char *file_all_charsets_label = N_("&All charsets");
490 #endif
492 /* file content */
493 const char *content_use_label = N_("Sea&rch for content");
494 const char *content_case_label = N_("Case sens&itive");
495 const char *content_regexp_label = N_("Re&gular expression");
496 const char *content_first_hit_label = N_("Fir&st hit");
497 const char *content_whole_words_label = N_("&Whole words");
498 #ifdef HAVE_CHARSET
499 const char *content_all_charsets_label = N_("A&ll charsets");
500 #endif
502 const char *buts[] = { N_("&OK"), N_("&Cancel"), N_("&Tree") };
504 int b0, b1, b2;
506 int cbox_position;
507 gboolean disable;
509 #ifdef ENABLE_NLS
511 int i = sizeof (buts) / sizeof (buts[0]);
512 while (i-- != 0)
513 buts[i] = _(buts[i]);
515 file_case_label = _(file_case_label);
516 file_pattern_label = _(file_pattern_label);
517 file_recurs_label = _(file_recurs_label);
518 file_skip_hidden_label = _(file_skip_hidden_label);
519 #ifdef HAVE_CHARSET
520 file_all_charsets_label = _(file_all_charsets_label);
521 content_all_charsets_label = _(content_all_charsets_label);
522 #endif
523 content_use_label = _(content_use_label);
524 content_case_label = _(content_case_label);
525 content_regexp_label = _(content_regexp_label);
526 content_first_hit_label = _(content_first_hit_label);
527 content_whole_words_label = _(content_whole_words_label);
529 #endif /* ENABLE_NLS */
531 b0 = str_term_width1 (buts[0]) + 6; /* default button */
532 b1 = str_term_width1 (buts[1]) + 4;
533 b2 = str_term_width1 (buts[2]) + 4;
535 find_load_options ();
537 if (in_start_dir == NULL)
538 in_start_dir = g_strdup (".");
540 disable = !options.content_use;
542 find_dlg =
543 create_dlg (TRUE, 0, 0, FIND_Y, FIND_X, dialog_colors,
544 find_parm_callback, NULL, "[Find File]", _("Find File"),
545 DLG_CENTER | DLG_REVERSE);
547 add_widget (find_dlg,
548 button_new (FIND_Y - 3, FIND_X * 3 / 4 - b1 / 2, B_CANCEL, NORMAL_BUTTON, buts[1],
549 0));
550 add_widget (find_dlg,
551 button_new (FIND_Y - 3, FIND_X / 4 - b0 / 2, B_ENTER, DEFPUSH_BUTTON, buts[0], 0));
553 cbox_position = FIND_Y - 5;
555 content_first_hit_cbox =
556 check_new (cbox_position--, FIND_X / 2 + 1, options.content_first_hit,
557 content_first_hit_label);
558 widget_disable (WIDGET (content_first_hit_cbox), disable);
559 add_widget (find_dlg, content_first_hit_cbox);
561 content_whole_words_cbox =
562 check_new (cbox_position--, FIND_X / 2 + 1, options.content_whole_words,
563 content_whole_words_label);
564 widget_disable (WIDGET (content_whole_words_cbox), disable);
565 add_widget (find_dlg, content_whole_words_cbox);
567 #ifdef HAVE_CHARSET
568 content_all_charsets_cbox = check_new (cbox_position--, FIND_X / 2 + 1,
569 options.content_all_charsets,
570 content_all_charsets_label);
571 widget_disable (WIDGET (content_all_charsets_cbox), disable);
572 add_widget (find_dlg, content_all_charsets_cbox);
573 #endif
575 content_case_sens_cbox =
576 check_new (cbox_position--, FIND_X / 2 + 1, options.content_case_sens, content_case_label);
577 widget_disable (WIDGET (content_case_sens_cbox), disable);
578 add_widget (find_dlg, content_case_sens_cbox);
580 content_regexp_cbox =
581 check_new (cbox_position--, FIND_X / 2 + 1, options.content_regexp, content_regexp_label);
582 widget_disable (WIDGET (content_regexp_cbox), disable);
583 add_widget (find_dlg, content_regexp_cbox);
585 cbox_position = FIND_Y - 6;
587 skip_hidden_cbox = check_new (cbox_position--, 3, options.skip_hidden, file_skip_hidden_label);
588 add_widget (find_dlg, skip_hidden_cbox);
590 #ifdef HAVE_CHARSET
591 file_all_charsets_cbox =
592 check_new (cbox_position--, 3, options.file_all_charsets, file_all_charsets_label);
593 add_widget (find_dlg, file_all_charsets_cbox);
594 #endif
596 file_case_sens_cbox = check_new (cbox_position--, 3, options.file_case_sens, file_case_label);
597 add_widget (find_dlg, file_case_sens_cbox);
599 file_pattern_cbox = check_new (cbox_position--, 3, options.file_pattern, file_pattern_label);
600 add_widget (find_dlg, file_pattern_cbox);
602 recursively_cbox = check_new (cbox_position, 3, options.find_recurs, file_recurs_label);
603 add_widget (find_dlg, recursively_cbox);
605 /* This checkbox is located in the second column */
606 content_use_cbox =
607 check_new (cbox_position, FIND_X / 2 + 1, options.content_use, content_use_label);
608 add_widget (find_dlg, content_use_cbox);
610 in_with =
611 input_new (8, FIND_X / 2 + 1, input_get_default_colors (), FIND_X / 2 - 4, INPUT_LAST_TEXT,
612 MC_HISTORY_SHARED_SEARCH, INPUT_COMPLETE_DEFAULT);
613 widget_disable (WIDGET (in_with), disable);
614 add_widget (find_dlg, in_with);
616 content_label = label_new (7, FIND_X / 2 + 1, _("Content:"));
617 widget_disable (WIDGET (content_label), disable);
618 add_widget (find_dlg, content_label);
620 in_name = input_new (8, 3, input_get_default_colors (),
621 FIND_X / 2 - 4, INPUT_LAST_TEXT, "name", INPUT_COMPLETE_DEFAULT);
622 add_widget (find_dlg, in_name);
623 add_widget (find_dlg, label_new (7, 3, _("File name:")));
625 in_ignore = input_new (5, 3, input_get_default_colors (), FIND_X - 6,
626 options.ignore_dirs != NULL ? options.ignore_dirs : "",
627 "ignoredirs", INPUT_COMPLETE_DEFAULT);
628 widget_disable (WIDGET (in_ignore), !options.ignore_dirs_enable);
629 add_widget (find_dlg, in_ignore);
631 ignore_dirs_cbox =
632 check_new (4, 3, options.ignore_dirs_enable, _("Ena&ble ignore directories:"));
633 add_widget (find_dlg, ignore_dirs_cbox);
635 add_widget (find_dlg, button_new (3, FIND_X - b2 - 2, B_TREE, NORMAL_BUTTON, buts[2], 0));
637 in_start = input_new (3, 3, input_get_default_colors (),
638 FIND_X - b2 - 6, in_start_dir, "start", INPUT_COMPLETE_DEFAULT);
639 add_widget (find_dlg, in_start);
640 add_widget (find_dlg, label_new (2, 3, _("Start at:")));
642 find_par_start:
643 dlg_select_widget (in_name);
645 switch (run_dlg (find_dlg))
647 case B_CANCEL:
648 return_value = FALSE;
649 break;
651 case B_TREE:
653 char *temp_dir;
655 temp_dir = in_start->buffer;
656 if ((temp_dir[0] == '\0') || ((temp_dir[0] == '.') && (temp_dir[1] == '\0')))
657 temp_dir = vfs_path_to_str (current_panel->cwd_vpath);
658 else
659 temp_dir = g_strdup (temp_dir);
661 if (in_start_dir != INPUT_LAST_TEXT)
662 g_free (in_start_dir);
663 in_start_dir = tree_box (temp_dir);
664 if (in_start_dir == NULL)
665 in_start_dir = temp_dir;
666 else
667 g_free (temp_dir);
669 input_assign_text (in_start, in_start_dir);
671 /* Warning: Dreadful goto */
672 goto find_par_start;
675 default:
677 char *s;
679 #ifdef HAVE_CHARSET
680 options.file_all_charsets = file_all_charsets_cbox->state & C_BOOL;
681 options.content_all_charsets = content_all_charsets_cbox->state & C_BOOL;
682 #endif
683 options.content_use = content_use_cbox->state & C_BOOL;
684 options.content_case_sens = content_case_sens_cbox->state & C_BOOL;
685 options.content_regexp = content_regexp_cbox->state & C_BOOL;
686 options.content_first_hit = content_first_hit_cbox->state & C_BOOL;
687 options.content_whole_words = content_whole_words_cbox->state & C_BOOL;
688 options.find_recurs = recursively_cbox->state & C_BOOL;
689 options.file_pattern = file_pattern_cbox->state & C_BOOL;
690 options.file_case_sens = file_case_sens_cbox->state & C_BOOL;
691 options.skip_hidden = skip_hidden_cbox->state & C_BOOL;
692 options.ignore_dirs_enable = ignore_dirs_cbox->state & C_BOOL;
693 g_free (options.ignore_dirs);
694 options.ignore_dirs = g_strdup (in_ignore->buffer);
696 *content = (options.content_use && in_with->buffer[0] != '\0')
697 ? g_strdup (in_with->buffer) : NULL;
698 *start_dir = in_start->buffer[0] != '\0' ? in_start->buffer : (char *) ".";
699 *pattern = g_strdup (in_name->buffer);
700 if (in_start_dir != INPUT_LAST_TEXT)
701 g_free (in_start_dir);
702 in_start_dir = g_strdup (*start_dir);
704 s = tilde_expand (*start_dir);
705 canonicalize_pathname (s);
707 if (s[0] == '.' && s[1] == '\0')
709 *start_dir = vfs_path_to_str (current_panel->cwd_vpath);
710 /* FIXME: is current_panel->cwd_vpath canonicalized? */
711 /* relative paths will be used in panelization */
712 *start_dir_len = (ssize_t) strlen (*start_dir);
713 g_free (s);
715 else if (g_path_is_absolute (s))
717 *start_dir = s;
718 *start_dir_len = -1;
720 else
722 /* relative paths will be used in panelization */
723 char *cwd_str;
725 cwd_str = vfs_path_to_str (current_panel->cwd_vpath);
726 *start_dir = mc_build_filename (cwd_str, s, (char *) NULL);
727 *start_dir_len = (ssize_t) strlen (cwd_str);
728 g_free (cwd_str);
729 g_free (s);
732 if (!options.ignore_dirs_enable || in_ignore->buffer[0] == '\0'
733 || (in_ignore->buffer[0] == '.' && in_ignore->buffer[1] == '\0'))
734 *ignore_dirs = NULL;
735 else
736 *ignore_dirs = g_strdup (in_ignore->buffer);
738 find_save_options ();
740 return_value = TRUE;
744 destroy_dlg (find_dlg);
746 return return_value;
749 /* --------------------------------------------------------------------------------------------- */
751 #if GLIB_CHECK_VERSION (2, 14, 0)
752 static inline void
753 push_directory (const vfs_path_t * dir)
755 g_queue_push_head (&dir_queue, (void *) dir);
758 /* --------------------------------------------------------------------------------------------- */
760 static inline vfs_path_t *
761 pop_directory (void)
763 return (vfs_path_t *) g_queue_pop_tail (&dir_queue);
766 /* --------------------------------------------------------------------------------------------- */
767 /** Remove all the items from the stack */
769 static void
770 clear_stack (void)
772 g_queue_foreach (&dir_queue, (GFunc) vfs_path_free, NULL);
773 g_queue_clear (&dir_queue);
776 /* --------------------------------------------------------------------------------------------- */
778 #else /* GLIB_CHECK_VERSION */
779 static void
780 push_directory (const vfs_path_t * dir)
782 dir_stack *new;
784 new = g_new (dir_stack, 1);
785 new->name = (vfs_path_t *) dir;
786 new->prev = dir_stack_base;
787 dir_stack_base = new;
790 /* --------------------------------------------------------------------------------------------- */
792 static vfs_path_t *
793 pop_directory (void)
795 vfs_path_t *name = NULL;
797 if (dir_stack_base != NULL)
799 dir_stack *next;
800 name = dir_stack_base->name;
801 next = dir_stack_base->prev;
802 g_free (dir_stack_base);
803 dir_stack_base = next;
806 return name;
809 /* --------------------------------------------------------------------------------------------- */
810 /** Remove all the items from the stack */
812 static void
813 clear_stack (void)
815 vfs_path_t *dir = NULL;
817 while ((dir = pop_directory ()) != NULL)
818 vfs_path_free (dir);
820 #endif /* GLIB_CHECK_VERSION */
822 /* --------------------------------------------------------------------------------------------- */
824 static void
825 insert_file (const char *dir, const char *file)
827 char *tmp_name = NULL;
828 static char *dirname = NULL;
830 while (dir[0] == PATH_SEP && dir[1] == PATH_SEP)
831 dir++;
833 if (old_dir)
835 if (strcmp (old_dir, dir))
837 g_free (old_dir);
838 old_dir = g_strdup (dir);
839 dirname = add_to_list (dir, NULL);
842 else
844 old_dir = g_strdup (dir);
845 dirname = add_to_list (dir, NULL);
848 tmp_name = g_strdup_printf (" %s", file);
849 add_to_list (tmp_name, dirname);
850 g_free (tmp_name);
853 /* --------------------------------------------------------------------------------------------- */
855 static void
856 find_add_match (const char *dir, const char *file)
858 insert_file (dir, file);
860 /* Don't scroll */
861 if (matches == 0)
862 listbox_select_first (find_list);
863 send_message (WIDGET (find_list), NULL, WIDGET_DRAW, 0, NULL);
865 matches++;
866 found_num_update ();
869 /* --------------------------------------------------------------------------------------------- */
871 * get_line_at:
873 * Returns malloced null-terminated line from file file_fd.
874 * Input is buffered in buf_size long buffer.
875 * Current pos in buf is stored in pos.
876 * n_read - number of read chars.
877 * has_newline - is there newline ?
880 static char *
881 get_line_at (int file_fd, char *buf, int buf_size, int *pos, int *n_read, gboolean * has_newline)
883 char *buffer = NULL;
884 int buffer_size = 0;
885 char ch = 0;
886 int i = 0;
888 while (TRUE)
890 if (*pos >= *n_read)
892 *pos = 0;
893 *n_read = mc_read (file_fd, buf, buf_size);
894 if (*n_read <= 0)
895 break;
898 ch = buf[(*pos)++];
899 if (ch == '\0')
901 /* skip possible leading zero(s) */
902 if (i == 0)
903 continue;
904 break;
907 if (i >= buffer_size - 1)
908 buffer = g_realloc (buffer, buffer_size += 80);
910 /* Strip newline */
911 if (ch == '\n')
912 break;
914 buffer[i++] = ch;
917 *has_newline = (ch != '\0');
919 if (buffer != NULL)
920 buffer[i] = '\0';
922 return buffer;
925 /* --------------------------------------------------------------------------------------------- */
927 static FindProgressStatus
928 check_find_events (Dlg_head * h)
930 Gpm_Event event;
931 int c;
933 event.x = -1;
934 c = tty_get_event (&event, h->mouse_status == MOU_REPEAT, FALSE);
935 if (c != EV_NONE)
937 dlg_process_event (h, c, &event);
938 if (h->ret_value == B_ENTER
939 || h->ret_value == B_CANCEL || h->ret_value == B_AGAIN || h->ret_value == B_PANELIZE)
941 /* dialog terminated */
942 return FIND_ABORT;
944 if (!(h->flags & DLG_WANT_IDLE))
946 /* searching suspended */
947 return FIND_SUSPEND;
951 return FIND_CONT;
954 /* --------------------------------------------------------------------------------------------- */
956 * search_content:
958 * Search the content_pattern string in the DIRECTORY/FILE.
959 * It will add the found entries to the find listbox.
961 * returns FALSE if do_search should look for another file
962 * TRUE if do_search should exit and proceed to the event handler
965 static gboolean
966 search_content (Dlg_head * h, const char *directory, const char *filename)
968 struct stat s;
969 char buffer[BUF_4K];
970 int file_fd;
971 gboolean ret_val = FALSE;
972 vfs_path_t *vpath;
974 vpath = vfs_path_build_filename (directory, filename, (char *) NULL);
976 if (mc_stat (vpath, &s) != 0 || !S_ISREG (s.st_mode))
978 vfs_path_free (vpath);
979 return FALSE;
982 file_fd = mc_open (vpath, O_RDONLY);
983 vfs_path_free (vpath);
985 if (file_fd == -1)
986 return FALSE;
988 g_snprintf (buffer, sizeof (buffer), _("Grepping in %s"), str_trunc (filename, FIND2_X_USE));
990 status_update (buffer);
991 mc_refresh ();
993 tty_enable_interrupt_key ();
994 tty_got_interrupt ();
997 int line = 1;
998 int pos = 0;
999 int n_read = 0;
1000 gboolean has_newline;
1001 char *p = NULL;
1002 gboolean found = FALSE;
1003 gsize found_len;
1004 char result[BUF_MEDIUM];
1006 if (resuming)
1008 /* We've been previously suspended, start from the previous position */
1009 resuming = 0;
1010 line = last_line;
1011 pos = last_pos;
1013 while (!ret_val
1014 && (p = get_line_at (file_fd, buffer, sizeof (buffer),
1015 &pos, &n_read, &has_newline)) != NULL)
1017 if (!found /* Search in binary line once */
1018 && mc_search_run (search_content_handle,
1019 (const void *) p, 0, strlen (p), &found_len))
1021 g_snprintf (result, sizeof (result), "%d:%s", line, filename);
1022 find_add_match (directory, result);
1023 found = TRUE;
1025 g_free (p);
1027 if (found && options.content_first_hit)
1028 break;
1030 if (has_newline)
1032 found = FALSE;
1033 line++;
1036 if ((line & 0xff) == 0)
1038 FindProgressStatus res;
1039 res = check_find_events (h);
1040 switch (res)
1042 case FIND_ABORT:
1043 stop_idle (h);
1044 ret_val = TRUE;
1045 break;
1046 case FIND_SUSPEND:
1047 resuming = 1;
1048 last_line = line;
1049 last_pos = pos;
1050 ret_val = TRUE;
1051 break;
1052 default:
1053 break;
1058 tty_disable_interrupt_key ();
1059 mc_close (file_fd);
1060 return ret_val;
1063 /* --------------------------------------------------------------------------------------------- */
1066 If dir is absolute, this means we're within dir and searching file here.
1067 If dir is relative, this means we're going to add dir to the directory stack.
1069 static gboolean
1070 find_ignore_dir_search (const char *dir)
1072 if (find_ignore_dirs != NULL)
1074 const size_t dlen = strlen (dir);
1075 const unsigned char dabs = g_path_is_absolute (dir) ? 1 : 0;
1077 char **ignore_dir;
1079 for (ignore_dir = find_ignore_dirs; *ignore_dir != NULL; ignore_dir++)
1081 const size_t ilen = strlen (*ignore_dir);
1082 const unsigned char iabs = g_path_is_absolute (*ignore_dir) ? 2 : 0;
1084 /* ignore dir is too long -- skip it */
1085 if (dlen < ilen)
1086 continue;
1088 /* handle absolute and relative paths */
1089 switch (iabs | dabs)
1091 case 0: /* both paths are relative */
1092 case 3: /* both paths are abolute */
1093 /* if ignore dir is not a path of dir -- skip it */
1094 if (strncmp (dir, *ignore_dir, ilen) == 0)
1096 /* be sure that ignore dir is not a part of dir like:
1097 ignore dir is "h", dir is "home" */
1098 if (dir[ilen] == '\0' || dir[ilen] == PATH_SEP)
1099 return TRUE;
1101 break;
1102 case 1: /* dir is absolute, ignore_dir is relative */
1104 char *d;
1106 d = strstr (dir, *ignore_dir);
1107 if (d != NULL && d[-1] == PATH_SEP && (d[ilen] == '\0' || d[ilen] == PATH_SEP))
1108 return TRUE;
1110 break;
1111 case 2: /* dir is relative, ignore_dir is absolute */
1112 /* FIXME: skip this case */
1113 break;
1114 default: /* this cannot occurs */
1115 return FALSE;
1120 return FALSE;
1123 /* --------------------------------------------------------------------------------------------- */
1125 static void
1126 find_rotate_dash (const Dlg_head * h, gboolean finish)
1128 static const char rotating_dash[] = "|/-\\";
1129 static unsigned int pos = 0;
1131 if (verbose)
1133 pos = (pos + 1) % 4;
1134 tty_setcolor (h->color[DLG_COLOR_NORMAL]);
1135 widget_move (h, FIND2_Y - 7, FIND2_X - 4);
1136 tty_print_char (finish ? ' ' : rotating_dash[pos]);
1137 mc_refresh ();
1141 /* --------------------------------------------------------------------------------------------- */
1143 static int
1144 do_search (Dlg_head * h)
1146 static struct dirent *dp = NULL;
1147 static DIR *dirp = NULL;
1148 static char *directory = NULL;
1149 struct stat tmp_stat;
1150 static int subdirs_left = 0;
1151 gsize bytes_found;
1152 unsigned short count;
1154 if (h == NULL)
1155 { /* someone forces me to close dirp */
1156 if (dirp != NULL)
1158 mc_closedir (dirp);
1159 dirp = NULL;
1161 g_free (directory);
1162 directory = NULL;
1163 dp = NULL;
1164 return 1;
1167 for (count = 0; count < 32; count++)
1169 while (dp == NULL)
1171 if (dirp != NULL)
1173 mc_closedir (dirp);
1174 dirp = NULL;
1177 while (dirp == NULL)
1179 vfs_path_t *tmp_vpath = NULL;
1181 tty_setcolor (REVERSE_COLOR);
1183 while (TRUE)
1185 tmp_vpath = pop_directory ();
1186 if (tmp_vpath == NULL)
1188 running = FALSE;
1189 if (ignore_count == 0)
1190 status_update (_("Finished"));
1191 else
1193 char msg[BUF_SMALL];
1194 g_snprintf (msg, sizeof (msg),
1195 ngettext ("Finished (ignored %zd directory)",
1196 "Finished (ignored %zd directories)",
1197 ignore_count), ignore_count);
1198 status_update (msg);
1200 find_rotate_dash (h, TRUE);
1201 stop_idle (h);
1202 return 0;
1205 /* handle absolute ignore dirs here */
1207 char *tmp;
1208 gboolean ok;
1210 tmp = vfs_path_to_str (tmp_vpath);
1211 ok = find_ignore_dir_search (tmp);
1212 g_free (tmp);
1213 if (!ok)
1214 break;
1217 vfs_path_free (tmp_vpath);
1218 ignore_count++;
1221 g_free (directory);
1222 directory = vfs_path_to_str (tmp_vpath);
1224 if (verbose)
1226 char buffer[BUF_SMALL];
1228 g_snprintf (buffer, sizeof (buffer), _("Searching %s"),
1229 str_trunc (directory, FIND2_X_USE));
1230 status_update (buffer);
1232 /* mc_stat should not be called after mc_opendir
1233 because vfs_s_opendir modifies the st_nlink
1235 if (mc_stat (tmp_vpath, &tmp_stat) == 0)
1236 subdirs_left = tmp_stat.st_nlink - 2;
1237 else
1238 subdirs_left = 0;
1240 dirp = mc_opendir (tmp_vpath);
1241 vfs_path_free (tmp_vpath);
1242 } /* while (!dirp) */
1244 /* skip invalid filenames */
1245 while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1247 } /* while (!dp) */
1249 if (strcmp (dp->d_name, ".") == 0 || strcmp (dp->d_name, "..") == 0)
1251 /* skip invalid filenames */
1252 while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1255 return 1;
1258 if (!(options.skip_hidden && (dp->d_name[0] == '.')))
1260 gboolean search_ok;
1262 if ((subdirs_left != 0) && options.find_recurs && (directory != NULL))
1263 { /* Can directory be NULL ? */
1264 /* handle relative ignore dirs here */
1265 if (options.ignore_dirs_enable && find_ignore_dir_search (dp->d_name))
1266 ignore_count++;
1267 else
1269 vfs_path_t *tmp_vpath;
1271 tmp_vpath = vfs_path_build_filename (directory, dp->d_name, (char *) NULL);
1273 if (mc_lstat (tmp_vpath, &tmp_stat) == 0 && S_ISDIR (tmp_stat.st_mode))
1275 push_directory (tmp_vpath);
1276 subdirs_left--;
1278 else
1279 vfs_path_free (tmp_vpath);
1283 search_ok = mc_search_run (search_file_handle, dp->d_name,
1284 0, strlen (dp->d_name), &bytes_found);
1286 if (search_ok)
1288 if (content_pattern == NULL)
1289 find_add_match (directory, dp->d_name);
1290 else if (search_content (h, directory, dp->d_name))
1291 return 1;
1295 /* skip invalid filenames */
1296 while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1298 } /* for */
1300 find_rotate_dash (h, FALSE);
1302 return 1;
1305 /* --------------------------------------------------------------------------------------------- */
1307 static void
1308 init_find_vars (void)
1310 g_free (old_dir);
1311 old_dir = NULL;
1312 matches = 0;
1313 ignore_count = 0;
1315 /* Remove all the items from the stack */
1316 clear_stack ();
1318 g_strfreev (find_ignore_dirs);
1319 find_ignore_dirs = NULL;
1322 /* --------------------------------------------------------------------------------------------- */
1324 static void
1325 find_do_view_edit (int unparsed_view, int edit, char *dir, char *file)
1327 char *fullname = NULL;
1328 const char *filename = NULL;
1329 int line;
1330 vfs_path_t *fullname_vpath;
1332 if (content_pattern != NULL)
1334 filename = strchr (file + 4, ':') + 1;
1335 line = atoi (file + 4);
1337 else
1339 filename = file + 4;
1340 line = 0;
1343 fullname_vpath = vfs_path_build_filename (dir, filename, (char *) NULL);
1344 if (edit)
1345 do_edit_at_line (fullname_vpath, use_internal_edit, line);
1346 else
1347 view_file_at_line (fullname_vpath, unparsed_view, use_internal_view, line);
1348 vfs_path_free (fullname_vpath);
1349 g_free (fullname);
1352 /* --------------------------------------------------------------------------------------------- */
1354 static cb_ret_t
1355 view_edit_currently_selected_file (int unparsed_view, int edit)
1357 char *dir = NULL;
1358 char *text = NULL;
1360 listbox_get_current (find_list, &text, (void **) &dir);
1362 if ((text == NULL) || (dir == NULL))
1363 return MSG_NOT_HANDLED;
1365 find_do_view_edit (unparsed_view, edit, dir, text);
1366 return MSG_HANDLED;
1369 /* --------------------------------------------------------------------------------------------- */
1371 static cb_ret_t
1372 find_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
1374 switch (msg)
1376 case DLG_KEY:
1377 if (parm == KEY_F (3) || parm == KEY_F (13))
1379 int unparsed_view = (parm == KEY_F (13));
1380 return view_edit_currently_selected_file (unparsed_view, 0);
1382 if (parm == KEY_F (4))
1384 return view_edit_currently_selected_file (0, 1);
1386 return MSG_NOT_HANDLED;
1388 case DLG_IDLE:
1389 do_search (h);
1390 return MSG_HANDLED;
1392 default:
1393 return default_dlg_callback (h, sender, msg, parm, data);
1397 /* --------------------------------------------------------------------------------------------- */
1398 /** Handles the Stop/Start button in the find window */
1400 static int
1401 start_stop (WButton * button, int action)
1403 (void) button;
1404 (void) action;
1406 running = is_start;
1407 set_idle_proc (find_dlg, running);
1408 is_start = !is_start;
1410 status_update (is_start ? _("Stopped") : _("Searching"));
1411 button_set_text (stop_button, fbuts[is_start ? 1 : 0].text);
1413 return 0;
1416 /* --------------------------------------------------------------------------------------------- */
1417 /** Handle view command, when invoked as a button */
1419 static int
1420 find_do_view_file (WButton * button, int action)
1422 (void) button;
1423 (void) action;
1425 view_edit_currently_selected_file (0, 0);
1426 return 0;
1429 /* --------------------------------------------------------------------------------------------- */
1430 /** Handle edit command, when invoked as a button */
1432 static int
1433 find_do_edit_file (WButton * button, int action)
1435 (void) button;
1436 (void) action;
1438 view_edit_currently_selected_file (0, 1);
1439 return 0;
1442 /* --------------------------------------------------------------------------------------------- */
1444 static void
1445 setup_gui (void)
1447 #ifdef ENABLE_NLS
1448 static gboolean i18n_flag = FALSE;
1450 if (!i18n_flag)
1452 int i = sizeof (fbuts) / sizeof (fbuts[0]);
1453 while (i-- != 0)
1455 fbuts[i].text = _(fbuts[i].text);
1456 fbuts[i].len = str_term_width1 (fbuts[i].text) + 3;
1459 fbuts[2].len += 2; /* DEFPUSH_BUTTON */
1460 i18n_flag = TRUE;
1462 #endif /* ENABLE_NLS */
1465 * Dynamically place buttons centered within current window size
1468 int l0 = max (fbuts[0].len, fbuts[1].len);
1469 int l1 = fbuts[2].len + fbuts[3].len + l0 + fbuts[4].len;
1470 int l2 = fbuts[5].len + fbuts[6].len + fbuts[7].len;
1471 int r1, r2;
1473 /* Check, if both button rows fit within FIND2_X */
1474 FIND2_X = max (l1 + 9, COLS - 16);
1475 FIND2_X = max (l2 + 8, FIND2_X);
1477 /* compute amount of space between buttons for each row */
1478 r1 = (FIND2_X - 4 - l1) % 5;
1479 l1 = (FIND2_X - 4 - l1) / 5;
1480 r2 = (FIND2_X - 4 - l2) % 4;
1481 l2 = (FIND2_X - 4 - l2) / 4;
1483 /* ...and finally, place buttons */
1484 fbuts[2].x = 2 + r1 / 2 + l1;
1485 fbuts[3].x = fbuts[2].x + fbuts[2].len + l1;
1486 fbuts[0].x = fbuts[3].x + fbuts[3].len + l1;
1487 fbuts[4].x = fbuts[0].x + l0 + l1;
1488 fbuts[5].x = 2 + r2 / 2 + l2;
1489 fbuts[6].x = fbuts[5].x + fbuts[5].len + l2;
1490 fbuts[7].x = fbuts[6].x + fbuts[6].len + l2;
1493 find_dlg =
1494 create_dlg (TRUE, 0, 0, FIND2_Y, FIND2_X, dialog_colors, find_callback, NULL,
1495 "[Find File]", _("Find File"), DLG_CENTER | DLG_REVERSE);
1497 add_widget (find_dlg,
1498 button_new (FIND2_Y - 3, fbuts[7].x, B_VIEW, NORMAL_BUTTON,
1499 fbuts[7].text, find_do_edit_file));
1500 add_widget (find_dlg,
1501 button_new (FIND2_Y - 3, fbuts[6].x, B_VIEW, NORMAL_BUTTON,
1502 fbuts[6].text, find_do_view_file));
1503 add_widget (find_dlg,
1504 button_new (FIND2_Y - 3, fbuts[5].x, B_PANELIZE, NORMAL_BUTTON, fbuts[5].text,
1505 NULL));
1507 add_widget (find_dlg,
1508 button_new (FIND2_Y - 4, fbuts[4].x, B_CANCEL, NORMAL_BUTTON, fbuts[4].text, NULL));
1509 stop_button =
1510 button_new (FIND2_Y - 4, fbuts[0].x, B_STOP, NORMAL_BUTTON, fbuts[0].text, start_stop);
1511 add_widget (find_dlg, stop_button);
1512 add_widget (find_dlg,
1513 button_new (FIND2_Y - 4, fbuts[3].x, B_AGAIN, NORMAL_BUTTON, fbuts[3].text, NULL));
1514 add_widget (find_dlg,
1515 button_new (FIND2_Y - 4, fbuts[2].x, B_ENTER, DEFPUSH_BUTTON, fbuts[2].text, NULL));
1517 status_label = label_new (FIND2_Y - 7, 4, _("Searching"));
1518 add_widget (find_dlg, status_label);
1520 found_num_label = label_new (FIND2_Y - 6, 4, "");
1521 add_widget (find_dlg, found_num_label);
1523 find_list = listbox_new (2, 2, FIND2_Y - 10, FIND2_X - 4, FALSE, NULL);
1524 add_widget (find_dlg, find_list);
1527 /* --------------------------------------------------------------------------------------------- */
1529 static int
1530 run_process (void)
1532 int ret;
1534 search_content_handle = mc_search_new (content_pattern, -1);
1535 if (search_content_handle)
1537 search_content_handle->search_type =
1538 options.content_regexp ? MC_SEARCH_T_REGEX : MC_SEARCH_T_NORMAL;
1539 search_content_handle->is_case_sensitive = options.content_case_sens;
1540 search_content_handle->whole_words = options.content_whole_words;
1541 search_content_handle->is_all_charsets = options.content_all_charsets;
1543 search_file_handle = mc_search_new (find_pattern, -1);
1544 search_file_handle->search_type = options.file_pattern ? MC_SEARCH_T_GLOB : MC_SEARCH_T_REGEX;
1545 search_file_handle->is_case_sensitive = options.file_case_sens;
1546 search_file_handle->is_all_charsets = options.file_all_charsets;
1547 search_file_handle->is_entire_line = options.file_pattern;
1549 resuming = 0;
1551 set_idle_proc (find_dlg, 1);
1552 ret = run_dlg (find_dlg);
1554 mc_search_free (search_file_handle);
1555 search_file_handle = NULL;
1556 mc_search_free (search_content_handle);
1557 search_content_handle = NULL;
1559 return ret;
1562 /* --------------------------------------------------------------------------------------------- */
1564 static void
1565 kill_gui (void)
1567 set_idle_proc (find_dlg, 0);
1568 destroy_dlg (find_dlg);
1571 /* --------------------------------------------------------------------------------------------- */
1573 static int
1574 do_find (const char *start_dir, ssize_t start_dir_len, const char *ignore_dirs,
1575 const char *pattern, const char *content, char **dirname, char **filename)
1577 int return_value = 0;
1578 char *dir_tmp = NULL, *file_tmp = NULL;
1580 setup_gui ();
1582 /* FIXME: Need to cleanup this, this ought to be passed non-globaly */
1583 find_pattern = (char *) pattern;
1585 content_pattern = NULL;
1586 if (options.content_use && content != NULL && str_is_valid_string (content))
1587 content_pattern = g_strdup (content);
1589 init_find_vars ();
1590 parse_ignore_dirs (ignore_dirs);
1591 push_directory (vfs_path_from_str (start_dir));
1593 return_value = run_process ();
1595 /* Clear variables */
1596 init_find_vars ();
1598 get_list_info (&file_tmp, &dir_tmp);
1600 if (dir_tmp)
1601 *dirname = g_strdup (dir_tmp);
1602 if (file_tmp)
1603 *filename = g_strdup (file_tmp);
1605 if (return_value == B_PANELIZE && *filename)
1607 int status, link_to_dir, stale_link;
1608 int next_free = 0;
1609 int i;
1610 struct stat st;
1611 GList *entry;
1612 dir_list *list = &current_panel->dir;
1613 char *name = NULL;
1615 if (set_zero_dir (list))
1616 next_free++;
1618 for (i = 0, entry = find_list->list; entry != NULL; i++, entry = g_list_next (entry))
1620 const char *lc_filename = NULL;
1621 WLEntry *le = (WLEntry *) entry->data;
1622 char *p;
1624 if ((le->text == NULL) || (le->data == NULL))
1625 continue;
1627 if (content_pattern != NULL)
1628 lc_filename = strchr (le->text + 4, ':') + 1;
1629 else
1630 lc_filename = le->text + 4;
1632 name = mc_build_filename (le->data, lc_filename, (char *) NULL);
1633 /* skip initial start dir */
1634 if (start_dir_len < 0)
1635 p = name;
1636 else
1638 p = name + (size_t) start_dir_len;
1639 if (*p == PATH_SEP)
1640 p++;
1643 status = handle_path (list, p, &st, next_free, &link_to_dir, &stale_link);
1644 if (status == 0)
1646 g_free (name);
1647 continue;
1649 if (status == -1)
1651 g_free (name);
1652 break;
1655 /* don't add files more than once to the panel */
1656 if (content_pattern != NULL && next_free > 0
1657 && strcmp (list->list[next_free - 1].fname, p) == 0)
1659 g_free (name);
1660 continue;
1663 if (next_free == 0) /* first turn i.e clean old list */
1664 panel_clean_dir (current_panel);
1665 list->list[next_free].fnamelen = strlen (p);
1666 list->list[next_free].fname = g_strndup (p, list->list[next_free].fnamelen);
1667 list->list[next_free].f.marked = 0;
1668 list->list[next_free].f.link_to_dir = link_to_dir;
1669 list->list[next_free].f.stale_link = stale_link;
1670 list->list[next_free].f.dir_size_computed = 0;
1671 list->list[next_free].st = st;
1672 list->list[next_free].sort_key = NULL;
1673 list->list[next_free].second_sort_key = NULL;
1674 next_free++;
1675 g_free (name);
1676 if (!(next_free & 15))
1677 rotate_dash ();
1680 if (next_free)
1682 current_panel->count = next_free;
1683 current_panel->is_panelized = TRUE;
1685 /* absolute path */
1686 if (start_dir_len < 0)
1688 int ret;
1689 vfs_path_free (current_panel->cwd_vpath);
1690 current_panel->cwd_vpath = vfs_path_from_str (PATH_SEP_STR);
1691 ret = chdir (PATH_SEP_STR);
1693 panelize_save_panel (current_panel);
1697 g_free (content_pattern);
1698 kill_gui ();
1699 do_search (NULL); /* force do_search to release resources */
1700 g_free (old_dir);
1701 old_dir = NULL;
1703 return return_value;
1706 /* --------------------------------------------------------------------------------------------- */
1707 /*** public functions ****************************************************************************/
1708 /* --------------------------------------------------------------------------------------------- */
1710 void
1711 find_file (void)
1713 char *start_dir = NULL, *pattern = NULL, *content = NULL, *ignore_dirs = NULL;
1714 ssize_t start_dir_len;
1715 char *filename = NULL, *dirname = NULL;
1716 int v;
1718 while (find_parameters (&start_dir, &start_dir_len, &ignore_dirs, &pattern, &content))
1720 if (pattern[0] == '\0')
1721 break; /* nothing search */
1723 dirname = filename = NULL;
1724 is_start = FALSE;
1725 v = do_find (start_dir, start_dir_len, ignore_dirs, pattern, content, &dirname, &filename);
1726 g_free (ignore_dirs);
1727 g_free (pattern);
1729 if (v == B_ENTER)
1731 if (dirname != NULL)
1733 vfs_path_t *dirname_vpath;
1735 dirname_vpath = vfs_path_from_str (dirname);
1736 do_cd (dirname_vpath, cd_exact);
1737 vfs_path_free (dirname_vpath);
1738 if (filename != NULL)
1739 try_to_select (current_panel,
1740 filename + (content != NULL
1741 ? strchr (filename + 4, ':') - filename + 1 : 4));
1743 else if (filename != NULL)
1745 vfs_path_t *filename_vpath;
1747 filename_vpath = vfs_path_from_str (filename);
1748 do_cd (filename_vpath, cd_exact);
1749 vfs_path_free (filename_vpath);
1752 g_free (dirname);
1753 g_free (filename);
1754 break;
1757 g_free (content);
1758 g_free (dirname);
1759 g_free (filename);
1761 if (v == B_CANCEL)
1762 break;
1764 if (v == B_PANELIZE)
1766 panel_re_sort (current_panel);
1767 try_to_select (current_panel, NULL);
1768 break;
1773 /* --------------------------------------------------------------------------------------------- */