Changed interface of function mc_opendir()
[midnight-commander.git] / src / filemanager / find.c
blob640ac2be8789afab6e62ba086b386b98cafa838a
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 */
54 #include "src/main.h" /* do_cd */
56 #include "dir.h"
57 #include "cmd.h" /* view_file_at_line */
58 #include "midnight.h" /* current_panel */
59 #include "boxes.h"
60 #include "panelize.h"
62 #include "find.h"
64 /*** global variables ****************************************************************************/
66 /*** file scope macro definitions ****************************************************************/
68 /* Size of the find window */
69 #define FIND2_Y (LINES - 4)
71 #define FIND2_X_USE (FIND2_X - 20)
73 /*** file scope type declarations ****************************************************************/
75 /* A couple of extra messages we need */
76 enum
78 B_STOP = B_USER + 1,
79 B_AGAIN,
80 B_PANELIZE,
81 B_TREE,
82 B_VIEW
85 typedef enum
87 FIND_CONT = 0,
88 FIND_SUSPEND,
89 FIND_ABORT
90 } FindProgressStatus;
92 /* find file options */
93 typedef struct
95 /* file name options */
96 gboolean file_case_sens;
97 gboolean file_pattern;
98 gboolean find_recurs;
99 gboolean skip_hidden;
100 gboolean file_all_charsets;
102 /* file content options */
103 gboolean content_use;
104 gboolean content_case_sens;
105 gboolean content_regexp;
106 gboolean content_first_hit;
107 gboolean content_whole_words;
108 gboolean content_all_charsets;
110 /* whether use ignore dirs or not */
111 gboolean ignore_dirs_enable;
112 /* list of directories to be ignored, separated by ':' */
113 char *ignore_dirs;
114 } find_file_options_t;
116 /*** file scope variables ************************************************************************/
118 /* Parsed ignore dirs */
119 static char **find_ignore_dirs = NULL;
121 /* Size of the find parameters window */
122 #ifdef HAVE_CHARSET
123 static int FIND_Y = 19;
124 #else
125 static int FIND_Y = 18;
126 #endif
127 static int FIND_X = 68;
129 static int FIND2_X = 64;
131 /* static variables to remember find parameters */
132 static WInput *in_start; /* Start path */
133 static WInput *in_name; /* Filename */
134 static WInput *in_with; /* Text */
135 static WInput *in_ignore;
136 static WLabel *content_label; /* 'Content:' label */
137 static WCheck *file_case_sens_cbox; /* "case sensitive" checkbox */
138 static WCheck *file_pattern_cbox; /* File name is glob or regexp */
139 static WCheck *recursively_cbox;
140 static WCheck *skip_hidden_cbox;
141 static WCheck *content_use_cbox; /* Take into account the Content field */
142 static WCheck *content_case_sens_cbox; /* "case sensitive" checkbox */
143 static WCheck *content_regexp_cbox; /* "find regular expression" checkbox */
144 static WCheck *content_first_hit_cbox; /* "First hit" checkbox" */
145 static WCheck *content_whole_words_cbox; /* "whole words" checkbox */
146 #ifdef HAVE_CHARSET
147 static WCheck *file_all_charsets_cbox;
148 static WCheck *content_all_charsets_cbox;
149 #endif
150 static WCheck *ignore_dirs_cbox;
152 static gboolean running = FALSE; /* nice flag */
153 static char *find_pattern = NULL; /* Pattern to search */
154 static char *content_pattern = NULL; /* pattern to search inside files; if
155 content_regexp_flag is true, it contains the
156 regex pattern, else the search string. */
157 static unsigned long matches; /* Number of matches */
158 static gboolean is_start = FALSE; /* Status of the start/stop toggle button */
159 static char *old_dir = NULL;
161 /* Where did we stop */
162 static int resuming;
163 static int last_line;
164 static int last_pos;
166 static size_t ignore_count = 0;
168 static Dlg_head *find_dlg; /* The dialog */
169 static WButton *stop_button; /* pointer to the stop button */
170 static WLabel *status_label; /* Finished, Searching etc. */
171 static WLabel *found_num_label; /* Number of found items */
172 static WListbox *find_list; /* Listbox with the file list */
174 /* This keeps track of the directory stack */
175 #if GLIB_CHECK_VERSION (2, 14, 0)
176 static GQueue dir_queue = G_QUEUE_INIT;
177 #else
178 typedef struct dir_stack
180 char *name;
181 struct dir_stack *prev;
182 } dir_stack;
184 static dir_stack *dir_stack_base = 0;
185 #endif /* GLIB_CHECK_VERSION */
187 /* *INDENT-OFF* */
188 static struct
190 const char *text;
191 int len; /* length including space and brackets */
192 int x;
193 } fbuts[] =
195 {N_("&Suspend"), 11, 29},
196 {N_("Con&tinue"), 12, 29},
197 {N_("&Chdir"), 11, 3},
198 {N_("&Again"), 9, 17},
199 {N_("&Quit"), 8, 43},
200 {N_("Pane&lize"), 12, 3},
201 {N_("&View - F3"), 13, 20},
202 {N_("&Edit - F4"), 13, 38}
204 /* *INDENT-ON* */
206 static find_file_options_t options = {
207 TRUE, TRUE, TRUE, FALSE, FALSE,
208 FALSE, TRUE, FALSE, FALSE, FALSE, FALSE
211 static char *in_start_dir = INPUT_LAST_TEXT;
213 static mc_search_t *search_file_handle = NULL;
214 static mc_search_t *search_content_handle = NULL;
216 /*** file scope functions ************************************************************************/
218 static void
219 parse_ignore_dirs (const char *ignore_dirs)
221 size_t r = 0, w = 0; /* read and write iterators */
223 if (!options.ignore_dirs_enable || ignore_dirs == NULL || ignore_dirs[0] == '\0')
224 return;
226 find_ignore_dirs = g_strsplit (ignore_dirs, ":", -1);
228 /* Values like '/foo::/bar: produce holes in list.
229 * Find and remove them */
230 for (; find_ignore_dirs[r] != NULL; r++)
232 if (find_ignore_dirs[r][0] == '\0')
234 /* empty entry -- skip it */
235 g_free (find_ignore_dirs[r]);
236 find_ignore_dirs[r] = NULL;
237 continue;
240 if (r != w)
242 /* copy entry to the previous free array cell */
243 find_ignore_dirs[w] = find_ignore_dirs[r];
244 find_ignore_dirs[r] = NULL;
247 canonicalize_pathname (find_ignore_dirs[w]);
248 if (find_ignore_dirs[w][0] != '\0')
249 w++;
250 else
252 g_free (find_ignore_dirs[w]);
253 find_ignore_dirs[w] = NULL;
257 if (find_ignore_dirs[0] == NULL)
259 g_strfreev (find_ignore_dirs);
260 find_ignore_dirs = NULL;
264 /* --------------------------------------------------------------------------------------------- */
266 static void
267 find_load_options (void)
269 static gboolean loaded = FALSE;
271 if (loaded)
272 return;
274 loaded = TRUE;
276 options.file_case_sens =
277 mc_config_get_bool (mc_main_config, "FindFile", "file_case_sens", TRUE);
278 options.file_pattern =
279 mc_config_get_bool (mc_main_config, "FindFile", "file_shell_pattern", TRUE);
280 options.find_recurs = mc_config_get_bool (mc_main_config, "FindFile", "file_find_recurs", TRUE);
281 options.skip_hidden =
282 mc_config_get_bool (mc_main_config, "FindFile", "file_skip_hidden", FALSE);
283 options.file_all_charsets =
284 mc_config_get_bool (mc_main_config, "FindFile", "file_all_charsets", FALSE);
285 options.content_use = mc_config_get_bool (mc_main_config, "FindFile", "content_use", TRUE);
286 options.content_case_sens =
287 mc_config_get_bool (mc_main_config, "FindFile", "content_case_sens", TRUE);
288 options.content_regexp =
289 mc_config_get_bool (mc_main_config, "FindFile", "content_regexp", FALSE);
290 options.content_first_hit =
291 mc_config_get_bool (mc_main_config, "FindFile", "content_first_hit", FALSE);
292 options.content_whole_words =
293 mc_config_get_bool (mc_main_config, "FindFile", "content_whole_words", FALSE);
294 options.content_all_charsets =
295 mc_config_get_bool (mc_main_config, "FindFile", "content_all_charsets", FALSE);
296 options.ignore_dirs_enable =
297 mc_config_get_bool (mc_main_config, "FindFile", "ignore_dirs_enable", TRUE);
298 options.ignore_dirs = mc_config_get_string (mc_main_config, "FindFile", "ignore_dirs", "");
300 if (options.ignore_dirs[0] == '\0')
302 g_free (options.ignore_dirs);
303 options.ignore_dirs = NULL;
307 /* --------------------------------------------------------------------------------------------- */
309 static void
310 find_save_options (void)
312 mc_config_set_bool (mc_main_config, "FindFile", "file_case_sens", options.file_case_sens);
313 mc_config_set_bool (mc_main_config, "FindFile", "file_shell_pattern", options.file_pattern);
314 mc_config_set_bool (mc_main_config, "FindFile", "file_find_recurs", options.find_recurs);
315 mc_config_set_bool (mc_main_config, "FindFile", "file_skip_hidden", options.skip_hidden);
316 mc_config_set_bool (mc_main_config, "FindFile", "file_all_charsets", options.file_all_charsets);
317 mc_config_set_bool (mc_main_config, "FindFile", "content_use", options.content_use);
318 mc_config_set_bool (mc_main_config, "FindFile", "content_case_sens", options.content_case_sens);
319 mc_config_set_bool (mc_main_config, "FindFile", "content_regexp", options.content_regexp);
320 mc_config_set_bool (mc_main_config, "FindFile", "content_first_hit", options.content_first_hit);
321 mc_config_set_bool (mc_main_config, "FindFile", "content_whole_words",
322 options.content_whole_words);
323 mc_config_set_bool (mc_main_config, "FindFile", "content_all_charsets",
324 options.content_all_charsets);
325 mc_config_set_bool (mc_main_config, "FindFile", "ignore_dirs_enable",
326 options.ignore_dirs_enable);
327 mc_config_set_string (mc_main_config, "FindFile", "ignore_dirs", options.ignore_dirs);
330 /* --------------------------------------------------------------------------------------------- */
332 static inline char *
333 add_to_list (const char *text, void *data)
335 return listbox_add_item (find_list, LISTBOX_APPEND_AT_END, 0, text, data);
338 /* --------------------------------------------------------------------------------------------- */
340 static inline void
341 stop_idle (void *data)
343 set_idle_proc (data, 0);
346 /* --------------------------------------------------------------------------------------------- */
348 static inline void
349 status_update (const char *text)
351 label_set_text (status_label, text);
354 /* --------------------------------------------------------------------------------------------- */
356 static void
357 found_num_update (void)
359 char buffer[BUF_TINY];
360 g_snprintf (buffer, sizeof (buffer), _("Found: %ld"), matches);
361 label_set_text (found_num_label, buffer);
364 /* --------------------------------------------------------------------------------------------- */
366 static void
367 get_list_info (char **file, char **dir)
369 listbox_get_current (find_list, file, (void **) dir);
372 /* --------------------------------------------------------------------------------------------- */
373 /** check regular expression */
375 static gboolean
376 find_check_regexp (const char *r)
378 mc_search_t *search;
379 gboolean regexp_ok = FALSE;
381 search = mc_search_new (r, -1);
383 if (search != NULL)
385 search->search_type = MC_SEARCH_T_REGEX;
386 regexp_ok = mc_search_prepare (search);
387 mc_search_free (search);
390 return regexp_ok;
393 /* --------------------------------------------------------------------------------------------- */
395 * Callback for the parameter dialog.
396 * Validate regex, prevent closing the dialog if it's invalid.
399 static cb_ret_t
400 find_parm_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
402 switch (msg)
404 case DLG_ACTION:
405 if (sender == (Widget *) content_use_cbox)
407 gboolean disable = !(content_use_cbox->state & C_BOOL);
409 widget_disable (content_label->widget, disable);
410 send_message ((Widget *) content_label, WIDGET_DRAW, 0);
411 widget_disable (in_with->widget, disable);
412 send_message ((Widget *) in_with, WIDGET_DRAW, 0);
413 widget_disable (content_first_hit_cbox->widget, disable);
414 send_message ((Widget *) content_first_hit_cbox, WIDGET_DRAW, 0);
415 widget_disable (content_regexp_cbox->widget, disable);
416 send_message ((Widget *) content_regexp_cbox, WIDGET_DRAW, 0);
417 widget_disable (content_case_sens_cbox->widget, disable);
418 send_message ((Widget *) content_case_sens_cbox, WIDGET_DRAW, 0);
419 #ifdef HAVE_CHARSET
420 widget_disable (content_all_charsets_cbox->widget, disable);
421 send_message ((Widget *) content_all_charsets_cbox, WIDGET_DRAW, 0);
422 #endif
423 widget_disable (content_whole_words_cbox->widget, disable);
424 send_message ((Widget *) content_whole_words_cbox, WIDGET_DRAW, 0);
426 return MSG_HANDLED;
429 if (sender == (Widget *) ignore_dirs_cbox)
431 gboolean disable = !(ignore_dirs_cbox->state & C_BOOL);
433 widget_disable (in_ignore->widget, disable);
434 send_message ((Widget *) in_ignore, WIDGET_DRAW, 0);
436 return MSG_HANDLED;
439 return MSG_NOT_HANDLED;
442 case DLG_VALIDATE:
443 if (h->ret_value != B_ENTER)
444 return MSG_HANDLED;
446 /* check filename regexp */
447 if (!(file_pattern_cbox->state & C_BOOL)
448 && (in_name->buffer[0] != '\0') && !find_check_regexp (in_name->buffer))
450 h->state = DLG_ACTIVE; /* Don't stop the dialog */
451 message (D_ERROR, MSG_ERROR, _("Malformed regular expression"));
452 dlg_select_widget (in_name);
453 return MSG_HANDLED;
456 /* check content regexp */
457 if ((content_use_cbox->state & C_BOOL) && (content_regexp_cbox->state & C_BOOL)
458 && (in_with->buffer[0] != '\0') && !find_check_regexp (in_with->buffer))
460 h->state = DLG_ACTIVE; /* Don't stop the dialog */
461 message (D_ERROR, MSG_ERROR, _("Malformed regular expression"));
462 dlg_select_widget (in_with);
463 return MSG_HANDLED;
466 return MSG_HANDLED;
468 default:
469 return default_dlg_callback (h, sender, msg, parm, data);
473 /* --------------------------------------------------------------------------------------------- */
475 * find_parameters: gets information from the user
477 * If the return value is TRUE, then the following holds:
479 * start_dir, ignore_dirs, pattern and content contain the information provided by the user.
480 * They are newly allocated strings and must be freed when uneeded.
482 * start_dir_len is -1 when user entered an absolute path, otherwise it is a length
483 * of start_dir (which is absolute). It is used to get a relative pats of find results.
486 static gboolean
487 find_parameters (char **start_dir, ssize_t * start_dir_len,
488 char **ignore_dirs, char **pattern, char **content)
490 gboolean return_value;
492 /* file name */
493 const char *file_case_label = N_("Cas&e sensitive");
494 const char *file_pattern_label = N_("&Using shell patterns");
495 const char *file_recurs_label = N_("&Find recursively");
496 const char *file_skip_hidden_label = N_("S&kip hidden");
497 #ifdef HAVE_CHARSET
498 const char *file_all_charsets_label = N_("&All charsets");
499 #endif
501 /* file content */
502 const char *content_use_label = N_("Sea&rch for content");
503 const char *content_case_label = N_("Case sens&itive");
504 const char *content_regexp_label = N_("Re&gular expression");
505 const char *content_first_hit_label = N_("Fir&st hit");
506 const char *content_whole_words_label = N_("&Whole words");
507 #ifdef HAVE_CHARSET
508 const char *content_all_charsets_label = N_("A&ll charsets");
509 #endif
511 const char *buts[] = { N_("&OK"), N_("&Cancel"), N_("&Tree") };
513 int b0, b1, b2;
515 int cbox_position;
516 gboolean disable;
518 #ifdef ENABLE_NLS
520 int i = sizeof (buts) / sizeof (buts[0]);
521 while (i-- != 0)
522 buts[i] = _(buts[i]);
524 file_case_label = _(file_case_label);
525 file_pattern_label = _(file_pattern_label);
526 file_recurs_label = _(file_recurs_label);
527 file_skip_hidden_label = _(file_skip_hidden_label);
528 #ifdef HAVE_CHARSET
529 file_all_charsets_label = _(file_all_charsets_label);
530 content_all_charsets_label = _(content_all_charsets_label);
531 #endif
532 content_use_label = _(content_use_label);
533 content_case_label = _(content_case_label);
534 content_regexp_label = _(content_regexp_label);
535 content_first_hit_label = _(content_first_hit_label);
536 content_whole_words_label = _(content_whole_words_label);
538 #endif /* ENABLE_NLS */
540 b0 = str_term_width1 (buts[0]) + 6; /* default button */
541 b1 = str_term_width1 (buts[1]) + 4;
542 b2 = str_term_width1 (buts[2]) + 4;
544 find_load_options ();
546 if (in_start_dir == NULL)
547 in_start_dir = g_strdup (".");
549 disable = !options.content_use;
551 find_dlg =
552 create_dlg (TRUE, 0, 0, FIND_Y, FIND_X, dialog_colors,
553 find_parm_callback, "[Find File]", _("Find File"), DLG_CENTER | DLG_REVERSE);
555 add_widget (find_dlg,
556 button_new (FIND_Y - 3, FIND_X * 3 / 4 - b1 / 2, B_CANCEL, NORMAL_BUTTON, buts[1],
557 0));
558 add_widget (find_dlg,
559 button_new (FIND_Y - 3, FIND_X / 4 - b0 / 2, B_ENTER, DEFPUSH_BUTTON, buts[0], 0));
561 cbox_position = FIND_Y - 5;
563 content_first_hit_cbox =
564 check_new (cbox_position--, FIND_X / 2 + 1, options.content_first_hit,
565 content_first_hit_label);
566 widget_disable (content_first_hit_cbox->widget, disable);
567 add_widget (find_dlg, content_first_hit_cbox);
569 content_whole_words_cbox =
570 check_new (cbox_position--, FIND_X / 2 + 1, options.content_whole_words,
571 content_whole_words_label);
572 widget_disable (content_whole_words_cbox->widget, disable);
573 add_widget (find_dlg, content_whole_words_cbox);
575 #ifdef HAVE_CHARSET
576 content_all_charsets_cbox = check_new (cbox_position--, FIND_X / 2 + 1,
577 options.content_all_charsets,
578 content_all_charsets_label);
579 widget_disable (content_all_charsets_cbox->widget, disable);
580 add_widget (find_dlg, content_all_charsets_cbox);
581 #endif
583 content_case_sens_cbox =
584 check_new (cbox_position--, FIND_X / 2 + 1, options.content_case_sens, content_case_label);
585 widget_disable (content_case_sens_cbox->widget, disable);
586 add_widget (find_dlg, content_case_sens_cbox);
588 content_regexp_cbox =
589 check_new (cbox_position--, FIND_X / 2 + 1, options.content_regexp, content_regexp_label);
590 widget_disable (content_regexp_cbox->widget, disable);
591 add_widget (find_dlg, content_regexp_cbox);
593 cbox_position = FIND_Y - 6;
595 skip_hidden_cbox = check_new (cbox_position--, 3, options.skip_hidden, file_skip_hidden_label);
596 add_widget (find_dlg, skip_hidden_cbox);
598 #ifdef HAVE_CHARSET
599 file_all_charsets_cbox =
600 check_new (cbox_position--, 3, options.file_all_charsets, file_all_charsets_label);
601 add_widget (find_dlg, file_all_charsets_cbox);
602 #endif
604 file_case_sens_cbox = check_new (cbox_position--, 3, options.file_case_sens, file_case_label);
605 add_widget (find_dlg, file_case_sens_cbox);
607 file_pattern_cbox = check_new (cbox_position--, 3, options.file_pattern, file_pattern_label);
608 add_widget (find_dlg, file_pattern_cbox);
610 recursively_cbox = check_new (cbox_position, 3, options.find_recurs, file_recurs_label);
611 add_widget (find_dlg, recursively_cbox);
613 /* This checkbox is located in the second column */
614 content_use_cbox =
615 check_new (cbox_position, FIND_X / 2 + 1, options.content_use, content_use_label);
616 add_widget (find_dlg, content_use_cbox);
618 in_with =
619 input_new (8, FIND_X / 2 + 1, input_get_default_colors (), FIND_X / 2 - 4, INPUT_LAST_TEXT,
620 MC_HISTORY_SHARED_SEARCH, INPUT_COMPLETE_DEFAULT);
621 widget_disable (in_with->widget, disable);
622 add_widget (find_dlg, in_with);
624 content_label = label_new (7, FIND_X / 2 + 1, _("Content:"));
625 widget_disable (content_label->widget, disable);
626 add_widget (find_dlg, content_label);
628 in_name = input_new (8, 3, input_get_default_colors (),
629 FIND_X / 2 - 4, INPUT_LAST_TEXT, "name", INPUT_COMPLETE_DEFAULT);
630 add_widget (find_dlg, in_name);
631 add_widget (find_dlg, label_new (7, 3, _("File name:")));
633 in_ignore = input_new (5, 3, input_get_default_colors (), FIND_X - 6,
634 options.ignore_dirs != NULL ? options.ignore_dirs : "",
635 "ignoredirs", INPUT_COMPLETE_DEFAULT);
636 widget_disable (in_ignore->widget, !options.ignore_dirs_enable);
637 add_widget (find_dlg, in_ignore);
639 ignore_dirs_cbox =
640 check_new (4, 3, options.ignore_dirs_enable, _("Ena&ble ignore directories:"));
641 add_widget (find_dlg, ignore_dirs_cbox);
643 add_widget (find_dlg, button_new (3, FIND_X - b2 - 2, B_TREE, NORMAL_BUTTON, buts[2], 0));
645 in_start = input_new (3, 3, input_get_default_colors (),
646 FIND_X - b2 - 6, in_start_dir, "start", INPUT_COMPLETE_DEFAULT);
647 add_widget (find_dlg, in_start);
648 add_widget (find_dlg, label_new (2, 3, _("Start at:")));
650 find_par_start:
651 dlg_select_widget (in_name);
653 switch (run_dlg (find_dlg))
655 case B_CANCEL:
656 return_value = FALSE;
657 break;
659 case B_TREE:
661 const char *temp_dir = in_start->buffer;
663 if ((temp_dir[0] == '\0') || ((temp_dir[0] == '.') && (temp_dir[1] == '\0')))
664 temp_dir = current_panel->cwd;
666 if (in_start_dir != INPUT_LAST_TEXT)
667 g_free (in_start_dir);
668 in_start_dir = tree_box (temp_dir);
669 if (in_start_dir == NULL)
670 in_start_dir = g_strdup (temp_dir);
672 input_assign_text (in_start, in_start_dir);
674 /* Warning: Dreadful goto */
675 goto find_par_start;
678 default:
680 char *s;
682 #ifdef HAVE_CHARSET
683 options.file_all_charsets = file_all_charsets_cbox->state & C_BOOL;
684 options.content_all_charsets = content_all_charsets_cbox->state & C_BOOL;
685 #endif
686 options.content_use = content_use_cbox->state & C_BOOL;
687 options.content_case_sens = content_case_sens_cbox->state & C_BOOL;
688 options.content_regexp = content_regexp_cbox->state & C_BOOL;
689 options.content_first_hit = content_first_hit_cbox->state & C_BOOL;
690 options.content_whole_words = content_whole_words_cbox->state & C_BOOL;
691 options.find_recurs = recursively_cbox->state & C_BOOL;
692 options.file_pattern = file_pattern_cbox->state & C_BOOL;
693 options.file_case_sens = file_case_sens_cbox->state & C_BOOL;
694 options.skip_hidden = skip_hidden_cbox->state & C_BOOL;
695 options.ignore_dirs_enable = ignore_dirs_cbox->state & C_BOOL;
696 g_free (options.ignore_dirs);
697 options.ignore_dirs = g_strdup (in_ignore->buffer);
699 *content = (options.content_use && in_with->buffer[0] != '\0')
700 ? g_strdup (in_with->buffer) : NULL;
701 *start_dir = in_start->buffer[0] != '\0' ? in_start->buffer : (char *) ".";
702 *pattern = g_strdup (in_name->buffer);
703 if (in_start_dir != INPUT_LAST_TEXT)
704 g_free (in_start_dir);
705 in_start_dir = g_strdup (*start_dir);
707 s = tilde_expand (*start_dir);
708 canonicalize_pathname (s);
710 if (s[0] == '.' && s[1] == '\0')
712 *start_dir = g_strdup (current_panel->cwd);
713 /* FIXME: is current_panel->cwd canonicalized? */
714 /* relative paths will be used in panelization */
715 *start_dir_len = (ssize_t) strlen (current_panel->cwd);
716 g_free (s);
718 else if (g_path_is_absolute (s))
720 *start_dir = s;
721 *start_dir_len = -1;
723 else
725 /* relative paths will be used in panelization */
726 *start_dir = mc_build_filename (current_panel->cwd, s, (char *) NULL);
727 *start_dir_len = (ssize_t) strlen (current_panel->cwd);
728 g_free (s);
731 if (!options.ignore_dirs_enable || in_ignore->buffer[0] == '\0'
732 || (in_ignore->buffer[0] == '.' && in_ignore->buffer[1] == '\0'))
733 *ignore_dirs = NULL;
734 else
735 *ignore_dirs = g_strdup (in_ignore->buffer);
737 find_save_options ();
739 return_value = TRUE;
743 destroy_dlg (find_dlg);
745 return return_value;
748 /* --------------------------------------------------------------------------------------------- */
750 #if GLIB_CHECK_VERSION (2, 14, 0)
751 static inline void
752 push_directory (const char *dir)
754 g_queue_push_head (&dir_queue, (void *) dir);
757 /* --------------------------------------------------------------------------------------------- */
759 static inline char *
760 pop_directory (void)
762 return (char *) g_queue_pop_tail (&dir_queue);
765 /* --------------------------------------------------------------------------------------------- */
766 /** Remove all the items from the stack */
768 static void
769 clear_stack (void)
771 g_queue_foreach (&dir_queue, (GFunc) g_free, NULL);
772 g_queue_clear (&dir_queue);
775 /* --------------------------------------------------------------------------------------------- */
777 #else /* GLIB_CHECK_VERSION */
778 static void
779 push_directory (const char *dir)
781 dir_stack *new;
783 new = g_new (dir_stack, 1);
784 new->name = (char *) dir;
785 new->prev = dir_stack_base;
786 dir_stack_base = new;
789 /* --------------------------------------------------------------------------------------------- */
791 static char *
792 pop_directory (void)
794 char *name = NULL;
796 if (dir_stack_base != NULL)
798 dir_stack *next;
799 name = dir_stack_base->name;
800 next = dir_stack_base->prev;
801 g_free (dir_stack_base);
802 dir_stack_base = next;
805 return name;
808 /* --------------------------------------------------------------------------------------------- */
809 /** Remove all the items from the stack */
811 static void
812 clear_stack (void)
814 char *dir = NULL;
815 while ((dir = pop_directory ()) != NULL)
816 g_free (dir);
818 #endif /* GLIB_CHECK_VERSION */
820 /* --------------------------------------------------------------------------------------------- */
822 static void
823 insert_file (const char *dir, const char *file)
825 char *tmp_name = NULL;
826 static char *dirname = NULL;
828 while (dir[0] == PATH_SEP && dir[1] == PATH_SEP)
829 dir++;
831 if (old_dir)
833 if (strcmp (old_dir, dir))
835 g_free (old_dir);
836 old_dir = g_strdup (dir);
837 dirname = add_to_list (dir, NULL);
840 else
842 old_dir = g_strdup (dir);
843 dirname = add_to_list (dir, NULL);
846 tmp_name = g_strdup_printf (" %s", file);
847 add_to_list (tmp_name, dirname);
848 g_free (tmp_name);
851 /* --------------------------------------------------------------------------------------------- */
853 static void
854 find_add_match (const char *dir, const char *file)
856 insert_file (dir, file);
858 /* Don't scroll */
859 if (matches == 0)
860 listbox_select_first (find_list);
861 send_message (&find_list->widget, WIDGET_DRAW, 0);
863 matches++;
864 found_num_update ();
867 /* --------------------------------------------------------------------------------------------- */
869 * get_line_at:
871 * Returns malloced null-terminated line from file file_fd.
872 * Input is buffered in buf_size long buffer.
873 * Current pos in buf is stored in pos.
874 * n_read - number of read chars.
875 * has_newline - is there newline ?
878 static char *
879 get_line_at (int file_fd, char *buf, int buf_size, int *pos, int *n_read, gboolean * has_newline)
881 char *buffer = NULL;
882 int buffer_size = 0;
883 char ch = 0;
884 int i = 0;
886 for (;;)
888 if (*pos >= *n_read)
890 *pos = 0;
891 *n_read = mc_read (file_fd, buf, buf_size);
892 if (*n_read <= 0)
893 break;
896 ch = buf[(*pos)++];
897 if (ch == '\0')
899 /* skip possible leading zero(s) */
900 if (i == 0)
901 continue;
902 break;
905 if (i >= buffer_size - 1)
906 buffer = g_realloc (buffer, buffer_size += 80);
908 /* Strip newline */
909 if (ch == '\n')
910 break;
912 buffer[i++] = ch;
915 *has_newline = (ch != '\0');
917 if (buffer != NULL)
918 buffer[i] = '\0';
920 return buffer;
923 /* --------------------------------------------------------------------------------------------- */
925 static FindProgressStatus
926 check_find_events (Dlg_head * h)
928 Gpm_Event event;
929 int c;
931 event.x = -1;
932 c = tty_get_event (&event, h->mouse_status == MOU_REPEAT, FALSE);
933 if (c != EV_NONE)
935 dlg_process_event (h, c, &event);
936 if (h->ret_value == B_ENTER
937 || h->ret_value == B_CANCEL || h->ret_value == B_AGAIN || h->ret_value == B_PANELIZE)
939 /* dialog terminated */
940 return FIND_ABORT;
942 if (!(h->flags & DLG_WANT_IDLE))
944 /* searching suspended */
945 return FIND_SUSPEND;
949 return FIND_CONT;
952 /* --------------------------------------------------------------------------------------------- */
954 * search_content:
956 * Search the content_pattern string in the DIRECTORY/FILE.
957 * It will add the found entries to the find listbox.
959 * returns FALSE if do_search should look for another file
960 * TRUE if do_search should exit and proceed to the event handler
963 static gboolean
964 search_content (Dlg_head * h, const char *directory, const char *filename)
966 struct stat s;
967 char buffer[BUF_4K];
968 char *fname = NULL;
969 int file_fd;
970 gboolean ret_val = FALSE;
971 vfs_path_t *vpath;
973 fname = mc_build_filename (directory, filename, (char *) NULL);
974 vpath = vfs_path_from_str (fname);
976 if (mc_stat (vpath, &s) != 0 || !S_ISREG (s.st_mode))
978 g_free (fname);
979 vfs_path_free (vpath);
980 return FALSE;
983 file_fd = mc_open (fname, O_RDONLY);
984 g_free (fname);
985 vfs_path_free (vpath);
987 if (file_fd == -1)
988 return FALSE;
990 g_snprintf (buffer, sizeof (buffer), _("Grepping in %s"), str_trunc (filename, FIND2_X_USE));
992 status_update (buffer);
993 mc_refresh ();
995 tty_enable_interrupt_key ();
996 tty_got_interrupt ();
999 int line = 1;
1000 int pos = 0;
1001 int n_read = 0;
1002 gboolean has_newline;
1003 char *p = NULL;
1004 gboolean found = FALSE;
1005 gsize found_len;
1006 char result[BUF_MEDIUM];
1008 if (resuming)
1010 /* We've been previously suspended, start from the previous position */
1011 resuming = 0;
1012 line = last_line;
1013 pos = last_pos;
1015 while (!ret_val
1016 && (p = get_line_at (file_fd, buffer, sizeof (buffer),
1017 &pos, &n_read, &has_newline)) != NULL)
1019 if (!found /* Search in binary line once */
1020 && mc_search_run (search_content_handle,
1021 (const void *) p, 0, strlen (p), &found_len))
1023 g_snprintf (result, sizeof (result), "%d:%s", line, filename);
1024 find_add_match (directory, result);
1025 found = TRUE;
1027 g_free (p);
1029 if (found && options.content_first_hit)
1030 break;
1032 if (has_newline)
1034 found = FALSE;
1035 line++;
1038 if ((line & 0xff) == 0)
1040 FindProgressStatus res;
1041 res = check_find_events (h);
1042 switch (res)
1044 case FIND_ABORT:
1045 stop_idle (h);
1046 ret_val = TRUE;
1047 break;
1048 case FIND_SUSPEND:
1049 resuming = 1;
1050 last_line = line;
1051 last_pos = pos;
1052 ret_val = TRUE;
1053 break;
1054 default:
1055 break;
1060 tty_disable_interrupt_key ();
1061 mc_close (file_fd);
1062 return ret_val;
1065 /* --------------------------------------------------------------------------------------------- */
1068 If dir is absolute, this means we're within dir and searching file here.
1069 If dir is relative, this means we're going to add dir to the directory stack.
1071 static gboolean
1072 find_ignore_dir_search (const char *dir)
1074 if (find_ignore_dirs != NULL)
1076 const size_t dlen = strlen (dir);
1077 const unsigned char dabs = g_path_is_absolute (dir) ? 1 : 0;
1079 char **ignore_dir;
1081 for (ignore_dir = find_ignore_dirs; *ignore_dir != NULL; ignore_dir++)
1083 const size_t ilen = strlen (*ignore_dir);
1084 const unsigned char iabs = g_path_is_absolute (*ignore_dir) ? 2 : 0;
1086 /* ignore dir is too long -- skip it */
1087 if (dlen < ilen)
1088 continue;
1090 /* handle absolute and relative paths */
1091 switch (iabs | dabs)
1093 case 0: /* both paths are relative */
1094 case 3: /* both paths are abolute */
1095 /* if ignore dir is not a path of dir -- skip it */
1096 if (strncmp (dir, *ignore_dir, ilen) == 0)
1098 /* be sure that ignore dir is not a part of dir like:
1099 ignore dir is "h", dir is "home" */
1100 if (dir[ilen] == '\0' || dir[ilen] == PATH_SEP)
1101 return TRUE;
1103 break;
1104 case 1: /* dir is absolute, ignore_dir is relative */
1106 char *d;
1108 d = strstr (dir, *ignore_dir);
1109 if (d != NULL && d[-1] == PATH_SEP && (d[ilen] == '\0' || d[ilen] == PATH_SEP))
1110 return TRUE;
1112 break;
1113 case 2: /* dir is relative, ignore_dir is absolute */
1114 /* FIXME: skip this case */
1115 break;
1116 default: /* this cannot occurs */
1117 return FALSE;
1122 return FALSE;
1125 /* --------------------------------------------------------------------------------------------- */
1127 static void
1128 find_rotate_dash (const Dlg_head * h, gboolean finish)
1130 static const char rotating_dash[] = "|/-\\";
1131 static unsigned int pos = 0;
1133 if (verbose)
1135 pos = (pos + 1) % 4;
1136 tty_setcolor (h->color[DLG_COLOR_NORMAL]);
1137 dlg_move (h, FIND2_Y - 7, FIND2_X - 4);
1138 tty_print_char (finish ? ' ' : rotating_dash[pos]);
1139 mc_refresh ();
1143 /* --------------------------------------------------------------------------------------------- */
1145 static int
1146 do_search (Dlg_head * h)
1148 static struct dirent *dp = NULL;
1149 static DIR *dirp = NULL;
1150 static char *directory = NULL;
1151 struct stat tmp_stat;
1152 static int subdirs_left = 0;
1153 gsize bytes_found;
1154 unsigned short count;
1156 if (h == NULL)
1157 { /* someone forces me to close dirp */
1158 if (dirp != NULL)
1160 mc_closedir (dirp);
1161 dirp = NULL;
1163 g_free (directory);
1164 directory = NULL;
1165 dp = NULL;
1166 return 1;
1169 for (count = 0; count < 32; count++)
1171 while (dp == NULL)
1173 if (dirp != NULL)
1175 mc_closedir (dirp);
1176 dirp = NULL;
1179 while (dirp == NULL)
1181 char *tmp = NULL;
1182 vfs_path_t *tmp_vpath;
1184 tty_setcolor (REVERSE_COLOR);
1186 while (TRUE)
1188 tmp = pop_directory ();
1189 if (tmp == NULL)
1191 running = FALSE;
1192 if (ignore_count == 0)
1193 status_update (_("Finished"));
1194 else
1196 char msg[BUF_SMALL];
1197 g_snprintf (msg, sizeof (msg),
1198 ngettext ("Finished (ignored %zd directory)",
1199 "Finished (ignored %zd directories)",
1200 ignore_count), ignore_count);
1201 status_update (msg);
1203 find_rotate_dash (h, TRUE);
1204 stop_idle (h);
1205 return 0;
1208 /* handle absolute ignore dirs here */
1209 if (!find_ignore_dir_search (tmp))
1210 break;
1212 g_free (tmp);
1213 ignore_count++;
1216 g_free (directory);
1217 directory = tmp;
1218 tmp_vpath = vfs_path_from_str (directory);
1220 if (verbose)
1222 char buffer[BUF_SMALL];
1224 g_snprintf (buffer, sizeof (buffer), _("Searching %s"),
1225 str_trunc (directory, FIND2_X_USE));
1226 status_update (buffer);
1228 /* mc_stat should not be called after mc_opendir
1229 because vfs_s_opendir modifies the st_nlink
1231 if (mc_stat (tmp_vpath, &tmp_stat) == 0)
1232 subdirs_left = tmp_stat.st_nlink - 2;
1233 else
1234 subdirs_left = 0;
1236 dirp = mc_opendir (tmp_vpath);
1237 vfs_path_free (tmp_vpath);
1238 } /* while (!dirp) */
1240 /* skip invalid filenames */
1241 while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1243 } /* while (!dp) */
1245 if (strcmp (dp->d_name, ".") == 0 || strcmp (dp->d_name, "..") == 0)
1247 /* skip invalid filenames */
1248 while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1251 return 1;
1254 if (!(options.skip_hidden && (dp->d_name[0] == '.')))
1256 gboolean search_ok;
1258 if ((subdirs_left != 0) && options.find_recurs && (directory != NULL))
1259 { /* Can directory be NULL ? */
1260 /* handle relative ignore dirs here */
1261 if (options.ignore_dirs_enable && find_ignore_dir_search (dp->d_name))
1262 ignore_count++;
1263 else
1265 vfs_path_t *tmp_vpath;
1267 tmp_vpath = vfs_path_build_filename (directory, dp->d_name, (char *) NULL);
1269 if (mc_lstat (tmp_vpath, &tmp_stat) == 0 && S_ISDIR (tmp_stat.st_mode))
1271 push_directory (vfs_path_to_str (tmp_vpath));
1272 vfs_path_free (tmp_vpath);
1273 subdirs_left--;
1275 else
1276 vfs_path_free (tmp_vpath);
1280 search_ok = mc_search_run (search_file_handle, dp->d_name,
1281 0, strlen (dp->d_name), &bytes_found);
1283 if (search_ok)
1285 if (content_pattern == NULL)
1286 find_add_match (directory, dp->d_name);
1287 else if (search_content (h, directory, dp->d_name))
1288 return 1;
1292 /* skip invalid filenames */
1293 while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1295 } /* for */
1297 find_rotate_dash (h, FALSE);
1299 return 1;
1302 /* --------------------------------------------------------------------------------------------- */
1304 static void
1305 init_find_vars (void)
1307 g_free (old_dir);
1308 old_dir = NULL;
1309 matches = 0;
1310 ignore_count = 0;
1312 /* Remove all the items from the stack */
1313 clear_stack ();
1315 g_strfreev (find_ignore_dirs);
1316 find_ignore_dirs = NULL;
1319 /* --------------------------------------------------------------------------------------------- */
1321 static void
1322 find_do_view_edit (int unparsed_view, int edit, char *dir, char *file)
1324 char *fullname = NULL;
1325 const char *filename = NULL;
1326 int line;
1328 if (content_pattern != NULL)
1330 filename = strchr (file + 4, ':') + 1;
1331 line = atoi (file + 4);
1333 else
1335 filename = file + 4;
1336 line = 0;
1339 fullname = mc_build_filename (dir, filename, (char *) NULL);
1340 if (edit)
1341 do_edit_at_line (fullname, use_internal_edit, line);
1342 else
1343 view_file_at_line (fullname, unparsed_view, use_internal_view, line);
1344 g_free (fullname);
1347 /* --------------------------------------------------------------------------------------------- */
1349 static cb_ret_t
1350 view_edit_currently_selected_file (int unparsed_view, int edit)
1352 char *dir = NULL;
1353 char *text = NULL;
1355 listbox_get_current (find_list, &text, (void **) &dir);
1357 if ((text == NULL) || (dir == NULL))
1358 return MSG_NOT_HANDLED;
1360 find_do_view_edit (unparsed_view, edit, dir, text);
1361 return MSG_HANDLED;
1364 /* --------------------------------------------------------------------------------------------- */
1366 static cb_ret_t
1367 find_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
1369 switch (msg)
1371 case DLG_KEY:
1372 if (parm == KEY_F (3) || parm == KEY_F (13))
1374 int unparsed_view = (parm == KEY_F (13));
1375 return view_edit_currently_selected_file (unparsed_view, 0);
1377 if (parm == KEY_F (4))
1379 return view_edit_currently_selected_file (0, 1);
1381 return MSG_NOT_HANDLED;
1383 case DLG_IDLE:
1384 do_search (h);
1385 return MSG_HANDLED;
1387 default:
1388 return default_dlg_callback (h, sender, msg, parm, data);
1392 /* --------------------------------------------------------------------------------------------- */
1393 /** Handles the Stop/Start button in the find window */
1395 static int
1396 start_stop (WButton * button, int action)
1398 (void) button;
1399 (void) action;
1401 running = is_start;
1402 set_idle_proc (find_dlg, running);
1403 is_start = !is_start;
1405 status_update (is_start ? _("Stopped") : _("Searching"));
1406 button_set_text (stop_button, fbuts[is_start ? 1 : 0].text);
1408 return 0;
1411 /* --------------------------------------------------------------------------------------------- */
1412 /** Handle view command, when invoked as a button */
1414 static int
1415 find_do_view_file (WButton * button, int action)
1417 (void) button;
1418 (void) action;
1420 view_edit_currently_selected_file (0, 0);
1421 return 0;
1424 /* --------------------------------------------------------------------------------------------- */
1425 /** Handle edit command, when invoked as a button */
1427 static int
1428 find_do_edit_file (WButton * button, int action)
1430 (void) button;
1431 (void) action;
1433 view_edit_currently_selected_file (0, 1);
1434 return 0;
1437 /* --------------------------------------------------------------------------------------------- */
1439 static void
1440 setup_gui (void)
1442 #ifdef ENABLE_NLS
1443 static gboolean i18n_flag = FALSE;
1445 if (!i18n_flag)
1447 int i = sizeof (fbuts) / sizeof (fbuts[0]);
1448 while (i-- != 0)
1450 fbuts[i].text = _(fbuts[i].text);
1451 fbuts[i].len = str_term_width1 (fbuts[i].text) + 3;
1454 fbuts[2].len += 2; /* DEFPUSH_BUTTON */
1455 i18n_flag = TRUE;
1457 #endif /* ENABLE_NLS */
1460 * Dynamically place buttons centered within current window size
1463 int l0 = max (fbuts[0].len, fbuts[1].len);
1464 int l1 = fbuts[2].len + fbuts[3].len + l0 + fbuts[4].len;
1465 int l2 = fbuts[5].len + fbuts[6].len + fbuts[7].len;
1466 int r1, r2;
1468 /* Check, if both button rows fit within FIND2_X */
1469 FIND2_X = max (l1 + 9, COLS - 16);
1470 FIND2_X = max (l2 + 8, FIND2_X);
1472 /* compute amount of space between buttons for each row */
1473 r1 = (FIND2_X - 4 - l1) % 5;
1474 l1 = (FIND2_X - 4 - l1) / 5;
1475 r2 = (FIND2_X - 4 - l2) % 4;
1476 l2 = (FIND2_X - 4 - l2) / 4;
1478 /* ...and finally, place buttons */
1479 fbuts[2].x = 2 + r1 / 2 + l1;
1480 fbuts[3].x = fbuts[2].x + fbuts[2].len + l1;
1481 fbuts[0].x = fbuts[3].x + fbuts[3].len + l1;
1482 fbuts[4].x = fbuts[0].x + l0 + l1;
1483 fbuts[5].x = 2 + r2 / 2 + l2;
1484 fbuts[6].x = fbuts[5].x + fbuts[5].len + l2;
1485 fbuts[7].x = fbuts[6].x + fbuts[6].len + l2;
1488 find_dlg =
1489 create_dlg (TRUE, 0, 0, FIND2_Y, FIND2_X, dialog_colors, find_callback,
1490 "[Find File]", _("Find File"), DLG_CENTER | DLG_REVERSE);
1492 add_widget (find_dlg,
1493 button_new (FIND2_Y - 3, fbuts[7].x, B_VIEW, NORMAL_BUTTON,
1494 fbuts[7].text, find_do_edit_file));
1495 add_widget (find_dlg,
1496 button_new (FIND2_Y - 3, fbuts[6].x, B_VIEW, NORMAL_BUTTON,
1497 fbuts[6].text, find_do_view_file));
1498 add_widget (find_dlg,
1499 button_new (FIND2_Y - 3, fbuts[5].x, B_PANELIZE, NORMAL_BUTTON, fbuts[5].text,
1500 NULL));
1502 add_widget (find_dlg,
1503 button_new (FIND2_Y - 4, fbuts[4].x, B_CANCEL, NORMAL_BUTTON, fbuts[4].text, NULL));
1504 stop_button =
1505 button_new (FIND2_Y - 4, fbuts[0].x, B_STOP, NORMAL_BUTTON, fbuts[0].text, start_stop);
1506 add_widget (find_dlg, stop_button);
1507 add_widget (find_dlg,
1508 button_new (FIND2_Y - 4, fbuts[3].x, B_AGAIN, NORMAL_BUTTON, fbuts[3].text, NULL));
1509 add_widget (find_dlg,
1510 button_new (FIND2_Y - 4, fbuts[2].x, B_ENTER, DEFPUSH_BUTTON, fbuts[2].text, NULL));
1512 status_label = label_new (FIND2_Y - 7, 4, _("Searching"));
1513 add_widget (find_dlg, status_label);
1515 found_num_label = label_new (FIND2_Y - 6, 4, "");
1516 add_widget (find_dlg, found_num_label);
1518 find_list = listbox_new (2, 2, FIND2_Y - 10, FIND2_X - 4, FALSE, NULL);
1519 add_widget (find_dlg, find_list);
1522 /* --------------------------------------------------------------------------------------------- */
1524 static int
1525 run_process (void)
1527 int ret;
1529 search_content_handle = mc_search_new (content_pattern, -1);
1530 if (search_content_handle)
1532 search_content_handle->search_type =
1533 options.content_regexp ? MC_SEARCH_T_REGEX : MC_SEARCH_T_NORMAL;
1534 search_content_handle->is_case_sensitive = options.content_case_sens;
1535 search_content_handle->whole_words = options.content_whole_words;
1536 search_content_handle->is_all_charsets = options.content_all_charsets;
1538 search_file_handle = mc_search_new (find_pattern, -1);
1539 search_file_handle->search_type = options.file_pattern ? MC_SEARCH_T_GLOB : MC_SEARCH_T_REGEX;
1540 search_file_handle->is_case_sensitive = options.file_case_sens;
1541 search_file_handle->is_all_charsets = options.file_all_charsets;
1542 search_file_handle->is_entire_line = options.file_pattern;
1544 resuming = 0;
1546 set_idle_proc (find_dlg, 1);
1547 ret = run_dlg (find_dlg);
1549 mc_search_free (search_file_handle);
1550 search_file_handle = NULL;
1551 mc_search_free (search_content_handle);
1552 search_content_handle = NULL;
1554 return ret;
1557 /* --------------------------------------------------------------------------------------------- */
1559 static void
1560 kill_gui (void)
1562 set_idle_proc (find_dlg, 0);
1563 destroy_dlg (find_dlg);
1566 /* --------------------------------------------------------------------------------------------- */
1568 static int
1569 do_find (const char *start_dir, ssize_t start_dir_len, const char *ignore_dirs,
1570 const char *pattern, const char *content, char **dirname, char **filename)
1572 int return_value = 0;
1573 char *dir_tmp = NULL, *file_tmp = NULL;
1575 setup_gui ();
1577 /* FIXME: Need to cleanup this, this ought to be passed non-globaly */
1578 find_pattern = (char *) pattern;
1580 content_pattern = NULL;
1581 if (options.content_use && content != NULL && str_is_valid_string (content))
1582 content_pattern = g_strdup (content);
1584 init_find_vars ();
1585 parse_ignore_dirs (ignore_dirs);
1586 push_directory (start_dir);
1588 return_value = run_process ();
1590 /* Clear variables */
1591 init_find_vars ();
1593 get_list_info (&file_tmp, &dir_tmp);
1595 if (dir_tmp)
1596 *dirname = g_strdup (dir_tmp);
1597 if (file_tmp)
1598 *filename = g_strdup (file_tmp);
1600 if (return_value == B_PANELIZE && *filename)
1602 int status, link_to_dir, stale_link;
1603 int next_free = 0;
1604 int i;
1605 struct stat st;
1606 GList *entry;
1607 dir_list *list = &current_panel->dir;
1608 char *name = NULL;
1610 if (set_zero_dir (list))
1611 next_free++;
1613 for (i = 0, entry = find_list->list; entry != NULL; i++, entry = g_list_next (entry))
1615 const char *lc_filename = NULL;
1616 WLEntry *le = (WLEntry *) entry->data;
1617 char *p;
1619 if ((le->text == NULL) || (le->data == NULL))
1620 continue;
1622 if (content_pattern != NULL)
1623 lc_filename = strchr (le->text + 4, ':') + 1;
1624 else
1625 lc_filename = le->text + 4;
1627 name = mc_build_filename (le->data, lc_filename, (char *) NULL);
1628 /* skip initial start dir */
1629 if (start_dir_len < 0)
1630 p = name;
1631 else
1633 p = name + (size_t) start_dir_len;
1634 if (*p == PATH_SEP)
1635 p++;
1638 status = handle_path (list, p, &st, next_free, &link_to_dir, &stale_link);
1639 if (status == 0)
1641 g_free (name);
1642 continue;
1644 if (status == -1)
1646 g_free (name);
1647 break;
1650 /* don't add files more than once to the panel */
1651 if (content_pattern != NULL && next_free > 0
1652 && strcmp (list->list[next_free - 1].fname, p) == 0)
1654 g_free (name);
1655 continue;
1658 if (next_free == 0) /* first turn i.e clean old list */
1659 panel_clean_dir (current_panel);
1660 list->list[next_free].fnamelen = strlen (p);
1661 list->list[next_free].fname = g_strndup (p, list->list[next_free].fnamelen);
1662 list->list[next_free].f.marked = 0;
1663 list->list[next_free].f.link_to_dir = link_to_dir;
1664 list->list[next_free].f.stale_link = stale_link;
1665 list->list[next_free].f.dir_size_computed = 0;
1666 list->list[next_free].st = st;
1667 list->list[next_free].sort_key = NULL;
1668 list->list[next_free].second_sort_key = NULL;
1669 next_free++;
1670 g_free (name);
1671 if (!(next_free & 15))
1672 rotate_dash ();
1675 if (next_free)
1677 current_panel->count = next_free;
1678 current_panel->is_panelized = TRUE;
1680 /* absolute path */
1681 if (start_dir_len < 0)
1683 int ret;
1685 strcpy (current_panel->cwd, PATH_SEP_STR);
1686 ret = chdir (PATH_SEP_STR);
1688 panelize_save_panel (current_panel);
1692 g_free (content_pattern);
1693 kill_gui ();
1694 do_search (NULL); /* force do_search to release resources */
1695 g_free (old_dir);
1696 old_dir = NULL;
1698 return return_value;
1701 /* --------------------------------------------------------------------------------------------- */
1702 /*** public functions ****************************************************************************/
1703 /* --------------------------------------------------------------------------------------------- */
1705 void
1706 find_file (void)
1708 char *start_dir = NULL, *pattern = NULL, *content = NULL, *ignore_dirs = NULL;
1709 ssize_t start_dir_len;
1710 char *filename = NULL, *dirname = NULL;
1711 int v;
1713 while (find_parameters (&start_dir, &start_dir_len, &ignore_dirs, &pattern, &content))
1715 if (pattern[0] == '\0')
1716 break; /* nothing search */
1718 dirname = filename = NULL;
1719 is_start = FALSE;
1720 v = do_find (start_dir, start_dir_len, ignore_dirs, pattern, content, &dirname, &filename);
1721 g_free (ignore_dirs);
1722 g_free (pattern);
1724 if (v == B_ENTER)
1726 if (dirname != NULL)
1728 do_cd (dirname, cd_exact);
1729 if (filename != NULL)
1730 try_to_select (current_panel,
1731 filename + (content != NULL
1732 ? strchr (filename + 4, ':') - filename + 1 : 4));
1734 else if (filename != NULL)
1735 do_cd (filename, cd_exact);
1737 g_free (dirname);
1738 g_free (filename);
1739 break;
1742 g_free (content);
1743 g_free (dirname);
1744 g_free (filename);
1746 if (v == B_CANCEL)
1747 break;
1749 if (v == B_PANELIZE)
1751 panel_re_sort (current_panel);
1752 try_to_select (current_panel, NULL);
1753 break;
1758 /* --------------------------------------------------------------------------------------------- */