Added function mc_build_filename() for processing URL-paths as well
[midnight-commander.git] / src / filemanager / find.c
blob5dfcc382f45bbc685295fd092e287cbe48bd6bbb
1 /* Find file command for the Midnight Commander
2 Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
3 2006, 2007 Free Software Foundation, Inc.
4 Written 1995 by Miguel de Icaza
6 Complete rewrite.
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
22 /** \file find.c
23 * \brief Source: Find file command
26 #include <config.h>
28 #include <ctype.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <fcntl.h>
33 #include <sys/stat.h>
35 #include "lib/global.h"
37 #include "lib/tty/tty.h"
38 #include "lib/tty/key.h"
39 #include "lib/skin.h"
40 #include "lib/search.h"
41 #include "lib/mcconfig.h"
42 #include "lib/vfs/vfs.h"
43 #include "lib/strutil.h"
44 #include "lib/widget.h"
45 #include "lib/util.h" /* canonicalize_pathname() */
47 #include "src/setup.h" /* verbose */
48 #include "src/history.h" /* MC_HISTORY_SHARED_SEARCH */
49 #include "src/main.h" /* do_cd */
51 #include "dir.h"
52 #include "cmd.h" /* view_file_at_line */
53 #include "midnight.h" /* current_panel */
54 #include "boxes.h"
56 #include "find.h"
58 /*** global variables ****************************************************************************/
60 /*** file scope macro definitions ****************************************************************/
62 /* Size of the find window */
63 #define FIND2_Y (LINES - 4)
65 #define FIND2_X_USE (FIND2_X - 20)
67 /*** file scope type declarations ****************************************************************/
69 /* A couple of extra messages we need */
70 enum
72 B_STOP = B_USER + 1,
73 B_AGAIN,
74 B_PANELIZE,
75 B_TREE,
76 B_VIEW
79 typedef enum
81 FIND_CONT = 0,
82 FIND_SUSPEND,
83 FIND_ABORT
84 } FindProgressStatus;
86 /* find file options */
87 typedef struct
89 /* file name options */
90 gboolean file_case_sens;
91 gboolean file_pattern;
92 gboolean find_recurs;
93 gboolean skip_hidden;
94 gboolean file_all_charsets;
96 /* file content options */
97 gboolean content_use;
98 gboolean content_case_sens;
99 gboolean content_regexp;
100 gboolean content_first_hit;
101 gboolean content_whole_words;
102 gboolean content_all_charsets;
104 /* whether use ignore dirs or not */
105 gboolean ignore_dirs_enable;
106 /* list of directories to be ignored, separated by ':' */
107 char *ignore_dirs;
108 } find_file_options_t;
110 /*** file scope variables ************************************************************************/
112 /* Parsed ignore dirs */
113 static char **find_ignore_dirs = NULL;
115 /* Size of the find parameters window */
116 #if HAVE_CHARSET
117 static int FIND_Y = 19;
118 #else
119 static int FIND_Y = 18;
120 #endif
121 static int FIND_X = 68;
123 static int FIND2_X = 64;
125 /* static variables to remember find parameters */
126 static WInput *in_start; /* Start path */
127 static WInput *in_name; /* Filename */
128 static WInput *in_with; /* Text */
129 static WInput *in_ignore;
130 static WLabel *content_label; /* 'Content:' label */
131 static WCheck *file_case_sens_cbox; /* "case sensitive" checkbox */
132 static WCheck *file_pattern_cbox; /* File name is glob or regexp */
133 static WCheck *recursively_cbox;
134 static WCheck *skip_hidden_cbox;
135 static WCheck *content_use_cbox; /* Take into account the Content field */
136 static WCheck *content_case_sens_cbox; /* "case sensitive" checkbox */
137 static WCheck *content_regexp_cbox; /* "find regular expression" checkbox */
138 static WCheck *content_first_hit_cbox; /* "First hit" checkbox" */
139 static WCheck *content_whole_words_cbox; /* "whole words" checkbox */
140 #ifdef HAVE_CHARSET
141 static WCheck *file_all_charsets_cbox;
142 static WCheck *content_all_charsets_cbox;
143 #endif
144 static WCheck *ignore_dirs_cbox;
146 static gboolean running = FALSE; /* nice flag */
147 static char *find_pattern = NULL; /* Pattern to search */
148 static char *content_pattern = NULL; /* pattern to search inside files; if
149 content_regexp_flag is true, it contains the
150 regex pattern, else the search string. */
151 static unsigned long matches; /* Number of matches */
152 static gboolean is_start = FALSE; /* Status of the start/stop toggle button */
153 static char *old_dir = NULL;
155 /* Where did we stop */
156 static int resuming;
157 static int last_line;
158 static int last_pos;
160 static size_t ignore_count = 0;
162 static Dlg_head *find_dlg; /* The dialog */
163 static WButton *stop_button; /* pointer to the stop button */
164 static WLabel *status_label; /* Finished, Searching etc. */
165 static WLabel *found_num_label; /* Number of found items */
166 static WListbox *find_list; /* Listbox with the file list */
168 /* This keeps track of the directory stack */
169 #if GLIB_CHECK_VERSION (2, 14, 0)
170 static GQueue dir_queue = G_QUEUE_INIT;
171 #else
172 typedef struct dir_stack
174 char *name;
175 struct dir_stack *prev;
176 } dir_stack;
178 static dir_stack *dir_stack_base = 0;
179 #endif /* GLIB_CHECK_VERSION */
181 /* *INDENT-OFF* */
182 static struct
184 const char *text;
185 int len; /* length including space and brackets */
186 int x;
187 } fbuts[] =
189 {N_("&Suspend"), 11, 29},
190 {N_("Con&tinue"), 12, 29},
191 {N_("&Chdir"), 11, 3},
192 {N_("&Again"), 9, 17},
193 {N_("&Quit"), 8, 43},
194 {N_("Pane&lize"), 12, 3},
195 {N_("&View - F3"), 13, 20},
196 {N_("&Edit - F4"), 13, 38}
198 /* *INDENT-ON* */
200 static find_file_options_t options = {
201 TRUE, TRUE, TRUE, FALSE, FALSE,
202 FALSE, TRUE, FALSE, FALSE, FALSE, FALSE
205 static char *in_start_dir = INPUT_LAST_TEXT;
207 static mc_search_t *search_file_handle = NULL;
208 static mc_search_t *search_content_handle = NULL;
210 /*** file scope functions ************************************************************************/
212 static void
213 parse_ignore_dirs (const char *ignore_dirs)
215 size_t r = 0, w = 0; /* read and write iterators */
217 if (!options.ignore_dirs_enable || ignore_dirs == NULL || ignore_dirs[0] == '\0')
218 return;
220 find_ignore_dirs = g_strsplit (ignore_dirs, ":", -1);
222 /* Values like '/foo::/bar: produce holes in list.
223 * Find and remove them */
224 for (; find_ignore_dirs[r] != NULL; r++)
226 if (find_ignore_dirs[r][0] == '\0')
228 /* empty entry -- skip it */
229 g_free (find_ignore_dirs[r]);
230 find_ignore_dirs[r] = NULL;
231 continue;
234 if (r != w)
236 /* copy entry to the previous free array cell */
237 find_ignore_dirs[w] = find_ignore_dirs[r];
238 find_ignore_dirs[r] = NULL;
241 canonicalize_pathname (find_ignore_dirs[w]);
242 if (find_ignore_dirs[w][0] != '\0')
243 w++;
244 else
246 g_free (find_ignore_dirs[w]);
247 find_ignore_dirs[w] = NULL;
251 if (find_ignore_dirs[0] == NULL)
253 g_strfreev (find_ignore_dirs);
254 find_ignore_dirs = NULL;
258 /* --------------------------------------------------------------------------------------------- */
260 static void
261 find_load_options (void)
263 static gboolean loaded = FALSE;
264 char *ignore_dirs;
266 if (loaded)
267 return;
269 loaded = TRUE;
271 options.file_case_sens =
272 mc_config_get_bool (mc_main_config, "FindFile", "file_case_sens", TRUE);
273 options.file_pattern =
274 mc_config_get_bool (mc_main_config, "FindFile", "file_shell_pattern", TRUE);
275 options.find_recurs = mc_config_get_bool (mc_main_config, "FindFile", "file_find_recurs", TRUE);
276 options.skip_hidden =
277 mc_config_get_bool (mc_main_config, "FindFile", "file_skip_hidden", FALSE);
278 options.file_all_charsets =
279 mc_config_get_bool (mc_main_config, "FindFile", "file_all_charsets", FALSE);
280 options.content_use = mc_config_get_bool (mc_main_config, "FindFile", "content_use", TRUE);
281 options.content_case_sens =
282 mc_config_get_bool (mc_main_config, "FindFile", "content_case_sens", TRUE);
283 options.content_regexp =
284 mc_config_get_bool (mc_main_config, "FindFile", "content_regexp", FALSE);
285 options.content_first_hit =
286 mc_config_get_bool (mc_main_config, "FindFile", "content_first_hit", FALSE);
287 options.content_whole_words =
288 mc_config_get_bool (mc_main_config, "FindFile", "content_whole_words", FALSE);
289 options.content_all_charsets =
290 mc_config_get_bool (mc_main_config, "FindFile", "content_all_charsets", FALSE);
291 options.ignore_dirs_enable =
292 mc_config_get_bool (mc_main_config, "FindFile", "ignore_dirs_enable", TRUE);
294 /* Back compatibility: try load old parameter at first */
295 ignore_dirs = mc_config_get_string (mc_main_config, "Misc", "find_ignore_dirs", "");
296 mc_config_del_key (mc_main_config, "Misc", "find_ignore_dirs");
298 /* Then load new parameters */
299 options.ignore_dirs = mc_config_get_string (mc_main_config, "FindFile", "ignore_dirs", "");
300 if (options.ignore_dirs[0] != '\0')
301 g_free (ignore_dirs);
302 else
304 g_free (options.ignore_dirs);
305 options.ignore_dirs = ignore_dirs;
308 if (options.ignore_dirs[0] == '\0')
310 g_free (options.ignore_dirs);
311 options.ignore_dirs = NULL;
315 /* --------------------------------------------------------------------------------------------- */
317 static void
318 find_save_options (void)
320 mc_config_set_bool (mc_main_config, "FindFile", "file_case_sens", options.file_case_sens);
321 mc_config_set_bool (mc_main_config, "FindFile", "file_shell_pattern", options.file_pattern);
322 mc_config_set_bool (mc_main_config, "FindFile", "file_find_recurs", options.find_recurs);
323 mc_config_set_bool (mc_main_config, "FindFile", "file_skip_hidden", options.skip_hidden);
324 mc_config_set_bool (mc_main_config, "FindFile", "file_all_charsets", options.file_all_charsets);
325 mc_config_set_bool (mc_main_config, "FindFile", "content_use", options.content_use);
326 mc_config_set_bool (mc_main_config, "FindFile", "content_case_sens", options.content_case_sens);
327 mc_config_set_bool (mc_main_config, "FindFile", "content_regexp", options.content_regexp);
328 mc_config_set_bool (mc_main_config, "FindFile", "content_first_hit", options.content_first_hit);
329 mc_config_set_bool (mc_main_config, "FindFile", "content_whole_words",
330 options.content_whole_words);
331 mc_config_set_bool (mc_main_config, "FindFile", "content_all_charsets",
332 options.content_all_charsets);
333 mc_config_set_bool (mc_main_config, "FindFile", "ignore_dirs_enable",
334 options.ignore_dirs_enable);
335 mc_config_set_string (mc_main_config, "FindFile", "ignore_dirs", options.ignore_dirs);
338 /* --------------------------------------------------------------------------------------------- */
340 static inline char *
341 add_to_list (const char *text, void *data)
343 return listbox_add_item (find_list, LISTBOX_APPEND_AT_END, 0, text, data);
346 /* --------------------------------------------------------------------------------------------- */
348 static inline void
349 stop_idle (void *data)
351 set_idle_proc (data, 0);
354 /* --------------------------------------------------------------------------------------------- */
356 static inline void
357 status_update (const char *text)
359 label_set_text (status_label, text);
362 /* --------------------------------------------------------------------------------------------- */
364 static void
365 found_num_update (void)
367 char buffer[BUF_TINY];
368 g_snprintf (buffer, sizeof (buffer), _("Found: %ld"), matches);
369 label_set_text (found_num_label, buffer);
372 /* --------------------------------------------------------------------------------------------- */
374 static void
375 get_list_info (char **file, char **dir)
377 listbox_get_current (find_list, file, (void **) dir);
380 /* --------------------------------------------------------------------------------------------- */
381 /** check regular expression */
383 static gboolean
384 find_check_regexp (const char *r)
386 mc_search_t *search;
387 gboolean regexp_ok = FALSE;
389 search = mc_search_new (r, -1);
391 if (search != NULL)
393 search->search_type = MC_SEARCH_T_REGEX;
394 regexp_ok = mc_search_prepare (search);
395 mc_search_free (search);
398 return regexp_ok;
401 /* --------------------------------------------------------------------------------------------- */
403 * Callback for the parameter dialog.
404 * Validate regex, prevent closing the dialog if it's invalid.
407 static cb_ret_t
408 find_parm_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
410 switch (msg)
412 case DLG_ACTION:
413 if (sender == (Widget *) content_use_cbox)
415 gboolean disable = !(content_use_cbox->state & C_BOOL);
417 widget_disable (content_label->widget, disable);
418 send_message ((Widget *) content_label, WIDGET_DRAW, 0);
419 widget_disable (in_with->widget, disable);
420 send_message ((Widget *) in_with, WIDGET_DRAW, 0);
421 widget_disable (content_first_hit_cbox->widget, disable);
422 send_message ((Widget *) content_first_hit_cbox, WIDGET_DRAW, 0);
423 widget_disable (content_regexp_cbox->widget, disable);
424 send_message ((Widget *) content_regexp_cbox, WIDGET_DRAW, 0);
425 widget_disable (content_case_sens_cbox->widget, disable);
426 send_message ((Widget *) content_case_sens_cbox, WIDGET_DRAW, 0);
427 #ifdef HAVE_CHARSET
428 widget_disable (content_all_charsets_cbox->widget, disable);
429 send_message ((Widget *) content_all_charsets_cbox, WIDGET_DRAW, 0);
430 #endif
431 widget_disable (content_whole_words_cbox->widget, disable);
432 send_message ((Widget *) content_whole_words_cbox, WIDGET_DRAW, 0);
434 return MSG_HANDLED;
437 if (sender == (Widget *) ignore_dirs_cbox)
439 gboolean disable = !(ignore_dirs_cbox->state & C_BOOL);
441 widget_disable (in_ignore->widget, disable);
442 send_message ((Widget *) in_ignore, WIDGET_DRAW, 0);
444 return MSG_HANDLED;
447 return MSG_NOT_HANDLED;
450 case DLG_VALIDATE:
451 if (h->ret_value != B_ENTER)
452 return MSG_HANDLED;
454 /* check filename regexp */
455 if (!(file_pattern_cbox->state & C_BOOL)
456 && (in_name->buffer[0] != '\0') && !find_check_regexp (in_name->buffer))
458 h->state = DLG_ACTIVE; /* Don't stop the dialog */
459 message (D_ERROR, MSG_ERROR, _("Malformed regular expression"));
460 dlg_select_widget (in_name);
461 return MSG_HANDLED;
464 /* check content regexp */
465 if ((content_use_cbox->state & C_BOOL) && (content_regexp_cbox->state & C_BOOL)
466 && (in_with->buffer[0] != '\0') && !find_check_regexp (in_with->buffer))
468 h->state = DLG_ACTIVE; /* Don't stop the dialog */
469 message (D_ERROR, MSG_ERROR, _("Malformed regular expression"));
470 dlg_select_widget (in_with);
471 return MSG_HANDLED;
474 return MSG_HANDLED;
476 default:
477 return default_dlg_callback (h, sender, msg, parm, data);
481 /* --------------------------------------------------------------------------------------------- */
483 * find_parameters: gets information from the user
485 * If the return value is TRUE, then the following holds:
487 * START_DIR, IGNORE_FIR and PATTERN are pointers to char * and upon return they
488 * contain the information provided by the user.
490 * CONTENT holds a strdup of the contents specified by the user if he
491 * asked for them or 0 if not (note, this is different from the
492 * behavior for the other two parameters.
496 static gboolean
497 find_parameters (char **start_dir, char **ignore_dirs, char **pattern, char **content)
499 gboolean return_value;
501 /* file name */
502 const char *file_case_label = N_("Cas&e sensitive");
503 const char *file_pattern_label = N_("&Using shell patterns");
504 const char *file_recurs_label = N_("&Find recursively");
505 const char *file_skip_hidden_label = N_("S&kip hidden");
506 #ifdef HAVE_CHARSET
507 const char *file_all_charsets_label = N_("&All charsets");
508 #endif
510 /* file content */
511 const char *content_use_label = N_("Sea&rch for content");
512 const char *content_case_label = N_("Case sens&itive");
513 const char *content_regexp_label = N_("Re&gular expression");
514 const char *content_first_hit_label = N_("Fir&st hit");
515 const char *content_whole_words_label = N_("&Whole words");
516 #ifdef HAVE_CHARSET
517 const char *content_all_charsets_label = N_("A&ll charsets");
518 #endif
520 const char *buts[] = { N_("&OK"), N_("&Cancel"), N_("&Tree") };
522 int b0, b1, b2;
524 int cbox_position;
525 gboolean disable;
527 #ifdef ENABLE_NLS
529 int i = sizeof (buts) / sizeof (buts[0]);
530 while (i-- != 0)
531 buts[i] = _(buts[i]);
533 file_case_label = _(file_case_label);
534 file_pattern_label = _(file_pattern_label);
535 file_recurs_label = _(file_recurs_label);
536 file_skip_hidden_label = _(file_skip_hidden_label);
537 #ifdef HAVE_CHARSET
538 file_all_charsets_label = _(file_all_charsets_label);
539 content_all_charsets_label = _(content_all_charsets_label);
540 #endif
541 content_use_label = _(content_use_label);
542 content_case_label = _(content_case_label);
543 content_regexp_label = _(content_regexp_label);
544 content_first_hit_label = _(content_first_hit_label);
545 content_whole_words_label = _(content_whole_words_label);
547 #endif /* ENABLE_NLS */
549 b0 = str_term_width1 (buts[0]) + 6; /* default button */
550 b1 = str_term_width1 (buts[1]) + 4;
551 b2 = str_term_width1 (buts[2]) + 4;
553 find_load_options ();
555 if (in_start_dir == NULL)
556 in_start_dir = g_strdup (".");
558 disable = !options.content_use;
560 find_dlg =
561 create_dlg (TRUE, 0, 0, FIND_Y, FIND_X, dialog_colors,
562 find_parm_callback, "[Find File]", _("Find File"), DLG_CENTER | DLG_REVERSE);
564 add_widget (find_dlg,
565 button_new (FIND_Y - 3, FIND_X * 3 / 4 - b1 / 2, B_CANCEL, NORMAL_BUTTON, buts[1],
566 0));
567 add_widget (find_dlg,
568 button_new (FIND_Y - 3, FIND_X / 4 - b0 / 2, B_ENTER, DEFPUSH_BUTTON, buts[0], 0));
570 cbox_position = FIND_Y - 5;
572 content_first_hit_cbox =
573 check_new (cbox_position--, FIND_X / 2 + 1, options.content_first_hit,
574 content_first_hit_label);
575 widget_disable (content_first_hit_cbox->widget, disable);
576 add_widget (find_dlg, content_first_hit_cbox);
578 content_whole_words_cbox =
579 check_new (cbox_position--, FIND_X / 2 + 1, options.content_whole_words,
580 content_whole_words_label);
581 widget_disable (content_whole_words_cbox->widget, disable);
582 add_widget (find_dlg, content_whole_words_cbox);
584 #ifdef HAVE_CHARSET
585 content_all_charsets_cbox = check_new (cbox_position--, FIND_X / 2 + 1,
586 options.content_all_charsets,
587 content_all_charsets_label);
588 widget_disable (content_all_charsets_cbox->widget, disable);
589 add_widget (find_dlg, content_all_charsets_cbox);
590 #endif
592 content_case_sens_cbox =
593 check_new (cbox_position--, FIND_X / 2 + 1, options.content_case_sens, content_case_label);
594 widget_disable (content_case_sens_cbox->widget, disable);
595 add_widget (find_dlg, content_case_sens_cbox);
597 content_regexp_cbox =
598 check_new (cbox_position--, FIND_X / 2 + 1, options.content_regexp, content_regexp_label);
599 widget_disable (content_regexp_cbox->widget, disable);
600 add_widget (find_dlg, content_regexp_cbox);
602 cbox_position = FIND_Y - 6;
604 skip_hidden_cbox = check_new (cbox_position--, 3, options.skip_hidden, file_skip_hidden_label);
605 add_widget (find_dlg, skip_hidden_cbox);
607 #ifdef HAVE_CHARSET
608 file_all_charsets_cbox =
609 check_new (cbox_position--, 3, options.file_all_charsets, file_all_charsets_label);
610 add_widget (find_dlg, file_all_charsets_cbox);
611 #endif
613 file_case_sens_cbox = check_new (cbox_position--, 3, options.file_case_sens, file_case_label);
614 add_widget (find_dlg, file_case_sens_cbox);
616 file_pattern_cbox = check_new (cbox_position--, 3, options.file_pattern, file_pattern_label);
617 add_widget (find_dlg, file_pattern_cbox);
619 recursively_cbox = check_new (cbox_position, 3, options.find_recurs, file_recurs_label);
620 add_widget (find_dlg, recursively_cbox);
622 /* This checkbox is located in the second column */
623 content_use_cbox =
624 check_new (cbox_position, FIND_X / 2 + 1, options.content_use, content_use_label);
625 add_widget (find_dlg, content_use_cbox);
627 in_with =
628 input_new (8, FIND_X / 2 + 1, input_get_default_colors (), FIND_X / 2 - 4, INPUT_LAST_TEXT,
629 MC_HISTORY_SHARED_SEARCH, INPUT_COMPLETE_DEFAULT);
630 widget_disable (in_with->widget, disable);
631 add_widget (find_dlg, in_with);
633 content_label = label_new (7, FIND_X / 2 + 1, _("Content:"));
634 widget_disable (content_label->widget, disable);
635 add_widget (find_dlg, content_label);
637 in_name = input_new (8, 3, input_get_default_colors (),
638 FIND_X / 2 - 4, INPUT_LAST_TEXT, "name", INPUT_COMPLETE_DEFAULT);
639 add_widget (find_dlg, in_name);
640 add_widget (find_dlg, label_new (7, 3, _("File name:")));
642 in_ignore = input_new (5, 3, input_get_default_colors (), FIND_X - 6,
643 options.ignore_dirs != NULL ? options.ignore_dirs : "",
644 "ignoredirs", INPUT_COMPLETE_DEFAULT);
645 widget_disable (in_ignore->widget, !options.ignore_dirs_enable);
646 add_widget (find_dlg, in_ignore);
648 ignore_dirs_cbox =
649 check_new (4, 3, options.ignore_dirs_enable, _("Ena&ble ignore directories:"));
650 add_widget (find_dlg, ignore_dirs_cbox);
652 add_widget (find_dlg, button_new (3, FIND_X - b2 - 2, B_TREE, NORMAL_BUTTON, buts[2], 0));
654 in_start = input_new (3, 3, input_get_default_colors (),
655 FIND_X - b2 - 6, in_start_dir, "start", INPUT_COMPLETE_DEFAULT);
656 add_widget (find_dlg, in_start);
657 add_widget (find_dlg, label_new (2, 3, _("Start at:")));
659 find_par_start:
660 dlg_select_widget (in_name);
662 switch (run_dlg (find_dlg))
664 case B_CANCEL:
665 return_value = FALSE;
666 break;
668 case B_TREE:
670 const char *temp_dir = in_start->buffer;
672 if ((temp_dir[0] == '\0') || ((temp_dir[0] == '.') && (temp_dir[1] == '\0')))
673 temp_dir = current_panel->cwd;
675 if (in_start_dir != INPUT_LAST_TEXT)
676 g_free (in_start_dir);
677 in_start_dir = tree_box (temp_dir);
678 if (in_start_dir == NULL)
679 in_start_dir = g_strdup (temp_dir);
681 input_assign_text (in_start, in_start_dir);
683 /* Warning: Dreadful goto */
684 goto find_par_start;
687 default:
688 #ifdef HAVE_CHARSET
689 options.file_all_charsets = file_all_charsets_cbox->state & C_BOOL;
690 options.content_all_charsets = content_all_charsets_cbox->state & C_BOOL;
691 #endif
692 options.content_use = content_use_cbox->state & C_BOOL;
693 options.content_case_sens = content_case_sens_cbox->state & C_BOOL;
694 options.content_regexp = content_regexp_cbox->state & C_BOOL;
695 options.content_first_hit = content_first_hit_cbox->state & C_BOOL;
696 options.content_whole_words = content_whole_words_cbox->state & C_BOOL;
697 options.find_recurs = recursively_cbox->state & C_BOOL;
698 options.file_pattern = file_pattern_cbox->state & C_BOOL;
699 options.file_case_sens = file_case_sens_cbox->state & C_BOOL;
700 options.skip_hidden = skip_hidden_cbox->state & C_BOOL;
701 options.ignore_dirs_enable = ignore_dirs_cbox->state & C_BOOL;
702 g_free (options.ignore_dirs);
703 options.ignore_dirs = g_strdup (in_ignore->buffer);
705 *content = (options.content_use && in_with->buffer[0] != '\0')
706 ? g_strdup (in_with->buffer) : NULL;
707 *start_dir = in_start->buffer[0] != '\0' ? in_start->buffer : (char *) ".";
708 *pattern = g_strdup (in_name->buffer);
709 if (in_start_dir != INPUT_LAST_TEXT)
710 g_free (in_start_dir);
711 in_start_dir = g_strdup (*start_dir);
712 if ((*start_dir)[0] == '.' && (*start_dir)[1] == '\0')
713 *start_dir = g_strdup (current_panel->cwd);
714 else if (g_path_is_absolute (*start_dir))
715 *start_dir = g_strdup (*start_dir);
716 else
717 *start_dir = mc_build_filename (current_panel->cwd, *start_dir, (char *) NULL);
719 canonicalize_pathname (*start_dir);
721 if (!options.ignore_dirs_enable || in_ignore->buffer[0] == '\0'
722 || (in_ignore->buffer[0] == '.' && in_ignore->buffer[1] == '\0'))
723 *ignore_dirs = NULL;
724 else
725 *ignore_dirs = g_strdup (in_ignore->buffer);
727 find_save_options ();
729 return_value = TRUE;
732 destroy_dlg (find_dlg);
734 return return_value;
737 /* --------------------------------------------------------------------------------------------- */
739 #if GLIB_CHECK_VERSION (2, 14, 0)
740 static inline void
741 push_directory (const char *dir)
743 g_queue_push_head (&dir_queue, (void *) dir);
746 /* --------------------------------------------------------------------------------------------- */
748 static inline char *
749 pop_directory (void)
751 return (char *) g_queue_pop_tail (&dir_queue);
754 /* --------------------------------------------------------------------------------------------- */
755 /** Remove all the items from the stack */
757 static void
758 clear_stack (void)
760 g_queue_foreach (&dir_queue, (GFunc) g_free, NULL);
761 g_queue_clear (&dir_queue);
764 /* --------------------------------------------------------------------------------------------- */
766 #else /* GLIB_CHECK_VERSION */
767 static void
768 push_directory (const char *dir)
770 dir_stack *new;
772 new = g_new (dir_stack, 1);
773 new->name = (char *) dir;
774 new->prev = dir_stack_base;
775 dir_stack_base = new;
778 /* --------------------------------------------------------------------------------------------- */
780 static char *
781 pop_directory (void)
783 char *name = NULL;
785 if (dir_stack_base != NULL)
787 dir_stack *next;
788 name = dir_stack_base->name;
789 next = dir_stack_base->prev;
790 g_free (dir_stack_base);
791 dir_stack_base = next;
794 return name;
797 /* --------------------------------------------------------------------------------------------- */
798 /** Remove all the items from the stack */
800 static void
801 clear_stack (void)
803 char *dir = NULL;
804 while ((dir = pop_directory ()) != NULL)
805 g_free (dir);
807 #endif /* GLIB_CHECK_VERSION */
809 /* --------------------------------------------------------------------------------------------- */
811 static void
812 insert_file (const char *dir, const char *file)
814 char *tmp_name = NULL;
815 static char *dirname = NULL;
817 while (dir[0] == PATH_SEP && dir[1] == PATH_SEP)
818 dir++;
820 if (old_dir)
822 if (strcmp (old_dir, dir))
824 g_free (old_dir);
825 old_dir = g_strdup (dir);
826 dirname = add_to_list (dir, NULL);
829 else
831 old_dir = g_strdup (dir);
832 dirname = add_to_list (dir, NULL);
835 tmp_name = g_strdup_printf (" %s", file);
836 add_to_list (tmp_name, dirname);
837 g_free (tmp_name);
840 /* --------------------------------------------------------------------------------------------- */
842 static void
843 find_add_match (const char *dir, const char *file)
845 insert_file (dir, file);
847 /* Don't scroll */
848 if (matches == 0)
849 listbox_select_first (find_list);
850 send_message (&find_list->widget, WIDGET_DRAW, 0);
852 matches++;
853 found_num_update ();
856 /* --------------------------------------------------------------------------------------------- */
858 * get_line_at:
860 * Returns malloced null-terminated line from file file_fd.
861 * Input is buffered in buf_size long buffer.
862 * Current pos in buf is stored in pos.
863 * n_read - number of read chars.
864 * has_newline - is there newline ?
867 static char *
868 get_line_at (int file_fd, char *buf, int buf_size, int *pos, int *n_read, gboolean * has_newline)
870 char *buffer = NULL;
871 int buffer_size = 0;
872 char ch = 0;
873 int i = 0;
875 for (;;)
877 if (*pos >= *n_read)
879 *pos = 0;
880 *n_read = mc_read (file_fd, buf, buf_size);
881 if (*n_read <= 0)
882 break;
885 ch = buf[(*pos)++];
886 if (ch == '\0')
888 /* skip possible leading zero(s) */
889 if (i == 0)
890 continue;
891 break;
894 if (i >= buffer_size - 1)
895 buffer = g_realloc (buffer, buffer_size += 80);
897 /* Strip newline */
898 if (ch == '\n')
899 break;
901 buffer[i++] = ch;
904 *has_newline = (ch != '\0');
906 if (buffer != NULL)
907 buffer[i] = '\0';
909 return buffer;
912 /* --------------------------------------------------------------------------------------------- */
914 static FindProgressStatus
915 check_find_events (Dlg_head * h)
917 Gpm_Event event;
918 int c;
920 event.x = -1;
921 c = tty_get_event (&event, h->mouse_status == MOU_REPEAT, FALSE);
922 if (c != EV_NONE)
924 dlg_process_event (h, c, &event);
925 if (h->ret_value == B_ENTER
926 || h->ret_value == B_CANCEL || h->ret_value == B_AGAIN || h->ret_value == B_PANELIZE)
928 /* dialog terminated */
929 return FIND_ABORT;
931 if (!(h->flags & DLG_WANT_IDLE))
933 /* searching suspended */
934 return FIND_SUSPEND;
938 return FIND_CONT;
941 /* --------------------------------------------------------------------------------------------- */
943 * search_content:
945 * Search the content_pattern string in the DIRECTORY/FILE.
946 * It will add the found entries to the find listbox.
948 * returns FALSE if do_search should look for another file
949 * TRUE if do_search should exit and proceed to the event handler
952 static gboolean
953 search_content (Dlg_head * h, const char *directory, const char *filename)
955 struct stat s;
956 char buffer[BUF_4K];
957 char *fname = NULL;
958 int file_fd;
959 gboolean ret_val = FALSE;
961 fname = concat_dir_and_file (directory, filename);
963 if (mc_stat (fname, &s) != 0 || !S_ISREG (s.st_mode))
965 g_free (fname);
966 return FALSE;
969 file_fd = mc_open (fname, O_RDONLY);
970 g_free (fname);
972 if (file_fd == -1)
973 return FALSE;
975 g_snprintf (buffer, sizeof (buffer), _("Grepping in %s"), str_trunc (filename, FIND2_X_USE));
977 status_update (buffer);
978 mc_refresh ();
980 tty_enable_interrupt_key ();
981 tty_got_interrupt ();
984 int line = 1;
985 int pos = 0;
986 int n_read = 0;
987 gboolean has_newline;
988 char *p = NULL;
989 gboolean found = FALSE;
990 gsize found_len;
991 char result[BUF_MEDIUM];
993 if (resuming)
995 /* We've been previously suspended, start from the previous position */
996 resuming = 0;
997 line = last_line;
998 pos = last_pos;
1000 while (!ret_val
1001 && (p = get_line_at (file_fd, buffer, sizeof (buffer),
1002 &pos, &n_read, &has_newline)) != NULL)
1004 if (!found /* Search in binary line once */
1005 && mc_search_run (search_content_handle,
1006 (const void *) p, 0, strlen (p), &found_len))
1008 g_snprintf (result, sizeof (result), "%d:%s", line, filename);
1009 find_add_match (directory, result);
1010 found = TRUE;
1012 g_free (p);
1014 if (found && options.content_first_hit)
1015 break;
1017 if (has_newline)
1019 found = FALSE;
1020 line++;
1023 if ((line & 0xff) == 0)
1025 FindProgressStatus res;
1026 res = check_find_events (h);
1027 switch (res)
1029 case FIND_ABORT:
1030 stop_idle (h);
1031 ret_val = TRUE;
1032 break;
1033 case FIND_SUSPEND:
1034 resuming = 1;
1035 last_line = line;
1036 last_pos = pos;
1037 ret_val = TRUE;
1038 break;
1039 default:
1040 break;
1045 tty_disable_interrupt_key ();
1046 mc_close (file_fd);
1047 return ret_val;
1050 /* --------------------------------------------------------------------------------------------- */
1053 If dir is absolute, this means we're within dir and searching file here.
1054 If dir is relative, this means we're going to add dir to the directory stack.
1056 static gboolean
1057 find_ignore_dir_search (const char *dir)
1059 if (find_ignore_dirs != NULL)
1061 const size_t dlen = strlen (dir);
1062 const unsigned char dabs = g_path_is_absolute (dir) ? 1 : 0;
1064 char **ignore_dir;
1066 for (ignore_dir = find_ignore_dirs; *ignore_dir != NULL; ignore_dir++)
1068 const size_t ilen = strlen (*ignore_dir);
1069 const unsigned char iabs = g_path_is_absolute (*ignore_dir) ? 2 : 0;
1071 /* ignore dir is too long -- skip it */
1072 if (dlen < ilen)
1073 continue;
1075 /* handle absolute and relative paths */
1076 switch (iabs | dabs)
1078 case 0: /* both paths are relative */
1079 case 3: /* both paths are abolute */
1080 /* if ignore dir is not a path of dir -- skip it */
1081 if (strncmp (dir, *ignore_dir, ilen) == 0)
1083 /* be sure that ignore dir is not a part of dir like:
1084 ignore dir is "h", dir is "home" */
1085 if (dir[ilen] == '\0' || dir[ilen] == PATH_SEP)
1086 return TRUE;
1088 break;
1089 case 1: /* dir is absolute, ignore_dir is relative */
1091 char *d;
1093 d = strstr (dir, *ignore_dir);
1094 if (d != NULL && d[-1] == PATH_SEP && (d[ilen] == '\0' || d[ilen] == PATH_SEP))
1095 return TRUE;
1097 break;
1098 case 2: /* dir is relative, ignore_dir is absolute */
1099 /* FIXME: skip this case */
1100 break;
1101 default: /* this cannot occurs */
1102 return FALSE;
1107 return FALSE;
1110 /* --------------------------------------------------------------------------------------------- */
1112 static void
1113 find_rotate_dash (const Dlg_head * h, gboolean finish)
1115 static const char rotating_dash[] = "|/-\\";
1116 static unsigned int pos = 0;
1118 if (verbose)
1120 pos = (pos + 1) % 4;
1121 tty_setcolor (h->color[DLG_COLOR_NORMAL]);
1122 dlg_move (h, FIND2_Y - 7, FIND2_X - 4);
1123 tty_print_char (finish ? ' ' : rotating_dash[pos]);
1124 mc_refresh ();
1128 /* --------------------------------------------------------------------------------------------- */
1130 static int
1131 do_search (Dlg_head * h)
1133 static struct dirent *dp = NULL;
1134 static DIR *dirp = NULL;
1135 static char *directory = NULL;
1136 struct stat tmp_stat;
1137 static int subdirs_left = 0;
1138 gsize bytes_found;
1139 unsigned short count;
1141 if (h == NULL)
1142 { /* someone forces me to close dirp */
1143 if (dirp != NULL)
1145 mc_closedir (dirp);
1146 dirp = NULL;
1148 g_free (directory);
1149 directory = NULL;
1150 dp = NULL;
1151 return 1;
1154 for (count = 0; count < 32; count++)
1156 while (dp == NULL)
1158 if (dirp != NULL)
1160 mc_closedir (dirp);
1161 dirp = NULL;
1164 while (dirp == NULL)
1166 char *tmp = NULL;
1168 tty_setcolor (REVERSE_COLOR);
1170 while (TRUE)
1172 tmp = pop_directory ();
1173 if (tmp == NULL)
1175 running = FALSE;
1176 if (ignore_count == 0)
1177 status_update (_("Finished"));
1178 else
1180 char msg[BUF_SMALL];
1181 g_snprintf (msg, sizeof (msg),
1182 ngettext ("Finished (ignored %zd directory)",
1183 "Finished (ignored %zd directories)",
1184 ignore_count), ignore_count);
1185 status_update (msg);
1187 find_rotate_dash (h, TRUE);
1188 stop_idle (h);
1189 return 0;
1192 /* handle absolute ignore dirs here */
1193 if (!find_ignore_dir_search (tmp))
1194 break;
1196 g_free (tmp);
1197 ignore_count++;
1200 g_free (directory);
1201 directory = tmp;
1203 if (verbose)
1205 char buffer[BUF_SMALL];
1207 g_snprintf (buffer, sizeof (buffer), _("Searching %s"),
1208 str_trunc (directory, FIND2_X_USE));
1209 status_update (buffer);
1211 /* mc_stat should not be called after mc_opendir
1212 because vfs_s_opendir modifies the st_nlink
1214 if (mc_stat (directory, &tmp_stat) == 0)
1215 subdirs_left = tmp_stat.st_nlink - 2;
1216 else
1217 subdirs_left = 0;
1219 dirp = mc_opendir (directory);
1220 } /* while (!dirp) */
1222 /* skip invalid filenames */
1223 while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1225 } /* while (!dp) */
1227 if (strcmp (dp->d_name, ".") == 0 || strcmp (dp->d_name, "..") == 0)
1229 /* skip invalid filenames */
1230 while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1233 return 1;
1236 if (!(options.skip_hidden && (dp->d_name[0] == '.')))
1238 gboolean search_ok;
1240 if ((subdirs_left != 0) && options.find_recurs && (directory != NULL))
1241 { /* Can directory be NULL ? */
1242 /* handle relative ignore dirs here */
1243 if (options.ignore_dirs_enable && find_ignore_dir_search (dp->d_name))
1244 ignore_count++;
1245 else
1247 char *tmp_name;
1249 tmp_name = mc_build_filename (directory, dp->d_name, (char *) NULL);
1251 if (mc_lstat (tmp_name, &tmp_stat) == 0 && S_ISDIR (tmp_stat.st_mode))
1253 push_directory (tmp_name);
1254 subdirs_left--;
1256 else
1257 g_free (tmp_name);
1261 search_ok = mc_search_run (search_file_handle, dp->d_name,
1262 0, strlen (dp->d_name), &bytes_found);
1264 if (search_ok)
1266 if (content_pattern == NULL)
1267 find_add_match (directory, dp->d_name);
1268 else if (search_content (h, directory, dp->d_name))
1269 return 1;
1273 /* skip invalid filenames */
1274 while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1276 } /* for */
1278 find_rotate_dash (h, FALSE);
1280 return 1;
1283 /* --------------------------------------------------------------------------------------------- */
1285 static void
1286 init_find_vars (void)
1288 g_free (old_dir);
1289 old_dir = NULL;
1290 matches = 0;
1291 ignore_count = 0;
1293 /* Remove all the items from the stack */
1294 clear_stack ();
1296 g_strfreev (find_ignore_dirs);
1297 find_ignore_dirs = NULL;
1300 /* --------------------------------------------------------------------------------------------- */
1302 static char *
1303 make_fullname (const char *dirname, const char *filename)
1306 if (strcmp (dirname, ".") == 0 || strcmp (dirname, "." PATH_SEP_STR) == 0)
1307 return g_strdup (filename);
1308 if (strncmp (dirname, "." PATH_SEP_STR, 2) == 0)
1309 return concat_dir_and_file (dirname + 2, filename);
1310 return concat_dir_and_file (dirname, filename);
1313 /* --------------------------------------------------------------------------------------------- */
1315 static void
1316 find_do_view_edit (int unparsed_view, int edit, char *dir, char *file)
1318 char *fullname = NULL;
1319 const char *filename = NULL;
1320 int line;
1322 if (content_pattern != NULL)
1324 filename = strchr (file + 4, ':') + 1;
1325 line = atoi (file + 4);
1327 else
1329 filename = file + 4;
1330 line = 0;
1333 fullname = make_fullname (dir, filename);
1334 if (edit)
1335 do_edit_at_line (fullname, use_internal_edit, line);
1336 else
1337 view_file_at_line (fullname, unparsed_view, use_internal_view, line);
1338 g_free (fullname);
1341 /* --------------------------------------------------------------------------------------------- */
1343 static cb_ret_t
1344 view_edit_currently_selected_file (int unparsed_view, int edit)
1346 char *dir = NULL;
1347 char *text = NULL;
1349 listbox_get_current (find_list, &text, (void **) &dir);
1351 if ((text == NULL) || (dir == NULL))
1352 return MSG_NOT_HANDLED;
1354 find_do_view_edit (unparsed_view, edit, dir, text);
1355 return MSG_HANDLED;
1358 /* --------------------------------------------------------------------------------------------- */
1360 static cb_ret_t
1361 find_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
1363 switch (msg)
1365 case DLG_KEY:
1366 if (parm == KEY_F (3) || parm == KEY_F (13))
1368 int unparsed_view = (parm == KEY_F (13));
1369 return view_edit_currently_selected_file (unparsed_view, 0);
1371 if (parm == KEY_F (4))
1373 return view_edit_currently_selected_file (0, 1);
1375 return MSG_NOT_HANDLED;
1377 case DLG_IDLE:
1378 do_search (h);
1379 return MSG_HANDLED;
1381 default:
1382 return default_dlg_callback (h, sender, msg, parm, data);
1386 /* --------------------------------------------------------------------------------------------- */
1387 /** Handles the Stop/Start button in the find window */
1389 static int
1390 start_stop (WButton * button, int action)
1392 (void) button;
1393 (void) action;
1395 running = is_start;
1396 set_idle_proc (find_dlg, running);
1397 is_start = !is_start;
1399 status_update (is_start ? _("Stopped") : _("Searching"));
1400 button_set_text (stop_button, fbuts[is_start ? 1 : 0].text);
1402 return 0;
1405 /* --------------------------------------------------------------------------------------------- */
1406 /** Handle view command, when invoked as a button */
1408 static int
1409 find_do_view_file (WButton * button, int action)
1411 (void) button;
1412 (void) action;
1414 view_edit_currently_selected_file (0, 0);
1415 return 0;
1418 /* --------------------------------------------------------------------------------------------- */
1419 /** Handle edit command, when invoked as a button */
1421 static int
1422 find_do_edit_file (WButton * button, int action)
1424 (void) button;
1425 (void) action;
1427 view_edit_currently_selected_file (0, 1);
1428 return 0;
1431 /* --------------------------------------------------------------------------------------------- */
1433 static void
1434 setup_gui (void)
1436 #ifdef ENABLE_NLS
1437 static gboolean i18n_flag = FALSE;
1439 if (!i18n_flag)
1441 int i = sizeof (fbuts) / sizeof (fbuts[0]);
1442 while (i-- != 0)
1444 fbuts[i].text = _(fbuts[i].text);
1445 fbuts[i].len = str_term_width1 (fbuts[i].text) + 3;
1448 fbuts[2].len += 2; /* DEFPUSH_BUTTON */
1449 i18n_flag = TRUE;
1451 #endif /* ENABLE_NLS */
1454 * Dynamically place buttons centered within current window size
1457 int l0 = max (fbuts[0].len, fbuts[1].len);
1458 int l1 = fbuts[2].len + fbuts[3].len + l0 + fbuts[4].len;
1459 int l2 = fbuts[5].len + fbuts[6].len + fbuts[7].len;
1460 int r1, r2;
1462 /* Check, if both button rows fit within FIND2_X */
1463 FIND2_X = max (l1 + 9, COLS - 16);
1464 FIND2_X = max (l2 + 8, FIND2_X);
1466 /* compute amount of space between buttons for each row */
1467 r1 = (FIND2_X - 4 - l1) % 5;
1468 l1 = (FIND2_X - 4 - l1) / 5;
1469 r2 = (FIND2_X - 4 - l2) % 4;
1470 l2 = (FIND2_X - 4 - l2) / 4;
1472 /* ...and finally, place buttons */
1473 fbuts[2].x = 2 + r1 / 2 + l1;
1474 fbuts[3].x = fbuts[2].x + fbuts[2].len + l1;
1475 fbuts[0].x = fbuts[3].x + fbuts[3].len + l1;
1476 fbuts[4].x = fbuts[0].x + l0 + l1;
1477 fbuts[5].x = 2 + r2 / 2 + l2;
1478 fbuts[6].x = fbuts[5].x + fbuts[5].len + l2;
1479 fbuts[7].x = fbuts[6].x + fbuts[6].len + l2;
1482 find_dlg =
1483 create_dlg (TRUE, 0, 0, FIND2_Y, FIND2_X, dialog_colors, find_callback,
1484 "[Find File]", _("Find File"), DLG_CENTER | DLG_REVERSE);
1486 add_widget (find_dlg,
1487 button_new (FIND2_Y - 3, fbuts[7].x, B_VIEW, NORMAL_BUTTON,
1488 fbuts[7].text, find_do_edit_file));
1489 add_widget (find_dlg,
1490 button_new (FIND2_Y - 3, fbuts[6].x, B_VIEW, NORMAL_BUTTON,
1491 fbuts[6].text, find_do_view_file));
1492 add_widget (find_dlg,
1493 button_new (FIND2_Y - 3, fbuts[5].x, B_PANELIZE, NORMAL_BUTTON, fbuts[5].text,
1494 NULL));
1496 add_widget (find_dlg,
1497 button_new (FIND2_Y - 4, fbuts[4].x, B_CANCEL, NORMAL_BUTTON, fbuts[4].text, NULL));
1498 stop_button =
1499 button_new (FIND2_Y - 4, fbuts[0].x, B_STOP, NORMAL_BUTTON, fbuts[0].text, start_stop);
1500 add_widget (find_dlg, stop_button);
1501 add_widget (find_dlg,
1502 button_new (FIND2_Y - 4, fbuts[3].x, B_AGAIN, NORMAL_BUTTON, fbuts[3].text, NULL));
1503 add_widget (find_dlg,
1504 button_new (FIND2_Y - 4, fbuts[2].x, B_ENTER, DEFPUSH_BUTTON, fbuts[2].text, NULL));
1506 status_label = label_new (FIND2_Y - 7, 4, _("Searching"));
1507 add_widget (find_dlg, status_label);
1509 found_num_label = label_new (FIND2_Y - 6, 4, "");
1510 add_widget (find_dlg, found_num_label);
1512 find_list = listbox_new (2, 2, FIND2_Y - 10, FIND2_X - 4, FALSE, NULL);
1513 add_widget (find_dlg, find_list);
1516 /* --------------------------------------------------------------------------------------------- */
1518 static int
1519 run_process (void)
1521 int ret;
1523 search_content_handle = mc_search_new (content_pattern, -1);
1524 if (search_content_handle)
1526 search_content_handle->search_type =
1527 options.content_regexp ? MC_SEARCH_T_REGEX : MC_SEARCH_T_NORMAL;
1528 search_content_handle->is_case_sensitive = options.content_case_sens;
1529 search_content_handle->whole_words = options.content_whole_words;
1530 search_content_handle->is_all_charsets = options.content_all_charsets;
1532 search_file_handle = mc_search_new (find_pattern, -1);
1533 search_file_handle->search_type = options.file_pattern ? MC_SEARCH_T_GLOB : MC_SEARCH_T_REGEX;
1534 search_file_handle->is_case_sensitive = options.file_case_sens;
1535 search_file_handle->is_all_charsets = options.file_all_charsets;
1536 search_file_handle->is_entire_line = options.file_pattern;
1538 resuming = 0;
1540 set_idle_proc (find_dlg, 1);
1541 ret = run_dlg (find_dlg);
1543 mc_search_free (search_file_handle);
1544 search_file_handle = NULL;
1545 mc_search_free (search_content_handle);
1546 search_content_handle = NULL;
1548 return ret;
1551 /* --------------------------------------------------------------------------------------------- */
1553 static void
1554 kill_gui (void)
1556 set_idle_proc (find_dlg, 0);
1557 destroy_dlg (find_dlg);
1560 /* --------------------------------------------------------------------------------------------- */
1562 static int
1563 find_file (const char *start_dir, const char *ignore_dirs, const char *pattern, const char *content,
1564 char **dirname, char **filename)
1566 int return_value = 0;
1567 char *dir_tmp = NULL, *file_tmp = NULL;
1569 setup_gui ();
1571 /* FIXME: Need to cleanup this, this ought to be passed non-globaly */
1572 find_pattern = (char *) pattern;
1574 content_pattern = NULL;
1575 if (options.content_use && content != NULL && str_is_valid_string (content))
1576 content_pattern = g_strdup (content);
1578 init_find_vars ();
1579 parse_ignore_dirs (ignore_dirs);
1580 push_directory (start_dir);
1582 return_value = run_process ();
1584 /* Clear variables */
1585 init_find_vars ();
1587 get_list_info (&file_tmp, &dir_tmp);
1589 if (dir_tmp)
1590 *dirname = g_strdup (dir_tmp);
1591 if (file_tmp)
1592 *filename = g_strdup (file_tmp);
1594 if (return_value == B_PANELIZE && *filename)
1596 int status, link_to_dir, stale_link;
1597 int next_free = 0;
1598 int i;
1599 struct stat st;
1600 GList *entry;
1601 dir_list *list = &current_panel->dir;
1602 char *name = NULL;
1604 for (i = 0, entry = find_list->list; entry != NULL; i++, entry = g_list_next (entry))
1606 const char *lc_filename = NULL;
1607 WLEntry *le = (WLEntry *) entry->data;
1609 if ((le->text == NULL) || (le->data == NULL))
1610 continue;
1612 if (content_pattern != NULL)
1613 lc_filename = strchr (le->text + 4, ':') + 1;
1614 else
1615 lc_filename = le->text + 4;
1617 name = make_fullname (le->data, lc_filename);
1618 status = handle_path (list, name, &st, next_free, &link_to_dir, &stale_link);
1619 if (status == 0)
1621 g_free (name);
1622 continue;
1624 if (status == -1)
1626 g_free (name);
1627 break;
1630 /* don't add files more than once to the panel */
1631 if (content_pattern != NULL && next_free > 0
1632 && strcmp (list->list[next_free - 1].fname, name) == 0)
1634 g_free (name);
1635 continue;
1638 if (!next_free) /* first turn i.e clean old list */
1639 panel_clean_dir (current_panel);
1640 list->list[next_free].fnamelen = strlen (name);
1641 list->list[next_free].fname = name;
1642 list->list[next_free].f.marked = 0;
1643 list->list[next_free].f.link_to_dir = link_to_dir;
1644 list->list[next_free].f.stale_link = stale_link;
1645 list->list[next_free].f.dir_size_computed = 0;
1646 list->list[next_free].st = st;
1647 list->list[next_free].sort_key = NULL;
1648 list->list[next_free].second_sort_key = NULL;
1649 next_free++;
1650 if (!(next_free & 15))
1651 rotate_dash ();
1654 if (next_free)
1656 current_panel->count = next_free;
1657 current_panel->is_panelized = 1;
1659 if (start_dir[0] == PATH_SEP)
1661 int ret;
1662 strcpy (current_panel->cwd, PATH_SEP_STR);
1663 ret = chdir (PATH_SEP_STR);
1668 g_free (content_pattern);
1669 kill_gui ();
1670 do_search (NULL); /* force do_search to release resources */
1671 g_free (old_dir);
1672 old_dir = NULL;
1674 return return_value;
1677 /* --------------------------------------------------------------------------------------------- */
1678 /*** public functions ****************************************************************************/
1679 /* --------------------------------------------------------------------------------------------- */
1681 void
1682 do_find (void)
1684 char *start_dir = NULL, *pattern = NULL, *content = NULL, *ignore_dirs = NULL;
1685 char *filename = NULL, *dirname = NULL;
1686 int v;
1687 gboolean dir_and_file_set;
1689 while (find_parameters (&start_dir, &ignore_dirs, &pattern, &content))
1691 if (pattern[0] == '\0')
1692 break; /* nothing search */
1694 dirname = filename = NULL;
1695 is_start = FALSE;
1696 v = find_file (start_dir, ignore_dirs, pattern, content, &dirname, &filename);
1697 g_free (ignore_dirs);
1698 g_free (pattern);
1700 if (v == B_ENTER)
1702 if (dirname != NULL)
1704 do_cd (dirname, cd_exact);
1705 if (filename != NULL)
1706 try_to_select (current_panel,
1707 filename + (content != NULL
1708 ? strchr (filename + 4, ':') - filename + 1 : 4));
1710 else if (filename != NULL)
1711 do_cd (filename, cd_exact);
1713 g_free (dirname);
1714 g_free (filename);
1715 break;
1718 g_free (content);
1719 dir_and_file_set = (dirname != NULL) && (filename != NULL);
1720 g_free (dirname);
1721 g_free (filename);
1723 if (v == B_CANCEL)
1724 break;
1726 if (v == B_PANELIZE)
1728 if (dir_and_file_set)
1730 try_to_select (current_panel, NULL);
1731 panel_re_sort (current_panel);
1732 try_to_select (current_panel, NULL);
1734 break;
1739 /* --------------------------------------------------------------------------------------------- */