Ticket 1551: Update GPL version from 2 to 3
[midnight-commander.git] / src / filemanager / find.c
blob2634f5115799e80a7ea3f8abfcd44e9b3ab3ddb7
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"
61 #include "find.h"
63 /*** global variables ****************************************************************************/
65 /*** file scope macro definitions ****************************************************************/
67 /* Size of the find window */
68 #define FIND2_Y (LINES - 4)
70 #define FIND2_X_USE (FIND2_X - 20)
72 /*** file scope type declarations ****************************************************************/
74 /* A couple of extra messages we need */
75 enum
77 B_STOP = B_USER + 1,
78 B_AGAIN,
79 B_PANELIZE,
80 B_TREE,
81 B_VIEW
84 typedef enum
86 FIND_CONT = 0,
87 FIND_SUSPEND,
88 FIND_ABORT
89 } FindProgressStatus;
91 /* find file options */
92 typedef struct
94 /* file name options */
95 gboolean file_case_sens;
96 gboolean file_pattern;
97 gboolean find_recurs;
98 gboolean skip_hidden;
99 gboolean file_all_charsets;
101 /* file content options */
102 gboolean content_use;
103 gboolean content_case_sens;
104 gboolean content_regexp;
105 gboolean content_first_hit;
106 gboolean content_whole_words;
107 gboolean content_all_charsets;
109 /* whether use ignore dirs or not */
110 gboolean ignore_dirs_enable;
111 /* list of directories to be ignored, separated by ':' */
112 char *ignore_dirs;
113 } find_file_options_t;
115 /*** file scope variables ************************************************************************/
117 /* Parsed ignore dirs */
118 static char **find_ignore_dirs = NULL;
120 /* Size of the find parameters window */
121 #if HAVE_CHARSET
122 static int FIND_Y = 19;
123 #else
124 static int FIND_Y = 18;
125 #endif
126 static int FIND_X = 68;
128 static int FIND2_X = 64;
130 /* static variables to remember find parameters */
131 static WInput *in_start; /* Start path */
132 static WInput *in_name; /* Filename */
133 static WInput *in_with; /* Text */
134 static WInput *in_ignore;
135 static WLabel *content_label; /* 'Content:' label */
136 static WCheck *file_case_sens_cbox; /* "case sensitive" checkbox */
137 static WCheck *file_pattern_cbox; /* File name is glob or regexp */
138 static WCheck *recursively_cbox;
139 static WCheck *skip_hidden_cbox;
140 static WCheck *content_use_cbox; /* Take into account the Content field */
141 static WCheck *content_case_sens_cbox; /* "case sensitive" checkbox */
142 static WCheck *content_regexp_cbox; /* "find regular expression" checkbox */
143 static WCheck *content_first_hit_cbox; /* "First hit" checkbox" */
144 static WCheck *content_whole_words_cbox; /* "whole words" checkbox */
145 #ifdef HAVE_CHARSET
146 static WCheck *file_all_charsets_cbox;
147 static WCheck *content_all_charsets_cbox;
148 #endif
149 static WCheck *ignore_dirs_cbox;
151 static gboolean running = FALSE; /* nice flag */
152 static char *find_pattern = NULL; /* Pattern to search */
153 static char *content_pattern = NULL; /* pattern to search inside files; if
154 content_regexp_flag is true, it contains the
155 regex pattern, else the search string. */
156 static unsigned long matches; /* Number of matches */
157 static gboolean is_start = FALSE; /* Status of the start/stop toggle button */
158 static char *old_dir = NULL;
160 /* Where did we stop */
161 static int resuming;
162 static int last_line;
163 static int last_pos;
165 static size_t ignore_count = 0;
167 static Dlg_head *find_dlg; /* The dialog */
168 static WButton *stop_button; /* pointer to the stop button */
169 static WLabel *status_label; /* Finished, Searching etc. */
170 static WLabel *found_num_label; /* Number of found items */
171 static WListbox *find_list; /* Listbox with the file list */
173 /* This keeps track of the directory stack */
174 #if GLIB_CHECK_VERSION (2, 14, 0)
175 static GQueue dir_queue = G_QUEUE_INIT;
176 #else
177 typedef struct dir_stack
179 char *name;
180 struct dir_stack *prev;
181 } dir_stack;
183 static dir_stack *dir_stack_base = 0;
184 #endif /* GLIB_CHECK_VERSION */
186 /* *INDENT-OFF* */
187 static struct
189 const char *text;
190 int len; /* length including space and brackets */
191 int x;
192 } fbuts[] =
194 {N_("&Suspend"), 11, 29},
195 {N_("Con&tinue"), 12, 29},
196 {N_("&Chdir"), 11, 3},
197 {N_("&Again"), 9, 17},
198 {N_("&Quit"), 8, 43},
199 {N_("Pane&lize"), 12, 3},
200 {N_("&View - F3"), 13, 20},
201 {N_("&Edit - F4"), 13, 38}
203 /* *INDENT-ON* */
205 static find_file_options_t options = {
206 TRUE, TRUE, TRUE, FALSE, FALSE,
207 FALSE, TRUE, FALSE, FALSE, FALSE, FALSE
210 static char *in_start_dir = INPUT_LAST_TEXT;
212 static mc_search_t *search_file_handle = NULL;
213 static mc_search_t *search_content_handle = NULL;
215 /*** file scope functions ************************************************************************/
217 static void
218 parse_ignore_dirs (const char *ignore_dirs)
220 size_t r = 0, w = 0; /* read and write iterators */
222 if (!options.ignore_dirs_enable || ignore_dirs == NULL || ignore_dirs[0] == '\0')
223 return;
225 find_ignore_dirs = g_strsplit (ignore_dirs, ":", -1);
227 /* Values like '/foo::/bar: produce holes in list.
228 * Find and remove them */
229 for (; find_ignore_dirs[r] != NULL; r++)
231 if (find_ignore_dirs[r][0] == '\0')
233 /* empty entry -- skip it */
234 g_free (find_ignore_dirs[r]);
235 find_ignore_dirs[r] = NULL;
236 continue;
239 if (r != w)
241 /* copy entry to the previous free array cell */
242 find_ignore_dirs[w] = find_ignore_dirs[r];
243 find_ignore_dirs[r] = NULL;
246 canonicalize_pathname (find_ignore_dirs[w]);
247 if (find_ignore_dirs[w][0] != '\0')
248 w++;
249 else
251 g_free (find_ignore_dirs[w]);
252 find_ignore_dirs[w] = NULL;
256 if (find_ignore_dirs[0] == NULL)
258 g_strfreev (find_ignore_dirs);
259 find_ignore_dirs = NULL;
263 /* --------------------------------------------------------------------------------------------- */
265 static void
266 find_load_options (void)
268 static gboolean loaded = FALSE;
269 char *ignore_dirs;
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);
299 /* Back compatibility: try load old parameter at first */
300 ignore_dirs = mc_config_get_string (mc_main_config, "Misc", "find_ignore_dirs", "");
301 mc_config_del_key (mc_main_config, "Misc", "find_ignore_dirs");
303 /* Then load new parameters */
304 options.ignore_dirs = mc_config_get_string (mc_main_config, "FindFile", "ignore_dirs", "");
305 if (options.ignore_dirs[0] != '\0')
306 g_free (ignore_dirs);
307 else
309 g_free (options.ignore_dirs);
310 options.ignore_dirs = ignore_dirs;
313 if (options.ignore_dirs[0] == '\0')
315 g_free (options.ignore_dirs);
316 options.ignore_dirs = NULL;
320 /* --------------------------------------------------------------------------------------------- */
322 static void
323 find_save_options (void)
325 mc_config_set_bool (mc_main_config, "FindFile", "file_case_sens", options.file_case_sens);
326 mc_config_set_bool (mc_main_config, "FindFile", "file_shell_pattern", options.file_pattern);
327 mc_config_set_bool (mc_main_config, "FindFile", "file_find_recurs", options.find_recurs);
328 mc_config_set_bool (mc_main_config, "FindFile", "file_skip_hidden", options.skip_hidden);
329 mc_config_set_bool (mc_main_config, "FindFile", "file_all_charsets", options.file_all_charsets);
330 mc_config_set_bool (mc_main_config, "FindFile", "content_use", options.content_use);
331 mc_config_set_bool (mc_main_config, "FindFile", "content_case_sens", options.content_case_sens);
332 mc_config_set_bool (mc_main_config, "FindFile", "content_regexp", options.content_regexp);
333 mc_config_set_bool (mc_main_config, "FindFile", "content_first_hit", options.content_first_hit);
334 mc_config_set_bool (mc_main_config, "FindFile", "content_whole_words",
335 options.content_whole_words);
336 mc_config_set_bool (mc_main_config, "FindFile", "content_all_charsets",
337 options.content_all_charsets);
338 mc_config_set_bool (mc_main_config, "FindFile", "ignore_dirs_enable",
339 options.ignore_dirs_enable);
340 mc_config_set_string (mc_main_config, "FindFile", "ignore_dirs", options.ignore_dirs);
343 /* --------------------------------------------------------------------------------------------- */
345 static inline char *
346 add_to_list (const char *text, void *data)
348 return listbox_add_item (find_list, LISTBOX_APPEND_AT_END, 0, text, data);
351 /* --------------------------------------------------------------------------------------------- */
353 static inline void
354 stop_idle (void *data)
356 set_idle_proc (data, 0);
359 /* --------------------------------------------------------------------------------------------- */
361 static inline void
362 status_update (const char *text)
364 label_set_text (status_label, text);
367 /* --------------------------------------------------------------------------------------------- */
369 static void
370 found_num_update (void)
372 char buffer[BUF_TINY];
373 g_snprintf (buffer, sizeof (buffer), _("Found: %ld"), matches);
374 label_set_text (found_num_label, buffer);
377 /* --------------------------------------------------------------------------------------------- */
379 static void
380 get_list_info (char **file, char **dir)
382 listbox_get_current (find_list, file, (void **) dir);
385 /* --------------------------------------------------------------------------------------------- */
386 /** check regular expression */
388 static gboolean
389 find_check_regexp (const char *r)
391 mc_search_t *search;
392 gboolean regexp_ok = FALSE;
394 search = mc_search_new (r, -1);
396 if (search != NULL)
398 search->search_type = MC_SEARCH_T_REGEX;
399 regexp_ok = mc_search_prepare (search);
400 mc_search_free (search);
403 return regexp_ok;
406 /* --------------------------------------------------------------------------------------------- */
408 * Callback for the parameter dialog.
409 * Validate regex, prevent closing the dialog if it's invalid.
412 static cb_ret_t
413 find_parm_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
415 switch (msg)
417 case DLG_ACTION:
418 if (sender == (Widget *) content_use_cbox)
420 gboolean disable = !(content_use_cbox->state & C_BOOL);
422 widget_disable (content_label->widget, disable);
423 send_message ((Widget *) content_label, WIDGET_DRAW, 0);
424 widget_disable (in_with->widget, disable);
425 send_message ((Widget *) in_with, WIDGET_DRAW, 0);
426 widget_disable (content_first_hit_cbox->widget, disable);
427 send_message ((Widget *) content_first_hit_cbox, WIDGET_DRAW, 0);
428 widget_disable (content_regexp_cbox->widget, disable);
429 send_message ((Widget *) content_regexp_cbox, WIDGET_DRAW, 0);
430 widget_disable (content_case_sens_cbox->widget, disable);
431 send_message ((Widget *) content_case_sens_cbox, WIDGET_DRAW, 0);
432 #ifdef HAVE_CHARSET
433 widget_disable (content_all_charsets_cbox->widget, disable);
434 send_message ((Widget *) content_all_charsets_cbox, WIDGET_DRAW, 0);
435 #endif
436 widget_disable (content_whole_words_cbox->widget, disable);
437 send_message ((Widget *) content_whole_words_cbox, WIDGET_DRAW, 0);
439 return MSG_HANDLED;
442 if (sender == (Widget *) ignore_dirs_cbox)
444 gboolean disable = !(ignore_dirs_cbox->state & C_BOOL);
446 widget_disable (in_ignore->widget, disable);
447 send_message ((Widget *) in_ignore, WIDGET_DRAW, 0);
449 return MSG_HANDLED;
452 return MSG_NOT_HANDLED;
455 case DLG_VALIDATE:
456 if (h->ret_value != B_ENTER)
457 return MSG_HANDLED;
459 /* check filename regexp */
460 if (!(file_pattern_cbox->state & C_BOOL)
461 && (in_name->buffer[0] != '\0') && !find_check_regexp (in_name->buffer))
463 h->state = DLG_ACTIVE; /* Don't stop the dialog */
464 message (D_ERROR, MSG_ERROR, _("Malformed regular expression"));
465 dlg_select_widget (in_name);
466 return MSG_HANDLED;
469 /* check content regexp */
470 if ((content_use_cbox->state & C_BOOL) && (content_regexp_cbox->state & C_BOOL)
471 && (in_with->buffer[0] != '\0') && !find_check_regexp (in_with->buffer))
473 h->state = DLG_ACTIVE; /* Don't stop the dialog */
474 message (D_ERROR, MSG_ERROR, _("Malformed regular expression"));
475 dlg_select_widget (in_with);
476 return MSG_HANDLED;
479 return MSG_HANDLED;
481 default:
482 return default_dlg_callback (h, sender, msg, parm, data);
486 /* --------------------------------------------------------------------------------------------- */
488 * find_parameters: gets information from the user
490 * If the return value is TRUE, then the following holds:
492 * start_dir, ignore_dirs, pattern and content contain the information provided by the user.
493 * They are newly allocated strings and must be freed when uneeded.
495 * start_dir_len is -1 when user entered an absolute path, otherwise it is a length
496 * of start_dir (which is absolute). It is used to get a relative pats of find results.
499 static gboolean
500 find_parameters (char **start_dir, ssize_t * start_dir_len,
501 char **ignore_dirs, char **pattern, char **content)
503 gboolean return_value;
505 /* file name */
506 const char *file_case_label = N_("Cas&e sensitive");
507 const char *file_pattern_label = N_("&Using shell patterns");
508 const char *file_recurs_label = N_("&Find recursively");
509 const char *file_skip_hidden_label = N_("S&kip hidden");
510 #ifdef HAVE_CHARSET
511 const char *file_all_charsets_label = N_("&All charsets");
512 #endif
514 /* file content */
515 const char *content_use_label = N_("Sea&rch for content");
516 const char *content_case_label = N_("Case sens&itive");
517 const char *content_regexp_label = N_("Re&gular expression");
518 const char *content_first_hit_label = N_("Fir&st hit");
519 const char *content_whole_words_label = N_("&Whole words");
520 #ifdef HAVE_CHARSET
521 const char *content_all_charsets_label = N_("A&ll charsets");
522 #endif
524 const char *buts[] = { N_("&OK"), N_("&Cancel"), N_("&Tree") };
526 int b0, b1, b2;
528 int cbox_position;
529 gboolean disable;
531 #ifdef ENABLE_NLS
533 int i = sizeof (buts) / sizeof (buts[0]);
534 while (i-- != 0)
535 buts[i] = _(buts[i]);
537 file_case_label = _(file_case_label);
538 file_pattern_label = _(file_pattern_label);
539 file_recurs_label = _(file_recurs_label);
540 file_skip_hidden_label = _(file_skip_hidden_label);
541 #ifdef HAVE_CHARSET
542 file_all_charsets_label = _(file_all_charsets_label);
543 content_all_charsets_label = _(content_all_charsets_label);
544 #endif
545 content_use_label = _(content_use_label);
546 content_case_label = _(content_case_label);
547 content_regexp_label = _(content_regexp_label);
548 content_first_hit_label = _(content_first_hit_label);
549 content_whole_words_label = _(content_whole_words_label);
551 #endif /* ENABLE_NLS */
553 b0 = str_term_width1 (buts[0]) + 6; /* default button */
554 b1 = str_term_width1 (buts[1]) + 4;
555 b2 = str_term_width1 (buts[2]) + 4;
557 find_load_options ();
559 if (in_start_dir == NULL)
560 in_start_dir = g_strdup (".");
562 disable = !options.content_use;
564 find_dlg =
565 create_dlg (TRUE, 0, 0, FIND_Y, FIND_X, dialog_colors,
566 find_parm_callback, "[Find File]", _("Find File"), DLG_CENTER | DLG_REVERSE);
568 add_widget (find_dlg,
569 button_new (FIND_Y - 3, FIND_X * 3 / 4 - b1 / 2, B_CANCEL, NORMAL_BUTTON, buts[1],
570 0));
571 add_widget (find_dlg,
572 button_new (FIND_Y - 3, FIND_X / 4 - b0 / 2, B_ENTER, DEFPUSH_BUTTON, buts[0], 0));
574 cbox_position = FIND_Y - 5;
576 content_first_hit_cbox =
577 check_new (cbox_position--, FIND_X / 2 + 1, options.content_first_hit,
578 content_first_hit_label);
579 widget_disable (content_first_hit_cbox->widget, disable);
580 add_widget (find_dlg, content_first_hit_cbox);
582 content_whole_words_cbox =
583 check_new (cbox_position--, FIND_X / 2 + 1, options.content_whole_words,
584 content_whole_words_label);
585 widget_disable (content_whole_words_cbox->widget, disable);
586 add_widget (find_dlg, content_whole_words_cbox);
588 #ifdef HAVE_CHARSET
589 content_all_charsets_cbox = check_new (cbox_position--, FIND_X / 2 + 1,
590 options.content_all_charsets,
591 content_all_charsets_label);
592 widget_disable (content_all_charsets_cbox->widget, disable);
593 add_widget (find_dlg, content_all_charsets_cbox);
594 #endif
596 content_case_sens_cbox =
597 check_new (cbox_position--, FIND_X / 2 + 1, options.content_case_sens, content_case_label);
598 widget_disable (content_case_sens_cbox->widget, disable);
599 add_widget (find_dlg, content_case_sens_cbox);
601 content_regexp_cbox =
602 check_new (cbox_position--, FIND_X / 2 + 1, options.content_regexp, content_regexp_label);
603 widget_disable (content_regexp_cbox->widget, disable);
604 add_widget (find_dlg, content_regexp_cbox);
606 cbox_position = FIND_Y - 6;
608 skip_hidden_cbox = check_new (cbox_position--, 3, options.skip_hidden, file_skip_hidden_label);
609 add_widget (find_dlg, skip_hidden_cbox);
611 #ifdef HAVE_CHARSET
612 file_all_charsets_cbox =
613 check_new (cbox_position--, 3, options.file_all_charsets, file_all_charsets_label);
614 add_widget (find_dlg, file_all_charsets_cbox);
615 #endif
617 file_case_sens_cbox = check_new (cbox_position--, 3, options.file_case_sens, file_case_label);
618 add_widget (find_dlg, file_case_sens_cbox);
620 file_pattern_cbox = check_new (cbox_position--, 3, options.file_pattern, file_pattern_label);
621 add_widget (find_dlg, file_pattern_cbox);
623 recursively_cbox = check_new (cbox_position, 3, options.find_recurs, file_recurs_label);
624 add_widget (find_dlg, recursively_cbox);
626 /* This checkbox is located in the second column */
627 content_use_cbox =
628 check_new (cbox_position, FIND_X / 2 + 1, options.content_use, content_use_label);
629 add_widget (find_dlg, content_use_cbox);
631 in_with =
632 input_new (8, FIND_X / 2 + 1, input_get_default_colors (), FIND_X / 2 - 4, INPUT_LAST_TEXT,
633 MC_HISTORY_SHARED_SEARCH, INPUT_COMPLETE_DEFAULT);
634 widget_disable (in_with->widget, disable);
635 add_widget (find_dlg, in_with);
637 content_label = label_new (7, FIND_X / 2 + 1, _("Content:"));
638 widget_disable (content_label->widget, disable);
639 add_widget (find_dlg, content_label);
641 in_name = input_new (8, 3, input_get_default_colors (),
642 FIND_X / 2 - 4, INPUT_LAST_TEXT, "name", INPUT_COMPLETE_DEFAULT);
643 add_widget (find_dlg, in_name);
644 add_widget (find_dlg, label_new (7, 3, _("File name:")));
646 in_ignore = input_new (5, 3, input_get_default_colors (), FIND_X - 6,
647 options.ignore_dirs != NULL ? options.ignore_dirs : "",
648 "ignoredirs", INPUT_COMPLETE_DEFAULT);
649 widget_disable (in_ignore->widget, !options.ignore_dirs_enable);
650 add_widget (find_dlg, in_ignore);
652 ignore_dirs_cbox =
653 check_new (4, 3, options.ignore_dirs_enable, _("Ena&ble ignore directories:"));
654 add_widget (find_dlg, ignore_dirs_cbox);
656 add_widget (find_dlg, button_new (3, FIND_X - b2 - 2, B_TREE, NORMAL_BUTTON, buts[2], 0));
658 in_start = input_new (3, 3, input_get_default_colors (),
659 FIND_X - b2 - 6, in_start_dir, "start", INPUT_COMPLETE_DEFAULT);
660 add_widget (find_dlg, in_start);
661 add_widget (find_dlg, label_new (2, 3, _("Start at:")));
663 find_par_start:
664 dlg_select_widget (in_name);
666 switch (run_dlg (find_dlg))
668 case B_CANCEL:
669 return_value = FALSE;
670 break;
672 case B_TREE:
674 const char *temp_dir = in_start->buffer;
676 if ((temp_dir[0] == '\0') || ((temp_dir[0] == '.') && (temp_dir[1] == '\0')))
677 temp_dir = current_panel->cwd;
679 if (in_start_dir != INPUT_LAST_TEXT)
680 g_free (in_start_dir);
681 in_start_dir = tree_box (temp_dir);
682 if (in_start_dir == NULL)
683 in_start_dir = g_strdup (temp_dir);
685 input_assign_text (in_start, in_start_dir);
687 /* Warning: Dreadful goto */
688 goto find_par_start;
691 default:
693 char *s;
695 #ifdef HAVE_CHARSET
696 options.file_all_charsets = file_all_charsets_cbox->state & C_BOOL;
697 options.content_all_charsets = content_all_charsets_cbox->state & C_BOOL;
698 #endif
699 options.content_use = content_use_cbox->state & C_BOOL;
700 options.content_case_sens = content_case_sens_cbox->state & C_BOOL;
701 options.content_regexp = content_regexp_cbox->state & C_BOOL;
702 options.content_first_hit = content_first_hit_cbox->state & C_BOOL;
703 options.content_whole_words = content_whole_words_cbox->state & C_BOOL;
704 options.find_recurs = recursively_cbox->state & C_BOOL;
705 options.file_pattern = file_pattern_cbox->state & C_BOOL;
706 options.file_case_sens = file_case_sens_cbox->state & C_BOOL;
707 options.skip_hidden = skip_hidden_cbox->state & C_BOOL;
708 options.ignore_dirs_enable = ignore_dirs_cbox->state & C_BOOL;
709 g_free (options.ignore_dirs);
710 options.ignore_dirs = g_strdup (in_ignore->buffer);
712 *content = (options.content_use && in_with->buffer[0] != '\0')
713 ? g_strdup (in_with->buffer) : NULL;
714 *start_dir = in_start->buffer[0] != '\0' ? in_start->buffer : (char *) ".";
715 *pattern = g_strdup (in_name->buffer);
716 if (in_start_dir != INPUT_LAST_TEXT)
717 g_free (in_start_dir);
718 in_start_dir = g_strdup (*start_dir);
720 s = g_strdup (*start_dir);
721 canonicalize_pathname (s);
723 if (s[0] == '.' && s[1] == '\0')
725 *start_dir = g_strdup (current_panel->cwd);
726 /* FIXME: is current_panel->cwd canonicalized? */
727 /* relative paths will be used in panelization */
728 *start_dir_len = (ssize_t) strlen (current_panel->cwd);
729 g_free (s);
731 else if (g_path_is_absolute (s))
733 *start_dir = s;
734 *start_dir_len = -1;
736 else
738 /* relative paths will be used in panelization */
739 *start_dir = mc_build_filename (current_panel->cwd, s, (char *) NULL);
740 *start_dir_len = (ssize_t) strlen (current_panel->cwd);
741 g_free (s);
744 if (!options.ignore_dirs_enable || in_ignore->buffer[0] == '\0'
745 || (in_ignore->buffer[0] == '.' && in_ignore->buffer[1] == '\0'))
746 *ignore_dirs = NULL;
747 else
748 *ignore_dirs = g_strdup (in_ignore->buffer);
750 find_save_options ();
752 return_value = TRUE;
756 destroy_dlg (find_dlg);
758 return return_value;
761 /* --------------------------------------------------------------------------------------------- */
763 #if GLIB_CHECK_VERSION (2, 14, 0)
764 static inline void
765 push_directory (const char *dir)
767 g_queue_push_head (&dir_queue, (void *) dir);
770 /* --------------------------------------------------------------------------------------------- */
772 static inline char *
773 pop_directory (void)
775 return (char *) g_queue_pop_tail (&dir_queue);
778 /* --------------------------------------------------------------------------------------------- */
779 /** Remove all the items from the stack */
781 static void
782 clear_stack (void)
784 g_queue_foreach (&dir_queue, (GFunc) g_free, NULL);
785 g_queue_clear (&dir_queue);
788 /* --------------------------------------------------------------------------------------------- */
790 #else /* GLIB_CHECK_VERSION */
791 static void
792 push_directory (const char *dir)
794 dir_stack *new;
796 new = g_new (dir_stack, 1);
797 new->name = (char *) dir;
798 new->prev = dir_stack_base;
799 dir_stack_base = new;
802 /* --------------------------------------------------------------------------------------------- */
804 static char *
805 pop_directory (void)
807 char *name = NULL;
809 if (dir_stack_base != NULL)
811 dir_stack *next;
812 name = dir_stack_base->name;
813 next = dir_stack_base->prev;
814 g_free (dir_stack_base);
815 dir_stack_base = next;
818 return name;
821 /* --------------------------------------------------------------------------------------------- */
822 /** Remove all the items from the stack */
824 static void
825 clear_stack (void)
827 char *dir = NULL;
828 while ((dir = pop_directory ()) != NULL)
829 g_free (dir);
831 #endif /* GLIB_CHECK_VERSION */
833 /* --------------------------------------------------------------------------------------------- */
835 static void
836 insert_file (const char *dir, const char *file)
838 char *tmp_name = NULL;
839 static char *dirname = NULL;
841 while (dir[0] == PATH_SEP && dir[1] == PATH_SEP)
842 dir++;
844 if (old_dir)
846 if (strcmp (old_dir, dir))
848 g_free (old_dir);
849 old_dir = g_strdup (dir);
850 dirname = add_to_list (dir, NULL);
853 else
855 old_dir = g_strdup (dir);
856 dirname = add_to_list (dir, NULL);
859 tmp_name = g_strdup_printf (" %s", file);
860 add_to_list (tmp_name, dirname);
861 g_free (tmp_name);
864 /* --------------------------------------------------------------------------------------------- */
866 static void
867 find_add_match (const char *dir, const char *file)
869 insert_file (dir, file);
871 /* Don't scroll */
872 if (matches == 0)
873 listbox_select_first (find_list);
874 send_message (&find_list->widget, WIDGET_DRAW, 0);
876 matches++;
877 found_num_update ();
880 /* --------------------------------------------------------------------------------------------- */
882 * get_line_at:
884 * Returns malloced null-terminated line from file file_fd.
885 * Input is buffered in buf_size long buffer.
886 * Current pos in buf is stored in pos.
887 * n_read - number of read chars.
888 * has_newline - is there newline ?
891 static char *
892 get_line_at (int file_fd, char *buf, int buf_size, int *pos, int *n_read, gboolean * has_newline)
894 char *buffer = NULL;
895 int buffer_size = 0;
896 char ch = 0;
897 int i = 0;
899 for (;;)
901 if (*pos >= *n_read)
903 *pos = 0;
904 *n_read = mc_read (file_fd, buf, buf_size);
905 if (*n_read <= 0)
906 break;
909 ch = buf[(*pos)++];
910 if (ch == '\0')
912 /* skip possible leading zero(s) */
913 if (i == 0)
914 continue;
915 break;
918 if (i >= buffer_size - 1)
919 buffer = g_realloc (buffer, buffer_size += 80);
921 /* Strip newline */
922 if (ch == '\n')
923 break;
925 buffer[i++] = ch;
928 *has_newline = (ch != '\0');
930 if (buffer != NULL)
931 buffer[i] = '\0';
933 return buffer;
936 /* --------------------------------------------------------------------------------------------- */
938 static FindProgressStatus
939 check_find_events (Dlg_head * h)
941 Gpm_Event event;
942 int c;
944 event.x = -1;
945 c = tty_get_event (&event, h->mouse_status == MOU_REPEAT, FALSE);
946 if (c != EV_NONE)
948 dlg_process_event (h, c, &event);
949 if (h->ret_value == B_ENTER
950 || h->ret_value == B_CANCEL || h->ret_value == B_AGAIN || h->ret_value == B_PANELIZE)
952 /* dialog terminated */
953 return FIND_ABORT;
955 if (!(h->flags & DLG_WANT_IDLE))
957 /* searching suspended */
958 return FIND_SUSPEND;
962 return FIND_CONT;
965 /* --------------------------------------------------------------------------------------------- */
967 * search_content:
969 * Search the content_pattern string in the DIRECTORY/FILE.
970 * It will add the found entries to the find listbox.
972 * returns FALSE if do_search should look for another file
973 * TRUE if do_search should exit and proceed to the event handler
976 static gboolean
977 search_content (Dlg_head * h, const char *directory, const char *filename)
979 struct stat s;
980 char buffer[BUF_4K];
981 char *fname = NULL;
982 int file_fd;
983 gboolean ret_val = FALSE;
985 fname = mc_build_filename (directory, filename, (char *) NULL);
987 if (mc_stat (fname, &s) != 0 || !S_ISREG (s.st_mode))
989 g_free (fname);
990 return FALSE;
993 file_fd = mc_open (fname, O_RDONLY);
994 g_free (fname);
996 if (file_fd == -1)
997 return FALSE;
999 g_snprintf (buffer, sizeof (buffer), _("Grepping in %s"), str_trunc (filename, FIND2_X_USE));
1001 status_update (buffer);
1002 mc_refresh ();
1004 tty_enable_interrupt_key ();
1005 tty_got_interrupt ();
1008 int line = 1;
1009 int pos = 0;
1010 int n_read = 0;
1011 gboolean has_newline;
1012 char *p = NULL;
1013 gboolean found = FALSE;
1014 gsize found_len;
1015 char result[BUF_MEDIUM];
1017 if (resuming)
1019 /* We've been previously suspended, start from the previous position */
1020 resuming = 0;
1021 line = last_line;
1022 pos = last_pos;
1024 while (!ret_val
1025 && (p = get_line_at (file_fd, buffer, sizeof (buffer),
1026 &pos, &n_read, &has_newline)) != NULL)
1028 if (!found /* Search in binary line once */
1029 && mc_search_run (search_content_handle,
1030 (const void *) p, 0, strlen (p), &found_len))
1032 g_snprintf (result, sizeof (result), "%d:%s", line, filename);
1033 find_add_match (directory, result);
1034 found = TRUE;
1036 g_free (p);
1038 if (found && options.content_first_hit)
1039 break;
1041 if (has_newline)
1043 found = FALSE;
1044 line++;
1047 if ((line & 0xff) == 0)
1049 FindProgressStatus res;
1050 res = check_find_events (h);
1051 switch (res)
1053 case FIND_ABORT:
1054 stop_idle (h);
1055 ret_val = TRUE;
1056 break;
1057 case FIND_SUSPEND:
1058 resuming = 1;
1059 last_line = line;
1060 last_pos = pos;
1061 ret_val = TRUE;
1062 break;
1063 default:
1064 break;
1069 tty_disable_interrupt_key ();
1070 mc_close (file_fd);
1071 return ret_val;
1074 /* --------------------------------------------------------------------------------------------- */
1077 If dir is absolute, this means we're within dir and searching file here.
1078 If dir is relative, this means we're going to add dir to the directory stack.
1080 static gboolean
1081 find_ignore_dir_search (const char *dir)
1083 if (find_ignore_dirs != NULL)
1085 const size_t dlen = strlen (dir);
1086 const unsigned char dabs = g_path_is_absolute (dir) ? 1 : 0;
1088 char **ignore_dir;
1090 for (ignore_dir = find_ignore_dirs; *ignore_dir != NULL; ignore_dir++)
1092 const size_t ilen = strlen (*ignore_dir);
1093 const unsigned char iabs = g_path_is_absolute (*ignore_dir) ? 2 : 0;
1095 /* ignore dir is too long -- skip it */
1096 if (dlen < ilen)
1097 continue;
1099 /* handle absolute and relative paths */
1100 switch (iabs | dabs)
1102 case 0: /* both paths are relative */
1103 case 3: /* both paths are abolute */
1104 /* if ignore dir is not a path of dir -- skip it */
1105 if (strncmp (dir, *ignore_dir, ilen) == 0)
1107 /* be sure that ignore dir is not a part of dir like:
1108 ignore dir is "h", dir is "home" */
1109 if (dir[ilen] == '\0' || dir[ilen] == PATH_SEP)
1110 return TRUE;
1112 break;
1113 case 1: /* dir is absolute, ignore_dir is relative */
1115 char *d;
1117 d = strstr (dir, *ignore_dir);
1118 if (d != NULL && d[-1] == PATH_SEP && (d[ilen] == '\0' || d[ilen] == PATH_SEP))
1119 return TRUE;
1121 break;
1122 case 2: /* dir is relative, ignore_dir is absolute */
1123 /* FIXME: skip this case */
1124 break;
1125 default: /* this cannot occurs */
1126 return FALSE;
1131 return FALSE;
1134 /* --------------------------------------------------------------------------------------------- */
1136 static void
1137 find_rotate_dash (const Dlg_head * h, gboolean finish)
1139 static const char rotating_dash[] = "|/-\\";
1140 static unsigned int pos = 0;
1142 if (verbose)
1144 pos = (pos + 1) % 4;
1145 tty_setcolor (h->color[DLG_COLOR_NORMAL]);
1146 dlg_move (h, FIND2_Y - 7, FIND2_X - 4);
1147 tty_print_char (finish ? ' ' : rotating_dash[pos]);
1148 mc_refresh ();
1152 /* --------------------------------------------------------------------------------------------- */
1154 static int
1155 do_search (Dlg_head * h)
1157 static struct dirent *dp = NULL;
1158 static DIR *dirp = NULL;
1159 static char *directory = NULL;
1160 struct stat tmp_stat;
1161 static int subdirs_left = 0;
1162 gsize bytes_found;
1163 unsigned short count;
1165 if (h == NULL)
1166 { /* someone forces me to close dirp */
1167 if (dirp != NULL)
1169 mc_closedir (dirp);
1170 dirp = NULL;
1172 g_free (directory);
1173 directory = NULL;
1174 dp = NULL;
1175 return 1;
1178 for (count = 0; count < 32; count++)
1180 while (dp == NULL)
1182 if (dirp != NULL)
1184 mc_closedir (dirp);
1185 dirp = NULL;
1188 while (dirp == NULL)
1190 char *tmp = NULL;
1192 tty_setcolor (REVERSE_COLOR);
1194 while (TRUE)
1196 tmp = pop_directory ();
1197 if (tmp == NULL)
1199 running = FALSE;
1200 if (ignore_count == 0)
1201 status_update (_("Finished"));
1202 else
1204 char msg[BUF_SMALL];
1205 g_snprintf (msg, sizeof (msg),
1206 ngettext ("Finished (ignored %zd directory)",
1207 "Finished (ignored %zd directories)",
1208 ignore_count), ignore_count);
1209 status_update (msg);
1211 find_rotate_dash (h, TRUE);
1212 stop_idle (h);
1213 return 0;
1216 /* handle absolute ignore dirs here */
1217 if (!find_ignore_dir_search (tmp))
1218 break;
1220 g_free (tmp);
1221 ignore_count++;
1224 g_free (directory);
1225 directory = tmp;
1227 if (verbose)
1229 char buffer[BUF_SMALL];
1231 g_snprintf (buffer, sizeof (buffer), _("Searching %s"),
1232 str_trunc (directory, FIND2_X_USE));
1233 status_update (buffer);
1235 /* mc_stat should not be called after mc_opendir
1236 because vfs_s_opendir modifies the st_nlink
1238 if (mc_stat (directory, &tmp_stat) == 0)
1239 subdirs_left = tmp_stat.st_nlink - 2;
1240 else
1241 subdirs_left = 0;
1243 dirp = mc_opendir (directory);
1244 } /* while (!dirp) */
1246 /* skip invalid filenames */
1247 while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1249 } /* while (!dp) */
1251 if (strcmp (dp->d_name, ".") == 0 || strcmp (dp->d_name, "..") == 0)
1253 /* skip invalid filenames */
1254 while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1257 return 1;
1260 if (!(options.skip_hidden && (dp->d_name[0] == '.')))
1262 gboolean search_ok;
1264 if ((subdirs_left != 0) && options.find_recurs && (directory != NULL))
1265 { /* Can directory be NULL ? */
1266 /* handle relative ignore dirs here */
1267 if (options.ignore_dirs_enable && find_ignore_dir_search (dp->d_name))
1268 ignore_count++;
1269 else
1271 char *tmp_name;
1273 tmp_name = mc_build_filename (directory, dp->d_name, (char *) NULL);
1275 if (mc_lstat (tmp_name, &tmp_stat) == 0 && S_ISDIR (tmp_stat.st_mode))
1277 push_directory (tmp_name);
1278 subdirs_left--;
1280 else
1281 g_free (tmp_name);
1285 search_ok = mc_search_run (search_file_handle, dp->d_name,
1286 0, strlen (dp->d_name), &bytes_found);
1288 if (search_ok)
1290 if (content_pattern == NULL)
1291 find_add_match (directory, dp->d_name);
1292 else if (search_content (h, directory, dp->d_name))
1293 return 1;
1297 /* skip invalid filenames */
1298 while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1300 } /* for */
1302 find_rotate_dash (h, FALSE);
1304 return 1;
1307 /* --------------------------------------------------------------------------------------------- */
1309 static void
1310 init_find_vars (void)
1312 g_free (old_dir);
1313 old_dir = NULL;
1314 matches = 0;
1315 ignore_count = 0;
1317 /* Remove all the items from the stack */
1318 clear_stack ();
1320 g_strfreev (find_ignore_dirs);
1321 find_ignore_dirs = NULL;
1324 /* --------------------------------------------------------------------------------------------- */
1326 static void
1327 find_do_view_edit (int unparsed_view, int edit, char *dir, char *file)
1329 char *fullname = NULL;
1330 const char *filename = NULL;
1331 int line;
1333 if (content_pattern != NULL)
1335 filename = strchr (file + 4, ':') + 1;
1336 line = atoi (file + 4);
1338 else
1340 filename = file + 4;
1341 line = 0;
1344 fullname = mc_build_filename (dir, filename, (char *) NULL);
1345 if (edit)
1346 do_edit_at_line (fullname, use_internal_edit, line);
1347 else
1348 view_file_at_line (fullname, unparsed_view, use_internal_view, line);
1349 g_free (fullname);
1352 /* --------------------------------------------------------------------------------------------- */
1354 static cb_ret_t
1355 view_edit_currently_selected_file (int unparsed_view, int edit)
1357 char *dir = NULL;
1358 char *text = NULL;
1360 listbox_get_current (find_list, &text, (void **) &dir);
1362 if ((text == NULL) || (dir == NULL))
1363 return MSG_NOT_HANDLED;
1365 find_do_view_edit (unparsed_view, edit, dir, text);
1366 return MSG_HANDLED;
1369 /* --------------------------------------------------------------------------------------------- */
1371 static cb_ret_t
1372 find_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
1374 switch (msg)
1376 case DLG_KEY:
1377 if (parm == KEY_F (3) || parm == KEY_F (13))
1379 int unparsed_view = (parm == KEY_F (13));
1380 return view_edit_currently_selected_file (unparsed_view, 0);
1382 if (parm == KEY_F (4))
1384 return view_edit_currently_selected_file (0, 1);
1386 return MSG_NOT_HANDLED;
1388 case DLG_IDLE:
1389 do_search (h);
1390 return MSG_HANDLED;
1392 default:
1393 return default_dlg_callback (h, sender, msg, parm, data);
1397 /* --------------------------------------------------------------------------------------------- */
1398 /** Handles the Stop/Start button in the find window */
1400 static int
1401 start_stop (WButton * button, int action)
1403 (void) button;
1404 (void) action;
1406 running = is_start;
1407 set_idle_proc (find_dlg, running);
1408 is_start = !is_start;
1410 status_update (is_start ? _("Stopped") : _("Searching"));
1411 button_set_text (stop_button, fbuts[is_start ? 1 : 0].text);
1413 return 0;
1416 /* --------------------------------------------------------------------------------------------- */
1417 /** Handle view command, when invoked as a button */
1419 static int
1420 find_do_view_file (WButton * button, int action)
1422 (void) button;
1423 (void) action;
1425 view_edit_currently_selected_file (0, 0);
1426 return 0;
1429 /* --------------------------------------------------------------------------------------------- */
1430 /** Handle edit command, when invoked as a button */
1432 static int
1433 find_do_edit_file (WButton * button, int action)
1435 (void) button;
1436 (void) action;
1438 view_edit_currently_selected_file (0, 1);
1439 return 0;
1442 /* --------------------------------------------------------------------------------------------- */
1444 static void
1445 setup_gui (void)
1447 #ifdef ENABLE_NLS
1448 static gboolean i18n_flag = FALSE;
1450 if (!i18n_flag)
1452 int i = sizeof (fbuts) / sizeof (fbuts[0]);
1453 while (i-- != 0)
1455 fbuts[i].text = _(fbuts[i].text);
1456 fbuts[i].len = str_term_width1 (fbuts[i].text) + 3;
1459 fbuts[2].len += 2; /* DEFPUSH_BUTTON */
1460 i18n_flag = TRUE;
1462 #endif /* ENABLE_NLS */
1465 * Dynamically place buttons centered within current window size
1468 int l0 = max (fbuts[0].len, fbuts[1].len);
1469 int l1 = fbuts[2].len + fbuts[3].len + l0 + fbuts[4].len;
1470 int l2 = fbuts[5].len + fbuts[6].len + fbuts[7].len;
1471 int r1, r2;
1473 /* Check, if both button rows fit within FIND2_X */
1474 FIND2_X = max (l1 + 9, COLS - 16);
1475 FIND2_X = max (l2 + 8, FIND2_X);
1477 /* compute amount of space between buttons for each row */
1478 r1 = (FIND2_X - 4 - l1) % 5;
1479 l1 = (FIND2_X - 4 - l1) / 5;
1480 r2 = (FIND2_X - 4 - l2) % 4;
1481 l2 = (FIND2_X - 4 - l2) / 4;
1483 /* ...and finally, place buttons */
1484 fbuts[2].x = 2 + r1 / 2 + l1;
1485 fbuts[3].x = fbuts[2].x + fbuts[2].len + l1;
1486 fbuts[0].x = fbuts[3].x + fbuts[3].len + l1;
1487 fbuts[4].x = fbuts[0].x + l0 + l1;
1488 fbuts[5].x = 2 + r2 / 2 + l2;
1489 fbuts[6].x = fbuts[5].x + fbuts[5].len + l2;
1490 fbuts[7].x = fbuts[6].x + fbuts[6].len + l2;
1493 find_dlg =
1494 create_dlg (TRUE, 0, 0, FIND2_Y, FIND2_X, dialog_colors, find_callback,
1495 "[Find File]", _("Find File"), DLG_CENTER | DLG_REVERSE);
1497 add_widget (find_dlg,
1498 button_new (FIND2_Y - 3, fbuts[7].x, B_VIEW, NORMAL_BUTTON,
1499 fbuts[7].text, find_do_edit_file));
1500 add_widget (find_dlg,
1501 button_new (FIND2_Y - 3, fbuts[6].x, B_VIEW, NORMAL_BUTTON,
1502 fbuts[6].text, find_do_view_file));
1503 add_widget (find_dlg,
1504 button_new (FIND2_Y - 3, fbuts[5].x, B_PANELIZE, NORMAL_BUTTON, fbuts[5].text,
1505 NULL));
1507 add_widget (find_dlg,
1508 button_new (FIND2_Y - 4, fbuts[4].x, B_CANCEL, NORMAL_BUTTON, fbuts[4].text, NULL));
1509 stop_button =
1510 button_new (FIND2_Y - 4, fbuts[0].x, B_STOP, NORMAL_BUTTON, fbuts[0].text, start_stop);
1511 add_widget (find_dlg, stop_button);
1512 add_widget (find_dlg,
1513 button_new (FIND2_Y - 4, fbuts[3].x, B_AGAIN, NORMAL_BUTTON, fbuts[3].text, NULL));
1514 add_widget (find_dlg,
1515 button_new (FIND2_Y - 4, fbuts[2].x, B_ENTER, DEFPUSH_BUTTON, fbuts[2].text, NULL));
1517 status_label = label_new (FIND2_Y - 7, 4, _("Searching"));
1518 add_widget (find_dlg, status_label);
1520 found_num_label = label_new (FIND2_Y - 6, 4, "");
1521 add_widget (find_dlg, found_num_label);
1523 find_list = listbox_new (2, 2, FIND2_Y - 10, FIND2_X - 4, FALSE, NULL);
1524 add_widget (find_dlg, find_list);
1527 /* --------------------------------------------------------------------------------------------- */
1529 static int
1530 run_process (void)
1532 int ret;
1534 search_content_handle = mc_search_new (content_pattern, -1);
1535 if (search_content_handle)
1537 search_content_handle->search_type =
1538 options.content_regexp ? MC_SEARCH_T_REGEX : MC_SEARCH_T_NORMAL;
1539 search_content_handle->is_case_sensitive = options.content_case_sens;
1540 search_content_handle->whole_words = options.content_whole_words;
1541 search_content_handle->is_all_charsets = options.content_all_charsets;
1543 search_file_handle = mc_search_new (find_pattern, -1);
1544 search_file_handle->search_type = options.file_pattern ? MC_SEARCH_T_GLOB : MC_SEARCH_T_REGEX;
1545 search_file_handle->is_case_sensitive = options.file_case_sens;
1546 search_file_handle->is_all_charsets = options.file_all_charsets;
1547 search_file_handle->is_entire_line = options.file_pattern;
1549 resuming = 0;
1551 set_idle_proc (find_dlg, 1);
1552 ret = run_dlg (find_dlg);
1554 mc_search_free (search_file_handle);
1555 search_file_handle = NULL;
1556 mc_search_free (search_content_handle);
1557 search_content_handle = NULL;
1559 return ret;
1562 /* --------------------------------------------------------------------------------------------- */
1564 static void
1565 kill_gui (void)
1567 set_idle_proc (find_dlg, 0);
1568 destroy_dlg (find_dlg);
1571 /* --------------------------------------------------------------------------------------------- */
1573 static int
1574 do_find (const char *start_dir, ssize_t start_dir_len, const char *ignore_dirs,
1575 const char *pattern, const char *content, char **dirname, char **filename)
1577 int return_value = 0;
1578 char *dir_tmp = NULL, *file_tmp = NULL;
1580 setup_gui ();
1582 /* FIXME: Need to cleanup this, this ought to be passed non-globaly */
1583 find_pattern = (char *) pattern;
1585 content_pattern = NULL;
1586 if (options.content_use && content != NULL && str_is_valid_string (content))
1587 content_pattern = g_strdup (content);
1589 init_find_vars ();
1590 parse_ignore_dirs (ignore_dirs);
1591 push_directory (start_dir);
1593 return_value = run_process ();
1595 /* Clear variables */
1596 init_find_vars ();
1598 get_list_info (&file_tmp, &dir_tmp);
1600 if (dir_tmp)
1601 *dirname = g_strdup (dir_tmp);
1602 if (file_tmp)
1603 *filename = g_strdup (file_tmp);
1605 if (return_value == B_PANELIZE && *filename)
1607 int status, link_to_dir, stale_link;
1608 int next_free = 0;
1609 int i;
1610 struct stat st;
1611 GList *entry;
1612 dir_list *list = &current_panel->dir;
1613 char *name = NULL;
1615 for (i = 0, entry = find_list->list; entry != NULL; i++, entry = g_list_next (entry))
1617 const char *lc_filename = NULL;
1618 WLEntry *le = (WLEntry *) entry->data;
1619 char *p;
1621 if ((le->text == NULL) || (le->data == NULL))
1622 continue;
1624 if (content_pattern != NULL)
1625 lc_filename = strchr (le->text + 4, ':') + 1;
1626 else
1627 lc_filename = le->text + 4;
1629 name = mc_build_filename (le->data, lc_filename, (char *) NULL);
1630 /* skip initial start dir */
1631 if (start_dir_len < 0)
1632 p = name;
1633 else
1635 p = name + (size_t) start_dir_len;
1636 if (*p == PATH_SEP)
1637 p++;
1640 status = handle_path (list, p, &st, next_free, &link_to_dir, &stale_link);
1641 if (status == 0)
1643 g_free (name);
1644 continue;
1646 if (status == -1)
1648 g_free (name);
1649 break;
1652 /* don't add files more than once to the panel */
1653 if (content_pattern != NULL && next_free > 0
1654 && strcmp (list->list[next_free - 1].fname, p) == 0)
1656 g_free (name);
1657 continue;
1660 if (next_free == 0) /* first turn i.e clean old list */
1661 panel_clean_dir (current_panel);
1662 list->list[next_free].fnamelen = strlen (p);
1663 list->list[next_free].fname = g_strdup (p);
1664 list->list[next_free].f.marked = 0;
1665 list->list[next_free].f.link_to_dir = link_to_dir;
1666 list->list[next_free].f.stale_link = stale_link;
1667 list->list[next_free].f.dir_size_computed = 0;
1668 list->list[next_free].st = st;
1669 list->list[next_free].sort_key = NULL;
1670 list->list[next_free].second_sort_key = NULL;
1671 next_free++;
1672 g_free (name);
1673 if (!(next_free & 15))
1674 rotate_dash ();
1677 if (next_free)
1679 current_panel->count = next_free;
1680 current_panel->is_panelized = 1;
1682 /* absolute path */
1683 if (start_dir_len < 0)
1685 int ret;
1687 strcpy (current_panel->cwd, PATH_SEP_STR);
1688 ret = chdir (PATH_SEP_STR);
1693 g_free (content_pattern);
1694 kill_gui ();
1695 do_search (NULL); /* force do_search to release resources */
1696 g_free (old_dir);
1697 old_dir = NULL;
1699 return return_value;
1702 /* --------------------------------------------------------------------------------------------- */
1703 /*** public functions ****************************************************************************/
1704 /* --------------------------------------------------------------------------------------------- */
1706 void
1707 find_file (void)
1709 char *start_dir = NULL, *pattern = NULL, *content = NULL, *ignore_dirs = NULL;
1710 ssize_t start_dir_len;
1711 char *filename = NULL, *dirname = NULL;
1712 int v;
1714 while (find_parameters (&start_dir, &start_dir_len, &ignore_dirs, &pattern, &content))
1716 if (pattern[0] == '\0')
1717 break; /* nothing search */
1719 dirname = filename = NULL;
1720 is_start = FALSE;
1721 v = do_find (start_dir, start_dir_len, ignore_dirs, pattern, content, &dirname, &filename);
1722 g_free (ignore_dirs);
1723 g_free (pattern);
1725 if (v == B_ENTER)
1727 if (dirname != NULL)
1729 do_cd (dirname, cd_exact);
1730 if (filename != NULL)
1731 try_to_select (current_panel,
1732 filename + (content != NULL
1733 ? strchr (filename + 4, ':') - filename + 1 : 4));
1735 else if (filename != NULL)
1736 do_cd (filename, cd_exact);
1738 g_free (dirname);
1739 g_free (filename);
1740 break;
1743 g_free (content);
1744 g_free (dirname);
1745 g_free (filename);
1747 if (v == B_CANCEL)
1748 break;
1750 if (v == B_PANELIZE)
1752 panel_re_sort (current_panel);
1753 try_to_select (current_panel, NULL);
1754 break;
1759 /* --------------------------------------------------------------------------------------------- */