Push/pop dirstack: changed type of saved items to vfs_path_t type.
[midnight-commander.git] / src / filemanager / find.c
blobbe0290165b5ad99f21aeb2f209730e6fccf57b69
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 #if 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 vfs_path_t *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;
270 char *ignore_dirs;
272 if (loaded)
273 return;
275 loaded = TRUE;
277 options.file_case_sens =
278 mc_config_get_bool (mc_main_config, "FindFile", "file_case_sens", TRUE);
279 options.file_pattern =
280 mc_config_get_bool (mc_main_config, "FindFile", "file_shell_pattern", TRUE);
281 options.find_recurs = mc_config_get_bool (mc_main_config, "FindFile", "file_find_recurs", TRUE);
282 options.skip_hidden =
283 mc_config_get_bool (mc_main_config, "FindFile", "file_skip_hidden", FALSE);
284 options.file_all_charsets =
285 mc_config_get_bool (mc_main_config, "FindFile", "file_all_charsets", FALSE);
286 options.content_use = mc_config_get_bool (mc_main_config, "FindFile", "content_use", TRUE);
287 options.content_case_sens =
288 mc_config_get_bool (mc_main_config, "FindFile", "content_case_sens", TRUE);
289 options.content_regexp =
290 mc_config_get_bool (mc_main_config, "FindFile", "content_regexp", FALSE);
291 options.content_first_hit =
292 mc_config_get_bool (mc_main_config, "FindFile", "content_first_hit", FALSE);
293 options.content_whole_words =
294 mc_config_get_bool (mc_main_config, "FindFile", "content_whole_words", FALSE);
295 options.content_all_charsets =
296 mc_config_get_bool (mc_main_config, "FindFile", "content_all_charsets", FALSE);
297 options.ignore_dirs_enable =
298 mc_config_get_bool (mc_main_config, "FindFile", "ignore_dirs_enable", TRUE);
300 /* Back compatibility: try load old parameter at first */
301 ignore_dirs = mc_config_get_string (mc_main_config, "Misc", "find_ignore_dirs", "");
302 mc_config_del_key (mc_main_config, "Misc", "find_ignore_dirs");
304 /* Then load new parameters */
305 options.ignore_dirs = mc_config_get_string (mc_main_config, "FindFile", "ignore_dirs", "");
306 if (options.ignore_dirs[0] != '\0')
307 g_free (ignore_dirs);
308 else
310 g_free (options.ignore_dirs);
311 options.ignore_dirs = ignore_dirs;
314 if (options.ignore_dirs[0] == '\0')
316 g_free (options.ignore_dirs);
317 options.ignore_dirs = NULL;
321 /* --------------------------------------------------------------------------------------------- */
323 static void
324 find_save_options (void)
326 mc_config_set_bool (mc_main_config, "FindFile", "file_case_sens", options.file_case_sens);
327 mc_config_set_bool (mc_main_config, "FindFile", "file_shell_pattern", options.file_pattern);
328 mc_config_set_bool (mc_main_config, "FindFile", "file_find_recurs", options.find_recurs);
329 mc_config_set_bool (mc_main_config, "FindFile", "file_skip_hidden", options.skip_hidden);
330 mc_config_set_bool (mc_main_config, "FindFile", "file_all_charsets", options.file_all_charsets);
331 mc_config_set_bool (mc_main_config, "FindFile", "content_use", options.content_use);
332 mc_config_set_bool (mc_main_config, "FindFile", "content_case_sens", options.content_case_sens);
333 mc_config_set_bool (mc_main_config, "FindFile", "content_regexp", options.content_regexp);
334 mc_config_set_bool (mc_main_config, "FindFile", "content_first_hit", options.content_first_hit);
335 mc_config_set_bool (mc_main_config, "FindFile", "content_whole_words",
336 options.content_whole_words);
337 mc_config_set_bool (mc_main_config, "FindFile", "content_all_charsets",
338 options.content_all_charsets);
339 mc_config_set_bool (mc_main_config, "FindFile", "ignore_dirs_enable",
340 options.ignore_dirs_enable);
341 mc_config_set_string (mc_main_config, "FindFile", "ignore_dirs", options.ignore_dirs);
344 /* --------------------------------------------------------------------------------------------- */
346 static inline char *
347 add_to_list (const char *text, void *data)
349 return listbox_add_item (find_list, LISTBOX_APPEND_AT_END, 0, text, data);
352 /* --------------------------------------------------------------------------------------------- */
354 static inline void
355 stop_idle (void *data)
357 set_idle_proc (data, 0);
360 /* --------------------------------------------------------------------------------------------- */
362 static inline void
363 status_update (const char *text)
365 label_set_text (status_label, text);
368 /* --------------------------------------------------------------------------------------------- */
370 static void
371 found_num_update (void)
373 char buffer[BUF_TINY];
374 g_snprintf (buffer, sizeof (buffer), _("Found: %ld"), matches);
375 label_set_text (found_num_label, buffer);
378 /* --------------------------------------------------------------------------------------------- */
380 static void
381 get_list_info (char **file, char **dir)
383 listbox_get_current (find_list, file, (void **) dir);
386 /* --------------------------------------------------------------------------------------------- */
387 /** check regular expression */
389 static gboolean
390 find_check_regexp (const char *r)
392 mc_search_t *search;
393 gboolean regexp_ok = FALSE;
395 search = mc_search_new (r, -1);
397 if (search != NULL)
399 search->search_type = MC_SEARCH_T_REGEX;
400 regexp_ok = mc_search_prepare (search);
401 mc_search_free (search);
404 return regexp_ok;
407 /* --------------------------------------------------------------------------------------------- */
409 * Callback for the parameter dialog.
410 * Validate regex, prevent closing the dialog if it's invalid.
413 static cb_ret_t
414 find_parm_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
416 switch (msg)
418 case DLG_ACTION:
419 if (sender == (Widget *) content_use_cbox)
421 gboolean disable = !(content_use_cbox->state & C_BOOL);
423 widget_disable (content_label->widget, disable);
424 send_message ((Widget *) content_label, WIDGET_DRAW, 0);
425 widget_disable (in_with->widget, disable);
426 send_message ((Widget *) in_with, WIDGET_DRAW, 0);
427 widget_disable (content_first_hit_cbox->widget, disable);
428 send_message ((Widget *) content_first_hit_cbox, WIDGET_DRAW, 0);
429 widget_disable (content_regexp_cbox->widget, disable);
430 send_message ((Widget *) content_regexp_cbox, WIDGET_DRAW, 0);
431 widget_disable (content_case_sens_cbox->widget, disable);
432 send_message ((Widget *) content_case_sens_cbox, WIDGET_DRAW, 0);
433 #ifdef HAVE_CHARSET
434 widget_disable (content_all_charsets_cbox->widget, disable);
435 send_message ((Widget *) content_all_charsets_cbox, WIDGET_DRAW, 0);
436 #endif
437 widget_disable (content_whole_words_cbox->widget, disable);
438 send_message ((Widget *) content_whole_words_cbox, WIDGET_DRAW, 0);
440 return MSG_HANDLED;
443 if (sender == (Widget *) ignore_dirs_cbox)
445 gboolean disable = !(ignore_dirs_cbox->state & C_BOOL);
447 widget_disable (in_ignore->widget, disable);
448 send_message ((Widget *) in_ignore, WIDGET_DRAW, 0);
450 return MSG_HANDLED;
453 return MSG_NOT_HANDLED;
456 case DLG_VALIDATE:
457 if (h->ret_value != B_ENTER)
458 return MSG_HANDLED;
460 /* check filename regexp */
461 if (!(file_pattern_cbox->state & C_BOOL)
462 && (in_name->buffer[0] != '\0') && !find_check_regexp (in_name->buffer))
464 h->state = DLG_ACTIVE; /* Don't stop the dialog */
465 message (D_ERROR, MSG_ERROR, _("Malformed regular expression"));
466 dlg_select_widget (in_name);
467 return MSG_HANDLED;
470 /* check content regexp */
471 if ((content_use_cbox->state & C_BOOL) && (content_regexp_cbox->state & C_BOOL)
472 && (in_with->buffer[0] != '\0') && !find_check_regexp (in_with->buffer))
474 h->state = DLG_ACTIVE; /* Don't stop the dialog */
475 message (D_ERROR, MSG_ERROR, _("Malformed regular expression"));
476 dlg_select_widget (in_with);
477 return MSG_HANDLED;
480 return MSG_HANDLED;
482 default:
483 return default_dlg_callback (h, sender, msg, parm, data);
487 /* --------------------------------------------------------------------------------------------- */
489 * find_parameters: gets information from the user
491 * If the return value is TRUE, then the following holds:
493 * start_dir, ignore_dirs, pattern and content contain the information provided by the user.
494 * They are newly allocated strings and must be freed when uneeded.
496 * start_dir_len is -1 when user entered an absolute path, otherwise it is a length
497 * of start_dir (which is absolute). It is used to get a relative pats of find results.
500 static gboolean
501 find_parameters (char **start_dir, ssize_t * start_dir_len,
502 char **ignore_dirs, char **pattern, char **content)
504 gboolean return_value;
506 /* file name */
507 const char *file_case_label = N_("Cas&e sensitive");
508 const char *file_pattern_label = N_("&Using shell patterns");
509 const char *file_recurs_label = N_("&Find recursively");
510 const char *file_skip_hidden_label = N_("S&kip hidden");
511 #ifdef HAVE_CHARSET
512 const char *file_all_charsets_label = N_("&All charsets");
513 #endif
515 /* file content */
516 const char *content_use_label = N_("Sea&rch for content");
517 const char *content_case_label = N_("Case sens&itive");
518 const char *content_regexp_label = N_("Re&gular expression");
519 const char *content_first_hit_label = N_("Fir&st hit");
520 const char *content_whole_words_label = N_("&Whole words");
521 #ifdef HAVE_CHARSET
522 const char *content_all_charsets_label = N_("A&ll charsets");
523 #endif
525 const char *buts[] = { N_("&OK"), N_("&Cancel"), N_("&Tree") };
527 int b0, b1, b2;
529 int cbox_position;
530 gboolean disable;
532 #ifdef ENABLE_NLS
534 int i = sizeof (buts) / sizeof (buts[0]);
535 while (i-- != 0)
536 buts[i] = _(buts[i]);
538 file_case_label = _(file_case_label);
539 file_pattern_label = _(file_pattern_label);
540 file_recurs_label = _(file_recurs_label);
541 file_skip_hidden_label = _(file_skip_hidden_label);
542 #ifdef HAVE_CHARSET
543 file_all_charsets_label = _(file_all_charsets_label);
544 content_all_charsets_label = _(content_all_charsets_label);
545 #endif
546 content_use_label = _(content_use_label);
547 content_case_label = _(content_case_label);
548 content_regexp_label = _(content_regexp_label);
549 content_first_hit_label = _(content_first_hit_label);
550 content_whole_words_label = _(content_whole_words_label);
552 #endif /* ENABLE_NLS */
554 b0 = str_term_width1 (buts[0]) + 6; /* default button */
555 b1 = str_term_width1 (buts[1]) + 4;
556 b2 = str_term_width1 (buts[2]) + 4;
558 find_load_options ();
560 if (in_start_dir == NULL)
561 in_start_dir = g_strdup (".");
563 disable = !options.content_use;
565 find_dlg =
566 create_dlg (TRUE, 0, 0, FIND_Y, FIND_X, dialog_colors,
567 find_parm_callback, "[Find File]", _("Find File"), DLG_CENTER | DLG_REVERSE);
569 add_widget (find_dlg,
570 button_new (FIND_Y - 3, FIND_X * 3 / 4 - b1 / 2, B_CANCEL, NORMAL_BUTTON, buts[1],
571 0));
572 add_widget (find_dlg,
573 button_new (FIND_Y - 3, FIND_X / 4 - b0 / 2, B_ENTER, DEFPUSH_BUTTON, buts[0], 0));
575 cbox_position = FIND_Y - 5;
577 content_first_hit_cbox =
578 check_new (cbox_position--, FIND_X / 2 + 1, options.content_first_hit,
579 content_first_hit_label);
580 widget_disable (content_first_hit_cbox->widget, disable);
581 add_widget (find_dlg, content_first_hit_cbox);
583 content_whole_words_cbox =
584 check_new (cbox_position--, FIND_X / 2 + 1, options.content_whole_words,
585 content_whole_words_label);
586 widget_disable (content_whole_words_cbox->widget, disable);
587 add_widget (find_dlg, content_whole_words_cbox);
589 #ifdef HAVE_CHARSET
590 content_all_charsets_cbox = check_new (cbox_position--, FIND_X / 2 + 1,
591 options.content_all_charsets,
592 content_all_charsets_label);
593 widget_disable (content_all_charsets_cbox->widget, disable);
594 add_widget (find_dlg, content_all_charsets_cbox);
595 #endif
597 content_case_sens_cbox =
598 check_new (cbox_position--, FIND_X / 2 + 1, options.content_case_sens, content_case_label);
599 widget_disable (content_case_sens_cbox->widget, disable);
600 add_widget (find_dlg, content_case_sens_cbox);
602 content_regexp_cbox =
603 check_new (cbox_position--, FIND_X / 2 + 1, options.content_regexp, content_regexp_label);
604 widget_disable (content_regexp_cbox->widget, disable);
605 add_widget (find_dlg, content_regexp_cbox);
607 cbox_position = FIND_Y - 6;
609 skip_hidden_cbox = check_new (cbox_position--, 3, options.skip_hidden, file_skip_hidden_label);
610 add_widget (find_dlg, skip_hidden_cbox);
612 #ifdef HAVE_CHARSET
613 file_all_charsets_cbox =
614 check_new (cbox_position--, 3, options.file_all_charsets, file_all_charsets_label);
615 add_widget (find_dlg, file_all_charsets_cbox);
616 #endif
618 file_case_sens_cbox = check_new (cbox_position--, 3, options.file_case_sens, file_case_label);
619 add_widget (find_dlg, file_case_sens_cbox);
621 file_pattern_cbox = check_new (cbox_position--, 3, options.file_pattern, file_pattern_label);
622 add_widget (find_dlg, file_pattern_cbox);
624 recursively_cbox = check_new (cbox_position, 3, options.find_recurs, file_recurs_label);
625 add_widget (find_dlg, recursively_cbox);
627 /* This checkbox is located in the second column */
628 content_use_cbox =
629 check_new (cbox_position, FIND_X / 2 + 1, options.content_use, content_use_label);
630 add_widget (find_dlg, content_use_cbox);
632 in_with =
633 input_new (8, FIND_X / 2 + 1, input_get_default_colors (), FIND_X / 2 - 4, INPUT_LAST_TEXT,
634 MC_HISTORY_SHARED_SEARCH, INPUT_COMPLETE_DEFAULT);
635 widget_disable (in_with->widget, disable);
636 add_widget (find_dlg, in_with);
638 content_label = label_new (7, FIND_X / 2 + 1, _("Content:"));
639 widget_disable (content_label->widget, disable);
640 add_widget (find_dlg, content_label);
642 in_name = input_new (8, 3, input_get_default_colors (),
643 FIND_X / 2 - 4, INPUT_LAST_TEXT, "name", INPUT_COMPLETE_DEFAULT);
644 add_widget (find_dlg, in_name);
645 add_widget (find_dlg, label_new (7, 3, _("File name:")));
647 in_ignore = input_new (5, 3, input_get_default_colors (), FIND_X - 6,
648 options.ignore_dirs != NULL ? options.ignore_dirs : "",
649 "ignoredirs", INPUT_COMPLETE_DEFAULT);
650 widget_disable (in_ignore->widget, !options.ignore_dirs_enable);
651 add_widget (find_dlg, in_ignore);
653 ignore_dirs_cbox =
654 check_new (4, 3, options.ignore_dirs_enable, _("Ena&ble ignore directories:"));
655 add_widget (find_dlg, ignore_dirs_cbox);
657 add_widget (find_dlg, button_new (3, FIND_X - b2 - 2, B_TREE, NORMAL_BUTTON, buts[2], 0));
659 in_start = input_new (3, 3, input_get_default_colors (),
660 FIND_X - b2 - 6, in_start_dir, "start", INPUT_COMPLETE_DEFAULT);
661 add_widget (find_dlg, in_start);
662 add_widget (find_dlg, label_new (2, 3, _("Start at:")));
664 find_par_start:
665 dlg_select_widget (in_name);
667 switch (run_dlg (find_dlg))
669 case B_CANCEL:
670 return_value = FALSE;
671 break;
673 case B_TREE:
675 char *temp_dir;
677 temp_dir = in_start->buffer;
678 if ((temp_dir[0] == '\0') || ((temp_dir[0] == '.') && (temp_dir[1] == '\0')))
679 temp_dir = vfs_path_to_str (current_panel->cwd_vpath);
680 else
681 temp_dir = g_strdup (temp_dir);
683 if (in_start_dir != INPUT_LAST_TEXT)
684 g_free (in_start_dir);
685 in_start_dir = tree_box (temp_dir);
686 if (in_start_dir == NULL)
687 in_start_dir = temp_dir;
688 else
689 g_free (temp_dir);
691 input_assign_text (in_start, in_start_dir);
693 /* Warning: Dreadful goto */
694 goto find_par_start;
697 default:
699 char *s;
701 #ifdef HAVE_CHARSET
702 options.file_all_charsets = file_all_charsets_cbox->state & C_BOOL;
703 options.content_all_charsets = content_all_charsets_cbox->state & C_BOOL;
704 #endif
705 options.content_use = content_use_cbox->state & C_BOOL;
706 options.content_case_sens = content_case_sens_cbox->state & C_BOOL;
707 options.content_regexp = content_regexp_cbox->state & C_BOOL;
708 options.content_first_hit = content_first_hit_cbox->state & C_BOOL;
709 options.content_whole_words = content_whole_words_cbox->state & C_BOOL;
710 options.find_recurs = recursively_cbox->state & C_BOOL;
711 options.file_pattern = file_pattern_cbox->state & C_BOOL;
712 options.file_case_sens = file_case_sens_cbox->state & C_BOOL;
713 options.skip_hidden = skip_hidden_cbox->state & C_BOOL;
714 options.ignore_dirs_enable = ignore_dirs_cbox->state & C_BOOL;
715 g_free (options.ignore_dirs);
716 options.ignore_dirs = g_strdup (in_ignore->buffer);
718 *content = (options.content_use && in_with->buffer[0] != '\0')
719 ? g_strdup (in_with->buffer) : NULL;
720 *start_dir = in_start->buffer[0] != '\0' ? in_start->buffer : (char *) ".";
721 *pattern = g_strdup (in_name->buffer);
722 if (in_start_dir != INPUT_LAST_TEXT)
723 g_free (in_start_dir);
724 in_start_dir = g_strdup (*start_dir);
726 s = tilde_expand (*start_dir);
727 canonicalize_pathname (s);
729 if (s[0] == '.' && s[1] == '\0')
731 *start_dir = vfs_path_to_str (current_panel->cwd_vpath);
732 /* FIXME: is current_panel->cwd_vpath canonicalized? */
733 /* relative paths will be used in panelization */
734 *start_dir_len = (ssize_t) strlen (*start_dir);
735 g_free (s);
737 else if (g_path_is_absolute (s))
739 *start_dir = s;
740 *start_dir_len = -1;
742 else
744 /* relative paths will be used in panelization */
745 char *cwd_str;
747 cwd_str = vfs_path_to_str (current_panel->cwd_vpath);
748 *start_dir = mc_build_filename (cwd_str, s, (char *) NULL);
749 *start_dir_len = (ssize_t) strlen (cwd_str);
750 g_free (cwd_str);
751 g_free (s);
754 if (!options.ignore_dirs_enable || in_ignore->buffer[0] == '\0'
755 || (in_ignore->buffer[0] == '.' && in_ignore->buffer[1] == '\0'))
756 *ignore_dirs = NULL;
757 else
758 *ignore_dirs = g_strdup (in_ignore->buffer);
760 find_save_options ();
762 return_value = TRUE;
766 destroy_dlg (find_dlg);
768 return return_value;
771 /* --------------------------------------------------------------------------------------------- */
773 #if GLIB_CHECK_VERSION (2, 14, 0)
774 static inline void
775 push_directory (const vfs_path_t * dir)
777 g_queue_push_head (&dir_queue, (void *) dir);
780 /* --------------------------------------------------------------------------------------------- */
782 static inline vfs_path_t *
783 pop_directory (void)
785 return (vfs_path_t *) g_queue_pop_tail (&dir_queue);
788 /* --------------------------------------------------------------------------------------------- */
789 /** Remove all the items from the stack */
791 static void
792 clear_stack (void)
794 g_queue_foreach (&dir_queue, (GFunc) vfs_path_free, NULL);
795 g_queue_clear (&dir_queue);
798 /* --------------------------------------------------------------------------------------------- */
800 #else /* GLIB_CHECK_VERSION */
801 static void
802 push_directory (const vfs_path_t * dir)
804 dir_stack *new;
806 new = g_new (dir_stack, 1);
807 new->name = (vfs_path_t *) dir;
808 new->prev = dir_stack_base;
809 dir_stack_base = new;
812 /* --------------------------------------------------------------------------------------------- */
814 static vfs_path_t *
815 pop_directory (void)
817 vfs_path_t *name = NULL;
819 if (dir_stack_base != NULL)
821 dir_stack *next;
822 name = dir_stack_base->name;
823 next = dir_stack_base->prev;
824 g_free (dir_stack_base);
825 dir_stack_base = next;
828 return name;
831 /* --------------------------------------------------------------------------------------------- */
832 /** Remove all the items from the stack */
834 static void
835 clear_stack (void)
837 vfs_path_t *dir = NULL;
839 while ((dir = pop_directory ()) != NULL)
840 vfs_path_free (dir);
842 #endif /* GLIB_CHECK_VERSION */
844 /* --------------------------------------------------------------------------------------------- */
846 static void
847 insert_file (const char *dir, const char *file)
849 char *tmp_name = NULL;
850 static char *dirname = NULL;
852 while (dir[0] == PATH_SEP && dir[1] == PATH_SEP)
853 dir++;
855 if (old_dir)
857 if (strcmp (old_dir, dir))
859 g_free (old_dir);
860 old_dir = g_strdup (dir);
861 dirname = add_to_list (dir, NULL);
864 else
866 old_dir = g_strdup (dir);
867 dirname = add_to_list (dir, NULL);
870 tmp_name = g_strdup_printf (" %s", file);
871 add_to_list (tmp_name, dirname);
872 g_free (tmp_name);
875 /* --------------------------------------------------------------------------------------------- */
877 static void
878 find_add_match (const char *dir, const char *file)
880 insert_file (dir, file);
882 /* Don't scroll */
883 if (matches == 0)
884 listbox_select_first (find_list);
885 send_message (&find_list->widget, WIDGET_DRAW, 0);
887 matches++;
888 found_num_update ();
891 /* --------------------------------------------------------------------------------------------- */
893 * get_line_at:
895 * Returns malloced null-terminated line from file file_fd.
896 * Input is buffered in buf_size long buffer.
897 * Current pos in buf is stored in pos.
898 * n_read - number of read chars.
899 * has_newline - is there newline ?
902 static char *
903 get_line_at (int file_fd, char *buf, int buf_size, int *pos, int *n_read, gboolean * has_newline)
905 char *buffer = NULL;
906 int buffer_size = 0;
907 char ch = 0;
908 int i = 0;
910 for (;;)
912 if (*pos >= *n_read)
914 *pos = 0;
915 *n_read = mc_read (file_fd, buf, buf_size);
916 if (*n_read <= 0)
917 break;
920 ch = buf[(*pos)++];
921 if (ch == '\0')
923 /* skip possible leading zero(s) */
924 if (i == 0)
925 continue;
926 break;
929 if (i >= buffer_size - 1)
930 buffer = g_realloc (buffer, buffer_size += 80);
932 /* Strip newline */
933 if (ch == '\n')
934 break;
936 buffer[i++] = ch;
939 *has_newline = (ch != '\0');
941 if (buffer != NULL)
942 buffer[i] = '\0';
944 return buffer;
947 /* --------------------------------------------------------------------------------------------- */
949 static FindProgressStatus
950 check_find_events (Dlg_head * h)
952 Gpm_Event event;
953 int c;
955 event.x = -1;
956 c = tty_get_event (&event, h->mouse_status == MOU_REPEAT, FALSE);
957 if (c != EV_NONE)
959 dlg_process_event (h, c, &event);
960 if (h->ret_value == B_ENTER
961 || h->ret_value == B_CANCEL || h->ret_value == B_AGAIN || h->ret_value == B_PANELIZE)
963 /* dialog terminated */
964 return FIND_ABORT;
966 if (!(h->flags & DLG_WANT_IDLE))
968 /* searching suspended */
969 return FIND_SUSPEND;
973 return FIND_CONT;
976 /* --------------------------------------------------------------------------------------------- */
978 * search_content:
980 * Search the content_pattern string in the DIRECTORY/FILE.
981 * It will add the found entries to the find listbox.
983 * returns FALSE if do_search should look for another file
984 * TRUE if do_search should exit and proceed to the event handler
987 static gboolean
988 search_content (Dlg_head * h, const char *directory, const char *filename)
990 struct stat s;
991 char buffer[BUF_4K];
992 int file_fd;
993 gboolean ret_val = FALSE;
994 vfs_path_t *vpath;
996 vpath = vfs_path_build_filename (directory, filename, (char *) NULL);
998 if (mc_stat (vpath, &s) != 0 || !S_ISREG (s.st_mode))
1000 vfs_path_free (vpath);
1001 return FALSE;
1004 file_fd = mc_open (vpath, O_RDONLY);
1005 vfs_path_free (vpath);
1007 if (file_fd == -1)
1008 return FALSE;
1010 g_snprintf (buffer, sizeof (buffer), _("Grepping in %s"), str_trunc (filename, FIND2_X_USE));
1012 status_update (buffer);
1013 mc_refresh ();
1015 tty_enable_interrupt_key ();
1016 tty_got_interrupt ();
1019 int line = 1;
1020 int pos = 0;
1021 int n_read = 0;
1022 gboolean has_newline;
1023 char *p = NULL;
1024 gboolean found = FALSE;
1025 gsize found_len;
1026 char result[BUF_MEDIUM];
1028 if (resuming)
1030 /* We've been previously suspended, start from the previous position */
1031 resuming = 0;
1032 line = last_line;
1033 pos = last_pos;
1035 while (!ret_val
1036 && (p = get_line_at (file_fd, buffer, sizeof (buffer),
1037 &pos, &n_read, &has_newline)) != NULL)
1039 if (!found /* Search in binary line once */
1040 && mc_search_run (search_content_handle,
1041 (const void *) p, 0, strlen (p), &found_len))
1043 g_snprintf (result, sizeof (result), "%d:%s", line, filename);
1044 find_add_match (directory, result);
1045 found = TRUE;
1047 g_free (p);
1049 if (found && options.content_first_hit)
1050 break;
1052 if (has_newline)
1054 found = FALSE;
1055 line++;
1058 if ((line & 0xff) == 0)
1060 FindProgressStatus res;
1061 res = check_find_events (h);
1062 switch (res)
1064 case FIND_ABORT:
1065 stop_idle (h);
1066 ret_val = TRUE;
1067 break;
1068 case FIND_SUSPEND:
1069 resuming = 1;
1070 last_line = line;
1071 last_pos = pos;
1072 ret_val = TRUE;
1073 break;
1074 default:
1075 break;
1080 tty_disable_interrupt_key ();
1081 mc_close (file_fd);
1082 return ret_val;
1085 /* --------------------------------------------------------------------------------------------- */
1088 If dir is absolute, this means we're within dir and searching file here.
1089 If dir is relative, this means we're going to add dir to the directory stack.
1091 static gboolean
1092 find_ignore_dir_search (const char *dir)
1094 if (find_ignore_dirs != NULL)
1096 const size_t dlen = strlen (dir);
1097 const unsigned char dabs = g_path_is_absolute (dir) ? 1 : 0;
1099 char **ignore_dir;
1101 for (ignore_dir = find_ignore_dirs; *ignore_dir != NULL; ignore_dir++)
1103 const size_t ilen = strlen (*ignore_dir);
1104 const unsigned char iabs = g_path_is_absolute (*ignore_dir) ? 2 : 0;
1106 /* ignore dir is too long -- skip it */
1107 if (dlen < ilen)
1108 continue;
1110 /* handle absolute and relative paths */
1111 switch (iabs | dabs)
1113 case 0: /* both paths are relative */
1114 case 3: /* both paths are abolute */
1115 /* if ignore dir is not a path of dir -- skip it */
1116 if (strncmp (dir, *ignore_dir, ilen) == 0)
1118 /* be sure that ignore dir is not a part of dir like:
1119 ignore dir is "h", dir is "home" */
1120 if (dir[ilen] == '\0' || dir[ilen] == PATH_SEP)
1121 return TRUE;
1123 break;
1124 case 1: /* dir is absolute, ignore_dir is relative */
1126 char *d;
1128 d = strstr (dir, *ignore_dir);
1129 if (d != NULL && d[-1] == PATH_SEP && (d[ilen] == '\0' || d[ilen] == PATH_SEP))
1130 return TRUE;
1132 break;
1133 case 2: /* dir is relative, ignore_dir is absolute */
1134 /* FIXME: skip this case */
1135 break;
1136 default: /* this cannot occurs */
1137 return FALSE;
1142 return FALSE;
1145 /* --------------------------------------------------------------------------------------------- */
1147 static void
1148 find_rotate_dash (const Dlg_head * h, gboolean finish)
1150 static const char rotating_dash[] = "|/-\\";
1151 static unsigned int pos = 0;
1153 if (verbose)
1155 pos = (pos + 1) % 4;
1156 tty_setcolor (h->color[DLG_COLOR_NORMAL]);
1157 dlg_move (h, FIND2_Y - 7, FIND2_X - 4);
1158 tty_print_char (finish ? ' ' : rotating_dash[pos]);
1159 mc_refresh ();
1163 /* --------------------------------------------------------------------------------------------- */
1165 static int
1166 do_search (Dlg_head * h)
1168 static struct dirent *dp = NULL;
1169 static DIR *dirp = NULL;
1170 static char *directory = NULL;
1171 struct stat tmp_stat;
1172 static int subdirs_left = 0;
1173 gsize bytes_found;
1174 unsigned short count;
1176 if (h == NULL)
1177 { /* someone forces me to close dirp */
1178 if (dirp != NULL)
1180 mc_closedir (dirp);
1181 dirp = NULL;
1183 g_free (directory);
1184 directory = NULL;
1185 dp = NULL;
1186 return 1;
1189 for (count = 0; count < 32; count++)
1191 while (dp == NULL)
1193 if (dirp != NULL)
1195 mc_closedir (dirp);
1196 dirp = NULL;
1199 while (dirp == NULL)
1201 vfs_path_t *tmp_vpath = NULL;
1203 tty_setcolor (REVERSE_COLOR);
1205 while (TRUE)
1207 tmp_vpath = pop_directory ();
1208 if (tmp_vpath == NULL)
1210 running = FALSE;
1211 if (ignore_count == 0)
1212 status_update (_("Finished"));
1213 else
1215 char msg[BUF_SMALL];
1216 g_snprintf (msg, sizeof (msg),
1217 ngettext ("Finished (ignored %zd directory)",
1218 "Finished (ignored %zd directories)",
1219 ignore_count), ignore_count);
1220 status_update (msg);
1222 find_rotate_dash (h, TRUE);
1223 stop_idle (h);
1224 return 0;
1227 /* handle absolute ignore dirs here */
1229 char *tmp;
1230 gboolean ok;
1232 tmp = vfs_path_to_str (tmp_vpath);
1233 ok = find_ignore_dir_search (tmp);
1234 g_free (tmp);
1235 if (!ok)
1236 break;
1239 vfs_path_free (tmp_vpath);
1240 ignore_count++;
1243 g_free (directory);
1244 directory = vfs_path_to_str (tmp_vpath);
1246 if (verbose)
1248 char buffer[BUF_SMALL];
1250 g_snprintf (buffer, sizeof (buffer), _("Searching %s"),
1251 str_trunc (directory, FIND2_X_USE));
1252 status_update (buffer);
1254 /* mc_stat should not be called after mc_opendir
1255 because vfs_s_opendir modifies the st_nlink
1257 if (mc_stat (tmp_vpath, &tmp_stat) == 0)
1258 subdirs_left = tmp_stat.st_nlink - 2;
1259 else
1260 subdirs_left = 0;
1262 dirp = mc_opendir (tmp_vpath);
1263 vfs_path_free (tmp_vpath);
1264 } /* while (!dirp) */
1266 /* skip invalid filenames */
1267 while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1269 } /* while (!dp) */
1271 if (strcmp (dp->d_name, ".") == 0 || strcmp (dp->d_name, "..") == 0)
1273 /* skip invalid filenames */
1274 while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1277 return 1;
1280 if (!(options.skip_hidden && (dp->d_name[0] == '.')))
1282 gboolean search_ok;
1284 if ((subdirs_left != 0) && options.find_recurs && (directory != NULL))
1285 { /* Can directory be NULL ? */
1286 /* handle relative ignore dirs here */
1287 if (options.ignore_dirs_enable && find_ignore_dir_search (dp->d_name))
1288 ignore_count++;
1289 else
1291 vfs_path_t *tmp_vpath;
1293 tmp_vpath = vfs_path_build_filename (directory, dp->d_name, (char *) NULL);
1295 if (mc_lstat (tmp_vpath, &tmp_stat) == 0 && S_ISDIR (tmp_stat.st_mode))
1297 push_directory (tmp_vpath);
1298 subdirs_left--;
1300 else
1301 vfs_path_free (tmp_vpath);
1305 search_ok = mc_search_run (search_file_handle, dp->d_name,
1306 0, strlen (dp->d_name), &bytes_found);
1308 if (search_ok)
1310 if (content_pattern == NULL)
1311 find_add_match (directory, dp->d_name);
1312 else if (search_content (h, directory, dp->d_name))
1313 return 1;
1317 /* skip invalid filenames */
1318 while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1320 } /* for */
1322 find_rotate_dash (h, FALSE);
1324 return 1;
1327 /* --------------------------------------------------------------------------------------------- */
1329 static void
1330 init_find_vars (void)
1332 g_free (old_dir);
1333 old_dir = NULL;
1334 matches = 0;
1335 ignore_count = 0;
1337 /* Remove all the items from the stack */
1338 clear_stack ();
1340 g_strfreev (find_ignore_dirs);
1341 find_ignore_dirs = NULL;
1344 /* --------------------------------------------------------------------------------------------- */
1346 static void
1347 find_do_view_edit (int unparsed_view, int edit, char *dir, char *file)
1349 char *fullname = NULL;
1350 const char *filename = NULL;
1351 int line;
1353 if (content_pattern != NULL)
1355 filename = strchr (file + 4, ':') + 1;
1356 line = atoi (file + 4);
1358 else
1360 filename = file + 4;
1361 line = 0;
1364 fullname = mc_build_filename (dir, filename, (char *) NULL);
1365 if (edit)
1366 do_edit_at_line (fullname, use_internal_edit, line);
1367 else
1368 view_file_at_line (fullname, unparsed_view, use_internal_view, line);
1369 g_free (fullname);
1372 /* --------------------------------------------------------------------------------------------- */
1374 static cb_ret_t
1375 view_edit_currently_selected_file (int unparsed_view, int edit)
1377 char *dir = NULL;
1378 char *text = NULL;
1380 listbox_get_current (find_list, &text, (void **) &dir);
1382 if ((text == NULL) || (dir == NULL))
1383 return MSG_NOT_HANDLED;
1385 find_do_view_edit (unparsed_view, edit, dir, text);
1386 return MSG_HANDLED;
1389 /* --------------------------------------------------------------------------------------------- */
1391 static cb_ret_t
1392 find_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
1394 switch (msg)
1396 case DLG_KEY:
1397 if (parm == KEY_F (3) || parm == KEY_F (13))
1399 int unparsed_view = (parm == KEY_F (13));
1400 return view_edit_currently_selected_file (unparsed_view, 0);
1402 if (parm == KEY_F (4))
1404 return view_edit_currently_selected_file (0, 1);
1406 return MSG_NOT_HANDLED;
1408 case DLG_IDLE:
1409 do_search (h);
1410 return MSG_HANDLED;
1412 default:
1413 return default_dlg_callback (h, sender, msg, parm, data);
1417 /* --------------------------------------------------------------------------------------------- */
1418 /** Handles the Stop/Start button in the find window */
1420 static int
1421 start_stop (WButton * button, int action)
1423 (void) button;
1424 (void) action;
1426 running = is_start;
1427 set_idle_proc (find_dlg, running);
1428 is_start = !is_start;
1430 status_update (is_start ? _("Stopped") : _("Searching"));
1431 button_set_text (stop_button, fbuts[is_start ? 1 : 0].text);
1433 return 0;
1436 /* --------------------------------------------------------------------------------------------- */
1437 /** Handle view command, when invoked as a button */
1439 static int
1440 find_do_view_file (WButton * button, int action)
1442 (void) button;
1443 (void) action;
1445 view_edit_currently_selected_file (0, 0);
1446 return 0;
1449 /* --------------------------------------------------------------------------------------------- */
1450 /** Handle edit command, when invoked as a button */
1452 static int
1453 find_do_edit_file (WButton * button, int action)
1455 (void) button;
1456 (void) action;
1458 view_edit_currently_selected_file (0, 1);
1459 return 0;
1462 /* --------------------------------------------------------------------------------------------- */
1464 static void
1465 setup_gui (void)
1467 #ifdef ENABLE_NLS
1468 static gboolean i18n_flag = FALSE;
1470 if (!i18n_flag)
1472 int i = sizeof (fbuts) / sizeof (fbuts[0]);
1473 while (i-- != 0)
1475 fbuts[i].text = _(fbuts[i].text);
1476 fbuts[i].len = str_term_width1 (fbuts[i].text) + 3;
1479 fbuts[2].len += 2; /* DEFPUSH_BUTTON */
1480 i18n_flag = TRUE;
1482 #endif /* ENABLE_NLS */
1485 * Dynamically place buttons centered within current window size
1488 int l0 = max (fbuts[0].len, fbuts[1].len);
1489 int l1 = fbuts[2].len + fbuts[3].len + l0 + fbuts[4].len;
1490 int l2 = fbuts[5].len + fbuts[6].len + fbuts[7].len;
1491 int r1, r2;
1493 /* Check, if both button rows fit within FIND2_X */
1494 FIND2_X = max (l1 + 9, COLS - 16);
1495 FIND2_X = max (l2 + 8, FIND2_X);
1497 /* compute amount of space between buttons for each row */
1498 r1 = (FIND2_X - 4 - l1) % 5;
1499 l1 = (FIND2_X - 4 - l1) / 5;
1500 r2 = (FIND2_X - 4 - l2) % 4;
1501 l2 = (FIND2_X - 4 - l2) / 4;
1503 /* ...and finally, place buttons */
1504 fbuts[2].x = 2 + r1 / 2 + l1;
1505 fbuts[3].x = fbuts[2].x + fbuts[2].len + l1;
1506 fbuts[0].x = fbuts[3].x + fbuts[3].len + l1;
1507 fbuts[4].x = fbuts[0].x + l0 + l1;
1508 fbuts[5].x = 2 + r2 / 2 + l2;
1509 fbuts[6].x = fbuts[5].x + fbuts[5].len + l2;
1510 fbuts[7].x = fbuts[6].x + fbuts[6].len + l2;
1513 find_dlg =
1514 create_dlg (TRUE, 0, 0, FIND2_Y, FIND2_X, dialog_colors, find_callback,
1515 "[Find File]", _("Find File"), DLG_CENTER | DLG_REVERSE);
1517 add_widget (find_dlg,
1518 button_new (FIND2_Y - 3, fbuts[7].x, B_VIEW, NORMAL_BUTTON,
1519 fbuts[7].text, find_do_edit_file));
1520 add_widget (find_dlg,
1521 button_new (FIND2_Y - 3, fbuts[6].x, B_VIEW, NORMAL_BUTTON,
1522 fbuts[6].text, find_do_view_file));
1523 add_widget (find_dlg,
1524 button_new (FIND2_Y - 3, fbuts[5].x, B_PANELIZE, NORMAL_BUTTON, fbuts[5].text,
1525 NULL));
1527 add_widget (find_dlg,
1528 button_new (FIND2_Y - 4, fbuts[4].x, B_CANCEL, NORMAL_BUTTON, fbuts[4].text, NULL));
1529 stop_button =
1530 button_new (FIND2_Y - 4, fbuts[0].x, B_STOP, NORMAL_BUTTON, fbuts[0].text, start_stop);
1531 add_widget (find_dlg, stop_button);
1532 add_widget (find_dlg,
1533 button_new (FIND2_Y - 4, fbuts[3].x, B_AGAIN, NORMAL_BUTTON, fbuts[3].text, NULL));
1534 add_widget (find_dlg,
1535 button_new (FIND2_Y - 4, fbuts[2].x, B_ENTER, DEFPUSH_BUTTON, fbuts[2].text, NULL));
1537 status_label = label_new (FIND2_Y - 7, 4, _("Searching"));
1538 add_widget (find_dlg, status_label);
1540 found_num_label = label_new (FIND2_Y - 6, 4, "");
1541 add_widget (find_dlg, found_num_label);
1543 find_list = listbox_new (2, 2, FIND2_Y - 10, FIND2_X - 4, FALSE, NULL);
1544 add_widget (find_dlg, find_list);
1547 /* --------------------------------------------------------------------------------------------- */
1549 static int
1550 run_process (void)
1552 int ret;
1554 search_content_handle = mc_search_new (content_pattern, -1);
1555 if (search_content_handle)
1557 search_content_handle->search_type =
1558 options.content_regexp ? MC_SEARCH_T_REGEX : MC_SEARCH_T_NORMAL;
1559 search_content_handle->is_case_sensitive = options.content_case_sens;
1560 search_content_handle->whole_words = options.content_whole_words;
1561 search_content_handle->is_all_charsets = options.content_all_charsets;
1563 search_file_handle = mc_search_new (find_pattern, -1);
1564 search_file_handle->search_type = options.file_pattern ? MC_SEARCH_T_GLOB : MC_SEARCH_T_REGEX;
1565 search_file_handle->is_case_sensitive = options.file_case_sens;
1566 search_file_handle->is_all_charsets = options.file_all_charsets;
1567 search_file_handle->is_entire_line = options.file_pattern;
1569 resuming = 0;
1571 set_idle_proc (find_dlg, 1);
1572 ret = run_dlg (find_dlg);
1574 mc_search_free (search_file_handle);
1575 search_file_handle = NULL;
1576 mc_search_free (search_content_handle);
1577 search_content_handle = NULL;
1579 return ret;
1582 /* --------------------------------------------------------------------------------------------- */
1584 static void
1585 kill_gui (void)
1587 set_idle_proc (find_dlg, 0);
1588 destroy_dlg (find_dlg);
1591 /* --------------------------------------------------------------------------------------------- */
1593 static int
1594 do_find (const char *start_dir, ssize_t start_dir_len, const char *ignore_dirs,
1595 const char *pattern, const char *content, char **dirname, char **filename)
1597 int return_value = 0;
1598 char *dir_tmp = NULL, *file_tmp = NULL;
1600 setup_gui ();
1602 /* FIXME: Need to cleanup this, this ought to be passed non-globaly */
1603 find_pattern = (char *) pattern;
1605 content_pattern = NULL;
1606 if (options.content_use && content != NULL && str_is_valid_string (content))
1607 content_pattern = g_strdup (content);
1609 init_find_vars ();
1610 parse_ignore_dirs (ignore_dirs);
1611 push_directory (vfs_path_from_str (start_dir));
1613 return_value = run_process ();
1615 /* Clear variables */
1616 init_find_vars ();
1618 get_list_info (&file_tmp, &dir_tmp);
1620 if (dir_tmp)
1621 *dirname = g_strdup (dir_tmp);
1622 if (file_tmp)
1623 *filename = g_strdup (file_tmp);
1625 if (return_value == B_PANELIZE && *filename)
1627 int status, link_to_dir, stale_link;
1628 int next_free = 0;
1629 int i;
1630 struct stat st;
1631 GList *entry;
1632 dir_list *list = &current_panel->dir;
1633 char *name = NULL;
1635 if (set_zero_dir (list))
1636 next_free++;
1638 for (i = 0, entry = find_list->list; entry != NULL; i++, entry = g_list_next (entry))
1640 const char *lc_filename = NULL;
1641 WLEntry *le = (WLEntry *) entry->data;
1642 char *p;
1644 if ((le->text == NULL) || (le->data == NULL))
1645 continue;
1647 if (content_pattern != NULL)
1648 lc_filename = strchr (le->text + 4, ':') + 1;
1649 else
1650 lc_filename = le->text + 4;
1652 name = mc_build_filename (le->data, lc_filename, (char *) NULL);
1653 /* skip initial start dir */
1654 if (start_dir_len < 0)
1655 p = name;
1656 else
1658 p = name + (size_t) start_dir_len;
1659 if (*p == PATH_SEP)
1660 p++;
1663 status = handle_path (list, p, &st, next_free, &link_to_dir, &stale_link);
1664 if (status == 0)
1666 g_free (name);
1667 continue;
1669 if (status == -1)
1671 g_free (name);
1672 break;
1675 /* don't add files more than once to the panel */
1676 if (content_pattern != NULL && next_free > 0
1677 && strcmp (list->list[next_free - 1].fname, p) == 0)
1679 g_free (name);
1680 continue;
1683 if (next_free == 0) /* first turn i.e clean old list */
1684 panel_clean_dir (current_panel);
1685 list->list[next_free].fnamelen = strlen (p);
1686 list->list[next_free].fname = g_strndup (p, list->list[next_free].fnamelen);
1687 list->list[next_free].f.marked = 0;
1688 list->list[next_free].f.link_to_dir = link_to_dir;
1689 list->list[next_free].f.stale_link = stale_link;
1690 list->list[next_free].f.dir_size_computed = 0;
1691 list->list[next_free].st = st;
1692 list->list[next_free].sort_key = NULL;
1693 list->list[next_free].second_sort_key = NULL;
1694 next_free++;
1695 g_free (name);
1696 if (!(next_free & 15))
1697 rotate_dash ();
1700 if (next_free)
1702 current_panel->count = next_free;
1703 current_panel->is_panelized = TRUE;
1705 /* absolute path */
1706 if (start_dir_len < 0)
1708 int ret;
1709 vfs_path_free (current_panel->cwd_vpath);
1710 current_panel->cwd_vpath = vfs_path_from_str (PATH_SEP_STR);
1711 ret = chdir (PATH_SEP_STR);
1713 panelize_save_panel (current_panel);
1717 g_free (content_pattern);
1718 kill_gui ();
1719 do_search (NULL); /* force do_search to release resources */
1720 g_free (old_dir);
1721 old_dir = NULL;
1723 return return_value;
1726 /* --------------------------------------------------------------------------------------------- */
1727 /*** public functions ****************************************************************************/
1728 /* --------------------------------------------------------------------------------------------- */
1730 void
1731 find_file (void)
1733 char *start_dir = NULL, *pattern = NULL, *content = NULL, *ignore_dirs = NULL;
1734 ssize_t start_dir_len;
1735 char *filename = NULL, *dirname = NULL;
1736 int v;
1738 while (find_parameters (&start_dir, &start_dir_len, &ignore_dirs, &pattern, &content))
1740 if (pattern[0] == '\0')
1741 break; /* nothing search */
1743 dirname = filename = NULL;
1744 is_start = FALSE;
1745 v = do_find (start_dir, start_dir_len, ignore_dirs, pattern, content, &dirname, &filename);
1746 g_free (ignore_dirs);
1747 g_free (pattern);
1749 if (v == B_ENTER)
1751 if (dirname != NULL)
1753 do_cd (dirname, cd_exact);
1754 if (filename != NULL)
1755 try_to_select (current_panel,
1756 filename + (content != NULL
1757 ? strchr (filename + 4, ':') - filename + 1 : 4));
1759 else if (filename != NULL)
1760 do_cd (filename, cd_exact);
1762 g_free (dirname);
1763 g_free (filename);
1764 break;
1767 g_free (content);
1768 g_free (dirname);
1769 g_free (filename);
1771 if (v == B_CANCEL)
1772 break;
1774 if (v == B_PANELIZE)
1776 panel_re_sort (current_panel);
1777 try_to_select (current_panel, NULL);
1778 break;
1783 /* --------------------------------------------------------------------------------------------- */