(get_fs_usage): avoid compile warning about mixed declarations and code.
[midnight-commander.git] / src / filemanager / find.c
blob07ee2adc5a9a73343d8fb1504050a16b91805b8f
1 /*
2 Find file command for the Midnight Commander
4 Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
5 2006, 2007, 2011
6 The Free Software Foundation, Inc.
8 Written by:
9 Miguel de Icaza, 1995
11 This file is part of the Midnight Commander.
13 The Midnight Commander is free software: you can redistribute it
14 and/or modify it under the terms of the GNU General Public License as
15 published by the Free Software Foundation, either version 3 of the License,
16 or (at your option) any later version.
18 The Midnight Commander is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
23 You should have received a copy of the GNU General Public License
24 along with this program. If not, see <http://www.gnu.org/licenses/>.
27 /** \file find.c
28 * \brief Source: Find file command
31 #include <config.h>
33 #include <ctype.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <fcntl.h>
38 #include <sys/stat.h>
40 #include "lib/global.h"
42 #include "lib/tty/tty.h"
43 #include "lib/tty/key.h"
44 #include "lib/skin.h"
45 #include "lib/search.h"
46 #include "lib/mcconfig.h"
47 #include "lib/vfs/vfs.h"
48 #include "lib/strutil.h"
49 #include "lib/widget.h"
50 #include "lib/util.h" /* canonicalize_pathname() */
52 #include "src/setup.h" /* verbose */
53 #include "src/history.h" /* MC_HISTORY_SHARED_SEARCH */
54 #include "src/main.h" /* do_cd */
56 #include "dir.h"
57 #include "cmd.h" /* view_file_at_line */
58 #include "midnight.h" /* current_panel */
59 #include "boxes.h"
60 #include "panelize.h"
62 #include "find.h"
64 /*** global variables ****************************************************************************/
66 /*** file scope macro definitions ****************************************************************/
68 /* Size of the find window */
69 #define FIND2_Y (LINES - 4)
71 #define FIND2_X_USE (FIND2_X - 20)
73 /*** file scope type declarations ****************************************************************/
75 /* A couple of extra messages we need */
76 enum
78 B_STOP = B_USER + 1,
79 B_AGAIN,
80 B_PANELIZE,
81 B_TREE,
82 B_VIEW
85 typedef enum
87 FIND_CONT = 0,
88 FIND_SUSPEND,
89 FIND_ABORT
90 } FindProgressStatus;
92 /* find file options */
93 typedef struct
95 /* file name options */
96 gboolean file_case_sens;
97 gboolean file_pattern;
98 gboolean find_recurs;
99 gboolean skip_hidden;
100 gboolean file_all_charsets;
102 /* file content options */
103 gboolean content_use;
104 gboolean content_case_sens;
105 gboolean content_regexp;
106 gboolean content_first_hit;
107 gboolean content_whole_words;
108 gboolean content_all_charsets;
110 /* whether use ignore dirs or not */
111 gboolean ignore_dirs_enable;
112 /* list of directories to be ignored, separated by ':' */
113 char *ignore_dirs;
114 } find_file_options_t;
116 /*** file scope variables ************************************************************************/
118 /* Parsed ignore dirs */
119 static char **find_ignore_dirs = NULL;
121 /* Size of the find parameters window */
122 #ifdef HAVE_CHARSET
123 static int FIND_Y = 19;
124 #else
125 static int FIND_Y = 18;
126 #endif
127 static int FIND_X = 68;
129 static int FIND2_X = 64;
131 /* static variables to remember find parameters */
132 static WInput *in_start; /* Start path */
133 static WInput *in_name; /* Filename */
134 static WInput *in_with; /* Text */
135 static WInput *in_ignore;
136 static WLabel *content_label; /* 'Content:' label */
137 static WCheck *file_case_sens_cbox; /* "case sensitive" checkbox */
138 static WCheck *file_pattern_cbox; /* File name is glob or regexp */
139 static WCheck *recursively_cbox;
140 static WCheck *skip_hidden_cbox;
141 static WCheck *content_use_cbox; /* Take into account the Content field */
142 static WCheck *content_case_sens_cbox; /* "case sensitive" checkbox */
143 static WCheck *content_regexp_cbox; /* "find regular expression" checkbox */
144 static WCheck *content_first_hit_cbox; /* "First hit" checkbox" */
145 static WCheck *content_whole_words_cbox; /* "whole words" checkbox */
146 #ifdef HAVE_CHARSET
147 static WCheck *file_all_charsets_cbox;
148 static WCheck *content_all_charsets_cbox;
149 #endif
150 static WCheck *ignore_dirs_cbox;
152 static gboolean running = FALSE; /* nice flag */
153 static char *find_pattern = NULL; /* Pattern to search */
154 static char *content_pattern = NULL; /* pattern to search inside files; if
155 content_regexp_flag is true, it contains the
156 regex pattern, else the search string. */
157 static unsigned long matches; /* Number of matches */
158 static gboolean is_start = FALSE; /* Status of the start/stop toggle button */
159 static char *old_dir = NULL;
161 /* Where did we stop */
162 static int resuming;
163 static int last_line;
164 static int last_pos;
166 static size_t ignore_count = 0;
168 static Dlg_head *find_dlg; /* The dialog */
169 static WButton *stop_button; /* pointer to the stop button */
170 static WLabel *status_label; /* Finished, Searching etc. */
171 static WLabel *found_num_label; /* Number of found items */
172 static WListbox *find_list; /* Listbox with the file list */
174 /* This keeps track of the directory stack */
175 #if GLIB_CHECK_VERSION (2, 14, 0)
176 static GQueue dir_queue = G_QUEUE_INIT;
177 #else
178 typedef struct dir_stack
180 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;
271 if (loaded)
272 return;
274 loaded = TRUE;
276 options.file_case_sens =
277 mc_config_get_bool (mc_main_config, "FindFile", "file_case_sens", TRUE);
278 options.file_pattern =
279 mc_config_get_bool (mc_main_config, "FindFile", "file_shell_pattern", TRUE);
280 options.find_recurs = mc_config_get_bool (mc_main_config, "FindFile", "file_find_recurs", TRUE);
281 options.skip_hidden =
282 mc_config_get_bool (mc_main_config, "FindFile", "file_skip_hidden", FALSE);
283 options.file_all_charsets =
284 mc_config_get_bool (mc_main_config, "FindFile", "file_all_charsets", FALSE);
285 options.content_use = mc_config_get_bool (mc_main_config, "FindFile", "content_use", TRUE);
286 options.content_case_sens =
287 mc_config_get_bool (mc_main_config, "FindFile", "content_case_sens", TRUE);
288 options.content_regexp =
289 mc_config_get_bool (mc_main_config, "FindFile", "content_regexp", FALSE);
290 options.content_first_hit =
291 mc_config_get_bool (mc_main_config, "FindFile", "content_first_hit", FALSE);
292 options.content_whole_words =
293 mc_config_get_bool (mc_main_config, "FindFile", "content_whole_words", FALSE);
294 options.content_all_charsets =
295 mc_config_get_bool (mc_main_config, "FindFile", "content_all_charsets", FALSE);
296 options.ignore_dirs_enable =
297 mc_config_get_bool (mc_main_config, "FindFile", "ignore_dirs_enable", TRUE);
298 options.ignore_dirs = mc_config_get_string (mc_main_config, "FindFile", "ignore_dirs", "");
300 if (options.ignore_dirs[0] == '\0')
302 g_free (options.ignore_dirs);
303 options.ignore_dirs = NULL;
307 /* --------------------------------------------------------------------------------------------- */
309 static void
310 find_save_options (void)
312 mc_config_set_bool (mc_main_config, "FindFile", "file_case_sens", options.file_case_sens);
313 mc_config_set_bool (mc_main_config, "FindFile", "file_shell_pattern", options.file_pattern);
314 mc_config_set_bool (mc_main_config, "FindFile", "file_find_recurs", options.find_recurs);
315 mc_config_set_bool (mc_main_config, "FindFile", "file_skip_hidden", options.skip_hidden);
316 mc_config_set_bool (mc_main_config, "FindFile", "file_all_charsets", options.file_all_charsets);
317 mc_config_set_bool (mc_main_config, "FindFile", "content_use", options.content_use);
318 mc_config_set_bool (mc_main_config, "FindFile", "content_case_sens", options.content_case_sens);
319 mc_config_set_bool (mc_main_config, "FindFile", "content_regexp", options.content_regexp);
320 mc_config_set_bool (mc_main_config, "FindFile", "content_first_hit", options.content_first_hit);
321 mc_config_set_bool (mc_main_config, "FindFile", "content_whole_words",
322 options.content_whole_words);
323 mc_config_set_bool (mc_main_config, "FindFile", "content_all_charsets",
324 options.content_all_charsets);
325 mc_config_set_bool (mc_main_config, "FindFile", "ignore_dirs_enable",
326 options.ignore_dirs_enable);
327 mc_config_set_string (mc_main_config, "FindFile", "ignore_dirs", options.ignore_dirs);
330 /* --------------------------------------------------------------------------------------------- */
332 static inline char *
333 add_to_list (const char *text, void *data)
335 return listbox_add_item (find_list, LISTBOX_APPEND_AT_END, 0, text, data);
338 /* --------------------------------------------------------------------------------------------- */
340 static inline void
341 stop_idle (void *data)
343 set_idle_proc (data, 0);
346 /* --------------------------------------------------------------------------------------------- */
348 static inline void
349 status_update (const char *text)
351 label_set_text (status_label, text);
354 /* --------------------------------------------------------------------------------------------- */
356 static void
357 found_num_update (void)
359 char buffer[BUF_TINY];
360 g_snprintf (buffer, sizeof (buffer), _("Found: %ld"), matches);
361 label_set_text (found_num_label, buffer);
364 /* --------------------------------------------------------------------------------------------- */
366 static void
367 get_list_info (char **file, char **dir)
369 listbox_get_current (find_list, file, (void **) dir);
372 /* --------------------------------------------------------------------------------------------- */
373 /** check regular expression */
375 static gboolean
376 find_check_regexp (const char *r)
378 mc_search_t *search;
379 gboolean regexp_ok = FALSE;
381 search = mc_search_new (r, -1);
383 if (search != NULL)
385 search->search_type = MC_SEARCH_T_REGEX;
386 regexp_ok = mc_search_prepare (search);
387 mc_search_free (search);
390 return regexp_ok;
393 /* --------------------------------------------------------------------------------------------- */
395 * Callback for the parameter dialog.
396 * Validate regex, prevent closing the dialog if it's invalid.
399 static cb_ret_t
400 find_parm_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
402 switch (msg)
404 case DLG_ACTION:
405 if (sender == (Widget *) content_use_cbox)
407 gboolean disable = !(content_use_cbox->state & C_BOOL);
409 widget_disable (content_label->widget, disable);
410 send_message ((Widget *) content_label, WIDGET_DRAW, 0);
411 widget_disable (in_with->widget, disable);
412 send_message ((Widget *) in_with, WIDGET_DRAW, 0);
413 widget_disable (content_first_hit_cbox->widget, disable);
414 send_message ((Widget *) content_first_hit_cbox, WIDGET_DRAW, 0);
415 widget_disable (content_regexp_cbox->widget, disable);
416 send_message ((Widget *) content_regexp_cbox, WIDGET_DRAW, 0);
417 widget_disable (content_case_sens_cbox->widget, disable);
418 send_message ((Widget *) content_case_sens_cbox, WIDGET_DRAW, 0);
419 #ifdef HAVE_CHARSET
420 widget_disable (content_all_charsets_cbox->widget, disable);
421 send_message ((Widget *) content_all_charsets_cbox, WIDGET_DRAW, 0);
422 #endif
423 widget_disable (content_whole_words_cbox->widget, disable);
424 send_message ((Widget *) content_whole_words_cbox, WIDGET_DRAW, 0);
426 return MSG_HANDLED;
429 if (sender == (Widget *) ignore_dirs_cbox)
431 gboolean disable = !(ignore_dirs_cbox->state & C_BOOL);
433 widget_disable (in_ignore->widget, disable);
434 send_message ((Widget *) in_ignore, WIDGET_DRAW, 0);
436 return MSG_HANDLED;
439 return MSG_NOT_HANDLED;
442 case DLG_VALIDATE:
443 if (h->ret_value != B_ENTER)
444 return MSG_HANDLED;
446 /* check filename regexp */
447 if (!(file_pattern_cbox->state & C_BOOL)
448 && (in_name->buffer[0] != '\0') && !find_check_regexp (in_name->buffer))
450 h->state = DLG_ACTIVE; /* Don't stop the dialog */
451 message (D_ERROR, MSG_ERROR, _("Malformed regular expression"));
452 dlg_select_widget (in_name);
453 return MSG_HANDLED;
456 /* check content regexp */
457 if ((content_use_cbox->state & C_BOOL) && (content_regexp_cbox->state & C_BOOL)
458 && (in_with->buffer[0] != '\0') && !find_check_regexp (in_with->buffer))
460 h->state = DLG_ACTIVE; /* Don't stop the dialog */
461 message (D_ERROR, MSG_ERROR, _("Malformed regular expression"));
462 dlg_select_widget (in_with);
463 return MSG_HANDLED;
466 return MSG_HANDLED;
468 default:
469 return default_dlg_callback (h, sender, msg, parm, data);
473 /* --------------------------------------------------------------------------------------------- */
475 * find_parameters: gets information from the user
477 * If the return value is TRUE, then the following holds:
479 * start_dir, ignore_dirs, pattern and content contain the information provided by the user.
480 * They are newly allocated strings and must be freed when uneeded.
482 * start_dir_len is -1 when user entered an absolute path, otherwise it is a length
483 * of start_dir (which is absolute). It is used to get a relative pats of find results.
486 static gboolean
487 find_parameters (char **start_dir, ssize_t * start_dir_len,
488 char **ignore_dirs, char **pattern, char **content)
490 gboolean return_value;
492 /* file name */
493 const char *file_case_label = N_("Cas&e sensitive");
494 const char *file_pattern_label = N_("&Using shell patterns");
495 const char *file_recurs_label = N_("&Find recursively");
496 const char *file_skip_hidden_label = N_("S&kip hidden");
497 #ifdef HAVE_CHARSET
498 const char *file_all_charsets_label = N_("&All charsets");
499 #endif
501 /* file content */
502 const char *content_use_label = N_("Sea&rch for content");
503 const char *content_case_label = N_("Case sens&itive");
504 const char *content_regexp_label = N_("Re&gular expression");
505 const char *content_first_hit_label = N_("Fir&st hit");
506 const char *content_whole_words_label = N_("&Whole words");
507 #ifdef HAVE_CHARSET
508 const char *content_all_charsets_label = N_("A&ll charsets");
509 #endif
511 const char *buts[] = { N_("&OK"), N_("&Cancel"), N_("&Tree") };
513 int b0, b1, b2;
515 int cbox_position;
516 gboolean disable;
518 #ifdef ENABLE_NLS
520 int i = sizeof (buts) / sizeof (buts[0]);
521 while (i-- != 0)
522 buts[i] = _(buts[i]);
524 file_case_label = _(file_case_label);
525 file_pattern_label = _(file_pattern_label);
526 file_recurs_label = _(file_recurs_label);
527 file_skip_hidden_label = _(file_skip_hidden_label);
528 #ifdef HAVE_CHARSET
529 file_all_charsets_label = _(file_all_charsets_label);
530 content_all_charsets_label = _(content_all_charsets_label);
531 #endif
532 content_use_label = _(content_use_label);
533 content_case_label = _(content_case_label);
534 content_regexp_label = _(content_regexp_label);
535 content_first_hit_label = _(content_first_hit_label);
536 content_whole_words_label = _(content_whole_words_label);
538 #endif /* ENABLE_NLS */
540 b0 = str_term_width1 (buts[0]) + 6; /* default button */
541 b1 = str_term_width1 (buts[1]) + 4;
542 b2 = str_term_width1 (buts[2]) + 4;
544 find_load_options ();
546 if (in_start_dir == NULL)
547 in_start_dir = g_strdup (".");
549 disable = !options.content_use;
551 find_dlg =
552 create_dlg (TRUE, 0, 0, FIND_Y, FIND_X, dialog_colors,
553 find_parm_callback, NULL, "[Find File]", _("Find File"),
554 DLG_CENTER | DLG_REVERSE);
556 add_widget (find_dlg,
557 button_new (FIND_Y - 3, FIND_X * 3 / 4 - b1 / 2, B_CANCEL, NORMAL_BUTTON, buts[1],
558 0));
559 add_widget (find_dlg,
560 button_new (FIND_Y - 3, FIND_X / 4 - b0 / 2, B_ENTER, DEFPUSH_BUTTON, buts[0], 0));
562 cbox_position = FIND_Y - 5;
564 content_first_hit_cbox =
565 check_new (cbox_position--, FIND_X / 2 + 1, options.content_first_hit,
566 content_first_hit_label);
567 widget_disable (content_first_hit_cbox->widget, disable);
568 add_widget (find_dlg, content_first_hit_cbox);
570 content_whole_words_cbox =
571 check_new (cbox_position--, FIND_X / 2 + 1, options.content_whole_words,
572 content_whole_words_label);
573 widget_disable (content_whole_words_cbox->widget, disable);
574 add_widget (find_dlg, content_whole_words_cbox);
576 #ifdef HAVE_CHARSET
577 content_all_charsets_cbox = check_new (cbox_position--, FIND_X / 2 + 1,
578 options.content_all_charsets,
579 content_all_charsets_label);
580 widget_disable (content_all_charsets_cbox->widget, disable);
581 add_widget (find_dlg, content_all_charsets_cbox);
582 #endif
584 content_case_sens_cbox =
585 check_new (cbox_position--, FIND_X / 2 + 1, options.content_case_sens, content_case_label);
586 widget_disable (content_case_sens_cbox->widget, disable);
587 add_widget (find_dlg, content_case_sens_cbox);
589 content_regexp_cbox =
590 check_new (cbox_position--, FIND_X / 2 + 1, options.content_regexp, content_regexp_label);
591 widget_disable (content_regexp_cbox->widget, disable);
592 add_widget (find_dlg, content_regexp_cbox);
594 cbox_position = FIND_Y - 6;
596 skip_hidden_cbox = check_new (cbox_position--, 3, options.skip_hidden, file_skip_hidden_label);
597 add_widget (find_dlg, skip_hidden_cbox);
599 #ifdef HAVE_CHARSET
600 file_all_charsets_cbox =
601 check_new (cbox_position--, 3, options.file_all_charsets, file_all_charsets_label);
602 add_widget (find_dlg, file_all_charsets_cbox);
603 #endif
605 file_case_sens_cbox = check_new (cbox_position--, 3, options.file_case_sens, file_case_label);
606 add_widget (find_dlg, file_case_sens_cbox);
608 file_pattern_cbox = check_new (cbox_position--, 3, options.file_pattern, file_pattern_label);
609 add_widget (find_dlg, file_pattern_cbox);
611 recursively_cbox = check_new (cbox_position, 3, options.find_recurs, file_recurs_label);
612 add_widget (find_dlg, recursively_cbox);
614 /* This checkbox is located in the second column */
615 content_use_cbox =
616 check_new (cbox_position, FIND_X / 2 + 1, options.content_use, content_use_label);
617 add_widget (find_dlg, content_use_cbox);
619 in_with =
620 input_new (8, FIND_X / 2 + 1, input_get_default_colors (), FIND_X / 2 - 4, INPUT_LAST_TEXT,
621 MC_HISTORY_SHARED_SEARCH, INPUT_COMPLETE_DEFAULT);
622 widget_disable (in_with->widget, disable);
623 add_widget (find_dlg, in_with);
625 content_label = label_new (7, FIND_X / 2 + 1, _("Content:"));
626 widget_disable (content_label->widget, disable);
627 add_widget (find_dlg, content_label);
629 in_name = input_new (8, 3, input_get_default_colors (),
630 FIND_X / 2 - 4, INPUT_LAST_TEXT, "name", INPUT_COMPLETE_DEFAULT);
631 add_widget (find_dlg, in_name);
632 add_widget (find_dlg, label_new (7, 3, _("File name:")));
634 in_ignore = input_new (5, 3, input_get_default_colors (), FIND_X - 6,
635 options.ignore_dirs != NULL ? options.ignore_dirs : "",
636 "ignoredirs", INPUT_COMPLETE_DEFAULT);
637 widget_disable (in_ignore->widget, !options.ignore_dirs_enable);
638 add_widget (find_dlg, in_ignore);
640 ignore_dirs_cbox =
641 check_new (4, 3, options.ignore_dirs_enable, _("Ena&ble ignore directories:"));
642 add_widget (find_dlg, ignore_dirs_cbox);
644 add_widget (find_dlg, button_new (3, FIND_X - b2 - 2, B_TREE, NORMAL_BUTTON, buts[2], 0));
646 in_start = input_new (3, 3, input_get_default_colors (),
647 FIND_X - b2 - 6, in_start_dir, "start", INPUT_COMPLETE_DEFAULT);
648 add_widget (find_dlg, in_start);
649 add_widget (find_dlg, label_new (2, 3, _("Start at:")));
651 find_par_start:
652 dlg_select_widget (in_name);
654 switch (run_dlg (find_dlg))
656 case B_CANCEL:
657 return_value = FALSE;
658 break;
660 case B_TREE:
662 char *temp_dir;
664 temp_dir = in_start->buffer;
665 if ((temp_dir[0] == '\0') || ((temp_dir[0] == '.') && (temp_dir[1] == '\0')))
666 temp_dir = vfs_path_to_str (current_panel->cwd_vpath);
667 else
668 temp_dir = g_strdup (temp_dir);
670 if (in_start_dir != INPUT_LAST_TEXT)
671 g_free (in_start_dir);
672 in_start_dir = tree_box (temp_dir);
673 if (in_start_dir == NULL)
674 in_start_dir = temp_dir;
675 else
676 g_free (temp_dir);
678 input_assign_text (in_start, in_start_dir);
680 /* Warning: Dreadful goto */
681 goto find_par_start;
684 default:
686 char *s;
688 #ifdef HAVE_CHARSET
689 options.file_all_charsets = file_all_charsets_cbox->state & C_BOOL;
690 options.content_all_charsets = content_all_charsets_cbox->state & C_BOOL;
691 #endif
692 options.content_use = content_use_cbox->state & C_BOOL;
693 options.content_case_sens = content_case_sens_cbox->state & C_BOOL;
694 options.content_regexp = content_regexp_cbox->state & C_BOOL;
695 options.content_first_hit = content_first_hit_cbox->state & C_BOOL;
696 options.content_whole_words = content_whole_words_cbox->state & C_BOOL;
697 options.find_recurs = recursively_cbox->state & C_BOOL;
698 options.file_pattern = file_pattern_cbox->state & C_BOOL;
699 options.file_case_sens = file_case_sens_cbox->state & C_BOOL;
700 options.skip_hidden = skip_hidden_cbox->state & C_BOOL;
701 options.ignore_dirs_enable = ignore_dirs_cbox->state & C_BOOL;
702 g_free (options.ignore_dirs);
703 options.ignore_dirs = g_strdup (in_ignore->buffer);
705 *content = (options.content_use && in_with->buffer[0] != '\0')
706 ? g_strdup (in_with->buffer) : NULL;
707 *start_dir = in_start->buffer[0] != '\0' ? in_start->buffer : (char *) ".";
708 *pattern = g_strdup (in_name->buffer);
709 if (in_start_dir != INPUT_LAST_TEXT)
710 g_free (in_start_dir);
711 in_start_dir = g_strdup (*start_dir);
713 s = tilde_expand (*start_dir);
714 canonicalize_pathname (s);
716 if (s[0] == '.' && s[1] == '\0')
718 *start_dir = vfs_path_to_str (current_panel->cwd_vpath);
719 /* FIXME: is current_panel->cwd_vpath canonicalized? */
720 /* relative paths will be used in panelization */
721 *start_dir_len = (ssize_t) strlen (*start_dir);
722 g_free (s);
724 else if (g_path_is_absolute (s))
726 *start_dir = s;
727 *start_dir_len = -1;
729 else
731 /* relative paths will be used in panelization */
732 char *cwd_str;
734 cwd_str = vfs_path_to_str (current_panel->cwd_vpath);
735 *start_dir = mc_build_filename (cwd_str, s, (char *) NULL);
736 *start_dir_len = (ssize_t) strlen (cwd_str);
737 g_free (cwd_str);
738 g_free (s);
741 if (!options.ignore_dirs_enable || in_ignore->buffer[0] == '\0'
742 || (in_ignore->buffer[0] == '.' && in_ignore->buffer[1] == '\0'))
743 *ignore_dirs = NULL;
744 else
745 *ignore_dirs = g_strdup (in_ignore->buffer);
747 find_save_options ();
749 return_value = TRUE;
753 destroy_dlg (find_dlg);
755 return return_value;
758 /* --------------------------------------------------------------------------------------------- */
760 #if GLIB_CHECK_VERSION (2, 14, 0)
761 static inline void
762 push_directory (const vfs_path_t * dir)
764 g_queue_push_head (&dir_queue, (void *) dir);
767 /* --------------------------------------------------------------------------------------------- */
769 static inline vfs_path_t *
770 pop_directory (void)
772 return (vfs_path_t *) g_queue_pop_tail (&dir_queue);
775 /* --------------------------------------------------------------------------------------------- */
776 /** Remove all the items from the stack */
778 static void
779 clear_stack (void)
781 g_queue_foreach (&dir_queue, (GFunc) vfs_path_free, NULL);
782 g_queue_clear (&dir_queue);
785 /* --------------------------------------------------------------------------------------------- */
787 #else /* GLIB_CHECK_VERSION */
788 static void
789 push_directory (const vfs_path_t * dir)
791 dir_stack *new;
793 new = g_new (dir_stack, 1);
794 new->name = (vfs_path_t *) dir;
795 new->prev = dir_stack_base;
796 dir_stack_base = new;
799 /* --------------------------------------------------------------------------------------------- */
801 static vfs_path_t *
802 pop_directory (void)
804 vfs_path_t *name = NULL;
806 if (dir_stack_base != NULL)
808 dir_stack *next;
809 name = dir_stack_base->name;
810 next = dir_stack_base->prev;
811 g_free (dir_stack_base);
812 dir_stack_base = next;
815 return name;
818 /* --------------------------------------------------------------------------------------------- */
819 /** Remove all the items from the stack */
821 static void
822 clear_stack (void)
824 vfs_path_t *dir = NULL;
826 while ((dir = pop_directory ()) != NULL)
827 vfs_path_free (dir);
829 #endif /* GLIB_CHECK_VERSION */
831 /* --------------------------------------------------------------------------------------------- */
833 static void
834 insert_file (const char *dir, const char *file)
836 char *tmp_name = NULL;
837 static char *dirname = NULL;
839 while (dir[0] == PATH_SEP && dir[1] == PATH_SEP)
840 dir++;
842 if (old_dir)
844 if (strcmp (old_dir, dir))
846 g_free (old_dir);
847 old_dir = g_strdup (dir);
848 dirname = add_to_list (dir, NULL);
851 else
853 old_dir = g_strdup (dir);
854 dirname = add_to_list (dir, NULL);
857 tmp_name = g_strdup_printf (" %s", file);
858 add_to_list (tmp_name, dirname);
859 g_free (tmp_name);
862 /* --------------------------------------------------------------------------------------------- */
864 static void
865 find_add_match (const char *dir, const char *file)
867 insert_file (dir, file);
869 /* Don't scroll */
870 if (matches == 0)
871 listbox_select_first (find_list);
872 send_message (&find_list->widget, WIDGET_DRAW, 0);
874 matches++;
875 found_num_update ();
878 /* --------------------------------------------------------------------------------------------- */
880 * get_line_at:
882 * Returns malloced null-terminated line from file file_fd.
883 * Input is buffered in buf_size long buffer.
884 * Current pos in buf is stored in pos.
885 * n_read - number of read chars.
886 * has_newline - is there newline ?
889 static char *
890 get_line_at (int file_fd, char *buf, int buf_size, int *pos, int *n_read, gboolean * has_newline)
892 char *buffer = NULL;
893 int buffer_size = 0;
894 char ch = 0;
895 int i = 0;
897 for (;;)
899 if (*pos >= *n_read)
901 *pos = 0;
902 *n_read = mc_read (file_fd, buf, buf_size);
903 if (*n_read <= 0)
904 break;
907 ch = buf[(*pos)++];
908 if (ch == '\0')
910 /* skip possible leading zero(s) */
911 if (i == 0)
912 continue;
913 break;
916 if (i >= buffer_size - 1)
917 buffer = g_realloc (buffer, buffer_size += 80);
919 /* Strip newline */
920 if (ch == '\n')
921 break;
923 buffer[i++] = ch;
926 *has_newline = (ch != '\0');
928 if (buffer != NULL)
929 buffer[i] = '\0';
931 return buffer;
934 /* --------------------------------------------------------------------------------------------- */
936 static FindProgressStatus
937 check_find_events (Dlg_head * h)
939 Gpm_Event event;
940 int c;
942 event.x = -1;
943 c = tty_get_event (&event, h->mouse_status == MOU_REPEAT, FALSE);
944 if (c != EV_NONE)
946 dlg_process_event (h, c, &event);
947 if (h->ret_value == B_ENTER
948 || h->ret_value == B_CANCEL || h->ret_value == B_AGAIN || h->ret_value == B_PANELIZE)
950 /* dialog terminated */
951 return FIND_ABORT;
953 if (!(h->flags & DLG_WANT_IDLE))
955 /* searching suspended */
956 return FIND_SUSPEND;
960 return FIND_CONT;
963 /* --------------------------------------------------------------------------------------------- */
965 * search_content:
967 * Search the content_pattern string in the DIRECTORY/FILE.
968 * It will add the found entries to the find listbox.
970 * returns FALSE if do_search should look for another file
971 * TRUE if do_search should exit and proceed to the event handler
974 static gboolean
975 search_content (Dlg_head * h, const char *directory, const char *filename)
977 struct stat s;
978 char buffer[BUF_4K];
979 int file_fd;
980 gboolean ret_val = FALSE;
981 vfs_path_t *vpath;
983 vpath = vfs_path_build_filename (directory, filename, (char *) NULL);
985 if (mc_stat (vpath, &s) != 0 || !S_ISREG (s.st_mode))
987 vfs_path_free (vpath);
988 return FALSE;
991 file_fd = mc_open (vpath, O_RDONLY);
992 vfs_path_free (vpath);
994 if (file_fd == -1)
995 return FALSE;
997 g_snprintf (buffer, sizeof (buffer), _("Grepping in %s"), str_trunc (filename, FIND2_X_USE));
999 status_update (buffer);
1000 mc_refresh ();
1002 tty_enable_interrupt_key ();
1003 tty_got_interrupt ();
1006 int line = 1;
1007 int pos = 0;
1008 int n_read = 0;
1009 gboolean has_newline;
1010 char *p = NULL;
1011 gboolean found = FALSE;
1012 gsize found_len;
1013 char result[BUF_MEDIUM];
1015 if (resuming)
1017 /* We've been previously suspended, start from the previous position */
1018 resuming = 0;
1019 line = last_line;
1020 pos = last_pos;
1022 while (!ret_val
1023 && (p = get_line_at (file_fd, buffer, sizeof (buffer),
1024 &pos, &n_read, &has_newline)) != NULL)
1026 if (!found /* Search in binary line once */
1027 && mc_search_run (search_content_handle,
1028 (const void *) p, 0, strlen (p), &found_len))
1030 g_snprintf (result, sizeof (result), "%d:%s", line, filename);
1031 find_add_match (directory, result);
1032 found = TRUE;
1034 g_free (p);
1036 if (found && options.content_first_hit)
1037 break;
1039 if (has_newline)
1041 found = FALSE;
1042 line++;
1045 if ((line & 0xff) == 0)
1047 FindProgressStatus res;
1048 res = check_find_events (h);
1049 switch (res)
1051 case FIND_ABORT:
1052 stop_idle (h);
1053 ret_val = TRUE;
1054 break;
1055 case FIND_SUSPEND:
1056 resuming = 1;
1057 last_line = line;
1058 last_pos = pos;
1059 ret_val = TRUE;
1060 break;
1061 default:
1062 break;
1067 tty_disable_interrupt_key ();
1068 mc_close (file_fd);
1069 return ret_val;
1072 /* --------------------------------------------------------------------------------------------- */
1075 If dir is absolute, this means we're within dir and searching file here.
1076 If dir is relative, this means we're going to add dir to the directory stack.
1078 static gboolean
1079 find_ignore_dir_search (const char *dir)
1081 if (find_ignore_dirs != NULL)
1083 const size_t dlen = strlen (dir);
1084 const unsigned char dabs = g_path_is_absolute (dir) ? 1 : 0;
1086 char **ignore_dir;
1088 for (ignore_dir = find_ignore_dirs; *ignore_dir != NULL; ignore_dir++)
1090 const size_t ilen = strlen (*ignore_dir);
1091 const unsigned char iabs = g_path_is_absolute (*ignore_dir) ? 2 : 0;
1093 /* ignore dir is too long -- skip it */
1094 if (dlen < ilen)
1095 continue;
1097 /* handle absolute and relative paths */
1098 switch (iabs | dabs)
1100 case 0: /* both paths are relative */
1101 case 3: /* both paths are abolute */
1102 /* if ignore dir is not a path of dir -- skip it */
1103 if (strncmp (dir, *ignore_dir, ilen) == 0)
1105 /* be sure that ignore dir is not a part of dir like:
1106 ignore dir is "h", dir is "home" */
1107 if (dir[ilen] == '\0' || dir[ilen] == PATH_SEP)
1108 return TRUE;
1110 break;
1111 case 1: /* dir is absolute, ignore_dir is relative */
1113 char *d;
1115 d = strstr (dir, *ignore_dir);
1116 if (d != NULL && d[-1] == PATH_SEP && (d[ilen] == '\0' || d[ilen] == PATH_SEP))
1117 return TRUE;
1119 break;
1120 case 2: /* dir is relative, ignore_dir is absolute */
1121 /* FIXME: skip this case */
1122 break;
1123 default: /* this cannot occurs */
1124 return FALSE;
1129 return FALSE;
1132 /* --------------------------------------------------------------------------------------------- */
1134 static void
1135 find_rotate_dash (const Dlg_head * h, gboolean finish)
1137 static const char rotating_dash[] = "|/-\\";
1138 static unsigned int pos = 0;
1140 if (verbose)
1142 pos = (pos + 1) % 4;
1143 tty_setcolor (h->color[DLG_COLOR_NORMAL]);
1144 dlg_move (h, FIND2_Y - 7, FIND2_X - 4);
1145 tty_print_char (finish ? ' ' : rotating_dash[pos]);
1146 mc_refresh ();
1150 /* --------------------------------------------------------------------------------------------- */
1152 static int
1153 do_search (Dlg_head * h)
1155 static struct dirent *dp = NULL;
1156 static DIR *dirp = NULL;
1157 static char *directory = NULL;
1158 struct stat tmp_stat;
1159 static int subdirs_left = 0;
1160 gsize bytes_found;
1161 unsigned short count;
1163 if (h == NULL)
1164 { /* someone forces me to close dirp */
1165 if (dirp != NULL)
1167 mc_closedir (dirp);
1168 dirp = NULL;
1170 g_free (directory);
1171 directory = NULL;
1172 dp = NULL;
1173 return 1;
1176 for (count = 0; count < 32; count++)
1178 while (dp == NULL)
1180 if (dirp != NULL)
1182 mc_closedir (dirp);
1183 dirp = NULL;
1186 while (dirp == NULL)
1188 vfs_path_t *tmp_vpath = NULL;
1190 tty_setcolor (REVERSE_COLOR);
1192 while (TRUE)
1194 tmp_vpath = pop_directory ();
1195 if (tmp_vpath == NULL)
1197 running = FALSE;
1198 if (ignore_count == 0)
1199 status_update (_("Finished"));
1200 else
1202 char msg[BUF_SMALL];
1203 g_snprintf (msg, sizeof (msg),
1204 ngettext ("Finished (ignored %zd directory)",
1205 "Finished (ignored %zd directories)",
1206 ignore_count), ignore_count);
1207 status_update (msg);
1209 find_rotate_dash (h, TRUE);
1210 stop_idle (h);
1211 return 0;
1214 /* handle absolute ignore dirs here */
1216 char *tmp;
1217 gboolean ok;
1219 tmp = vfs_path_to_str (tmp_vpath);
1220 ok = find_ignore_dir_search (tmp);
1221 g_free (tmp);
1222 if (!ok)
1223 break;
1226 vfs_path_free (tmp_vpath);
1227 ignore_count++;
1230 g_free (directory);
1231 directory = vfs_path_to_str (tmp_vpath);
1233 if (verbose)
1235 char buffer[BUF_SMALL];
1237 g_snprintf (buffer, sizeof (buffer), _("Searching %s"),
1238 str_trunc (directory, FIND2_X_USE));
1239 status_update (buffer);
1241 /* mc_stat should not be called after mc_opendir
1242 because vfs_s_opendir modifies the st_nlink
1244 if (mc_stat (tmp_vpath, &tmp_stat) == 0)
1245 subdirs_left = tmp_stat.st_nlink - 2;
1246 else
1247 subdirs_left = 0;
1249 dirp = mc_opendir (tmp_vpath);
1250 vfs_path_free (tmp_vpath);
1251 } /* while (!dirp) */
1253 /* skip invalid filenames */
1254 while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1256 } /* while (!dp) */
1258 if (strcmp (dp->d_name, ".") == 0 || strcmp (dp->d_name, "..") == 0)
1260 /* skip invalid filenames */
1261 while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1264 return 1;
1267 if (!(options.skip_hidden && (dp->d_name[0] == '.')))
1269 gboolean search_ok;
1271 if ((subdirs_left != 0) && options.find_recurs && (directory != NULL))
1272 { /* Can directory be NULL ? */
1273 /* handle relative ignore dirs here */
1274 if (options.ignore_dirs_enable && find_ignore_dir_search (dp->d_name))
1275 ignore_count++;
1276 else
1278 vfs_path_t *tmp_vpath;
1280 tmp_vpath = vfs_path_build_filename (directory, dp->d_name, (char *) NULL);
1282 if (mc_lstat (tmp_vpath, &tmp_stat) == 0 && S_ISDIR (tmp_stat.st_mode))
1284 push_directory (tmp_vpath);
1285 subdirs_left--;
1287 else
1288 vfs_path_free (tmp_vpath);
1292 search_ok = mc_search_run (search_file_handle, dp->d_name,
1293 0, strlen (dp->d_name), &bytes_found);
1295 if (search_ok)
1297 if (content_pattern == NULL)
1298 find_add_match (directory, dp->d_name);
1299 else if (search_content (h, directory, dp->d_name))
1300 return 1;
1304 /* skip invalid filenames */
1305 while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1307 } /* for */
1309 find_rotate_dash (h, FALSE);
1311 return 1;
1314 /* --------------------------------------------------------------------------------------------- */
1316 static void
1317 init_find_vars (void)
1319 g_free (old_dir);
1320 old_dir = NULL;
1321 matches = 0;
1322 ignore_count = 0;
1324 /* Remove all the items from the stack */
1325 clear_stack ();
1327 g_strfreev (find_ignore_dirs);
1328 find_ignore_dirs = NULL;
1331 /* --------------------------------------------------------------------------------------------- */
1333 static void
1334 find_do_view_edit (int unparsed_view, int edit, char *dir, char *file)
1336 char *fullname = NULL;
1337 const char *filename = NULL;
1338 int line;
1339 vfs_path_t *fullname_vpath;
1341 if (content_pattern != NULL)
1343 filename = strchr (file + 4, ':') + 1;
1344 line = atoi (file + 4);
1346 else
1348 filename = file + 4;
1349 line = 0;
1352 fullname_vpath = vfs_path_build_filename (dir, filename, (char *) NULL);
1353 if (edit)
1354 do_edit_at_line (fullname_vpath, use_internal_edit, line);
1355 else
1356 view_file_at_line (fullname_vpath, unparsed_view, use_internal_view, line);
1357 vfs_path_free (fullname_vpath);
1358 g_free (fullname);
1361 /* --------------------------------------------------------------------------------------------- */
1363 static cb_ret_t
1364 view_edit_currently_selected_file (int unparsed_view, int edit)
1366 char *dir = NULL;
1367 char *text = NULL;
1369 listbox_get_current (find_list, &text, (void **) &dir);
1371 if ((text == NULL) || (dir == NULL))
1372 return MSG_NOT_HANDLED;
1374 find_do_view_edit (unparsed_view, edit, dir, text);
1375 return MSG_HANDLED;
1378 /* --------------------------------------------------------------------------------------------- */
1380 static cb_ret_t
1381 find_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
1383 switch (msg)
1385 case DLG_KEY:
1386 if (parm == KEY_F (3) || parm == KEY_F (13))
1388 int unparsed_view = (parm == KEY_F (13));
1389 return view_edit_currently_selected_file (unparsed_view, 0);
1391 if (parm == KEY_F (4))
1393 return view_edit_currently_selected_file (0, 1);
1395 return MSG_NOT_HANDLED;
1397 case DLG_IDLE:
1398 do_search (h);
1399 return MSG_HANDLED;
1401 default:
1402 return default_dlg_callback (h, sender, msg, parm, data);
1406 /* --------------------------------------------------------------------------------------------- */
1407 /** Handles the Stop/Start button in the find window */
1409 static int
1410 start_stop (WButton * button, int action)
1412 (void) button;
1413 (void) action;
1415 running = is_start;
1416 set_idle_proc (find_dlg, running);
1417 is_start = !is_start;
1419 status_update (is_start ? _("Stopped") : _("Searching"));
1420 button_set_text (stop_button, fbuts[is_start ? 1 : 0].text);
1422 return 0;
1425 /* --------------------------------------------------------------------------------------------- */
1426 /** Handle view command, when invoked as a button */
1428 static int
1429 find_do_view_file (WButton * button, int action)
1431 (void) button;
1432 (void) action;
1434 view_edit_currently_selected_file (0, 0);
1435 return 0;
1438 /* --------------------------------------------------------------------------------------------- */
1439 /** Handle edit command, when invoked as a button */
1441 static int
1442 find_do_edit_file (WButton * button, int action)
1444 (void) button;
1445 (void) action;
1447 view_edit_currently_selected_file (0, 1);
1448 return 0;
1451 /* --------------------------------------------------------------------------------------------- */
1453 static void
1454 setup_gui (void)
1456 #ifdef ENABLE_NLS
1457 static gboolean i18n_flag = FALSE;
1459 if (!i18n_flag)
1461 int i = sizeof (fbuts) / sizeof (fbuts[0]);
1462 while (i-- != 0)
1464 fbuts[i].text = _(fbuts[i].text);
1465 fbuts[i].len = str_term_width1 (fbuts[i].text) + 3;
1468 fbuts[2].len += 2; /* DEFPUSH_BUTTON */
1469 i18n_flag = TRUE;
1471 #endif /* ENABLE_NLS */
1474 * Dynamically place buttons centered within current window size
1477 int l0 = max (fbuts[0].len, fbuts[1].len);
1478 int l1 = fbuts[2].len + fbuts[3].len + l0 + fbuts[4].len;
1479 int l2 = fbuts[5].len + fbuts[6].len + fbuts[7].len;
1480 int r1, r2;
1482 /* Check, if both button rows fit within FIND2_X */
1483 FIND2_X = max (l1 + 9, COLS - 16);
1484 FIND2_X = max (l2 + 8, FIND2_X);
1486 /* compute amount of space between buttons for each row */
1487 r1 = (FIND2_X - 4 - l1) % 5;
1488 l1 = (FIND2_X - 4 - l1) / 5;
1489 r2 = (FIND2_X - 4 - l2) % 4;
1490 l2 = (FIND2_X - 4 - l2) / 4;
1492 /* ...and finally, place buttons */
1493 fbuts[2].x = 2 + r1 / 2 + l1;
1494 fbuts[3].x = fbuts[2].x + fbuts[2].len + l1;
1495 fbuts[0].x = fbuts[3].x + fbuts[3].len + l1;
1496 fbuts[4].x = fbuts[0].x + l0 + l1;
1497 fbuts[5].x = 2 + r2 / 2 + l2;
1498 fbuts[6].x = fbuts[5].x + fbuts[5].len + l2;
1499 fbuts[7].x = fbuts[6].x + fbuts[6].len + l2;
1502 find_dlg =
1503 create_dlg (TRUE, 0, 0, FIND2_Y, FIND2_X, dialog_colors, find_callback, NULL,
1504 "[Find File]", _("Find File"), DLG_CENTER | DLG_REVERSE);
1506 add_widget (find_dlg,
1507 button_new (FIND2_Y - 3, fbuts[7].x, B_VIEW, NORMAL_BUTTON,
1508 fbuts[7].text, find_do_edit_file));
1509 add_widget (find_dlg,
1510 button_new (FIND2_Y - 3, fbuts[6].x, B_VIEW, NORMAL_BUTTON,
1511 fbuts[6].text, find_do_view_file));
1512 add_widget (find_dlg,
1513 button_new (FIND2_Y - 3, fbuts[5].x, B_PANELIZE, NORMAL_BUTTON, fbuts[5].text,
1514 NULL));
1516 add_widget (find_dlg,
1517 button_new (FIND2_Y - 4, fbuts[4].x, B_CANCEL, NORMAL_BUTTON, fbuts[4].text, NULL));
1518 stop_button =
1519 button_new (FIND2_Y - 4, fbuts[0].x, B_STOP, NORMAL_BUTTON, fbuts[0].text, start_stop);
1520 add_widget (find_dlg, stop_button);
1521 add_widget (find_dlg,
1522 button_new (FIND2_Y - 4, fbuts[3].x, B_AGAIN, NORMAL_BUTTON, fbuts[3].text, NULL));
1523 add_widget (find_dlg,
1524 button_new (FIND2_Y - 4, fbuts[2].x, B_ENTER, DEFPUSH_BUTTON, fbuts[2].text, NULL));
1526 status_label = label_new (FIND2_Y - 7, 4, _("Searching"));
1527 add_widget (find_dlg, status_label);
1529 found_num_label = label_new (FIND2_Y - 6, 4, "");
1530 add_widget (find_dlg, found_num_label);
1532 find_list = listbox_new (2, 2, FIND2_Y - 10, FIND2_X - 4, FALSE, NULL);
1533 add_widget (find_dlg, find_list);
1536 /* --------------------------------------------------------------------------------------------- */
1538 static int
1539 run_process (void)
1541 int ret;
1543 search_content_handle = mc_search_new (content_pattern, -1);
1544 if (search_content_handle)
1546 search_content_handle->search_type =
1547 options.content_regexp ? MC_SEARCH_T_REGEX : MC_SEARCH_T_NORMAL;
1548 search_content_handle->is_case_sensitive = options.content_case_sens;
1549 search_content_handle->whole_words = options.content_whole_words;
1550 search_content_handle->is_all_charsets = options.content_all_charsets;
1552 search_file_handle = mc_search_new (find_pattern, -1);
1553 search_file_handle->search_type = options.file_pattern ? MC_SEARCH_T_GLOB : MC_SEARCH_T_REGEX;
1554 search_file_handle->is_case_sensitive = options.file_case_sens;
1555 search_file_handle->is_all_charsets = options.file_all_charsets;
1556 search_file_handle->is_entire_line = options.file_pattern;
1558 resuming = 0;
1560 set_idle_proc (find_dlg, 1);
1561 ret = run_dlg (find_dlg);
1563 mc_search_free (search_file_handle);
1564 search_file_handle = NULL;
1565 mc_search_free (search_content_handle);
1566 search_content_handle = NULL;
1568 return ret;
1571 /* --------------------------------------------------------------------------------------------- */
1573 static void
1574 kill_gui (void)
1576 set_idle_proc (find_dlg, 0);
1577 destroy_dlg (find_dlg);
1580 /* --------------------------------------------------------------------------------------------- */
1582 static int
1583 do_find (const char *start_dir, ssize_t start_dir_len, const char *ignore_dirs,
1584 const char *pattern, const char *content, char **dirname, char **filename)
1586 int return_value = 0;
1587 char *dir_tmp = NULL, *file_tmp = NULL;
1589 setup_gui ();
1591 /* FIXME: Need to cleanup this, this ought to be passed non-globaly */
1592 find_pattern = (char *) pattern;
1594 content_pattern = NULL;
1595 if (options.content_use && content != NULL && str_is_valid_string (content))
1596 content_pattern = g_strdup (content);
1598 init_find_vars ();
1599 parse_ignore_dirs (ignore_dirs);
1600 push_directory (vfs_path_from_str (start_dir));
1602 return_value = run_process ();
1604 /* Clear variables */
1605 init_find_vars ();
1607 get_list_info (&file_tmp, &dir_tmp);
1609 if (dir_tmp)
1610 *dirname = g_strdup (dir_tmp);
1611 if (file_tmp)
1612 *filename = g_strdup (file_tmp);
1614 if (return_value == B_PANELIZE && *filename)
1616 int status, link_to_dir, stale_link;
1617 int next_free = 0;
1618 int i;
1619 struct stat st;
1620 GList *entry;
1621 dir_list *list = &current_panel->dir;
1622 char *name = NULL;
1624 if (set_zero_dir (list))
1625 next_free++;
1627 for (i = 0, entry = find_list->list; entry != NULL; i++, entry = g_list_next (entry))
1629 const char *lc_filename = NULL;
1630 WLEntry *le = (WLEntry *) entry->data;
1631 char *p;
1633 if ((le->text == NULL) || (le->data == NULL))
1634 continue;
1636 if (content_pattern != NULL)
1637 lc_filename = strchr (le->text + 4, ':') + 1;
1638 else
1639 lc_filename = le->text + 4;
1641 name = mc_build_filename (le->data, lc_filename, (char *) NULL);
1642 /* skip initial start dir */
1643 if (start_dir_len < 0)
1644 p = name;
1645 else
1647 p = name + (size_t) start_dir_len;
1648 if (*p == PATH_SEP)
1649 p++;
1652 status = handle_path (list, p, &st, next_free, &link_to_dir, &stale_link);
1653 if (status == 0)
1655 g_free (name);
1656 continue;
1658 if (status == -1)
1660 g_free (name);
1661 break;
1664 /* don't add files more than once to the panel */
1665 if (content_pattern != NULL && next_free > 0
1666 && strcmp (list->list[next_free - 1].fname, p) == 0)
1668 g_free (name);
1669 continue;
1672 if (next_free == 0) /* first turn i.e clean old list */
1673 panel_clean_dir (current_panel);
1674 list->list[next_free].fnamelen = strlen (p);
1675 list->list[next_free].fname = g_strndup (p, list->list[next_free].fnamelen);
1676 list->list[next_free].f.marked = 0;
1677 list->list[next_free].f.link_to_dir = link_to_dir;
1678 list->list[next_free].f.stale_link = stale_link;
1679 list->list[next_free].f.dir_size_computed = 0;
1680 list->list[next_free].st = st;
1681 list->list[next_free].sort_key = NULL;
1682 list->list[next_free].second_sort_key = NULL;
1683 next_free++;
1684 g_free (name);
1685 if (!(next_free & 15))
1686 rotate_dash ();
1689 if (next_free)
1691 current_panel->count = next_free;
1692 current_panel->is_panelized = TRUE;
1694 /* absolute path */
1695 if (start_dir_len < 0)
1697 int ret;
1698 vfs_path_free (current_panel->cwd_vpath);
1699 current_panel->cwd_vpath = vfs_path_from_str (PATH_SEP_STR);
1700 ret = chdir (PATH_SEP_STR);
1702 panelize_save_panel (current_panel);
1706 g_free (content_pattern);
1707 kill_gui ();
1708 do_search (NULL); /* force do_search to release resources */
1709 g_free (old_dir);
1710 old_dir = NULL;
1712 return return_value;
1715 /* --------------------------------------------------------------------------------------------- */
1716 /*** public functions ****************************************************************************/
1717 /* --------------------------------------------------------------------------------------------- */
1719 void
1720 find_file (void)
1722 char *start_dir = NULL, *pattern = NULL, *content = NULL, *ignore_dirs = NULL;
1723 ssize_t start_dir_len;
1724 char *filename = NULL, *dirname = NULL;
1725 int v;
1727 while (find_parameters (&start_dir, &start_dir_len, &ignore_dirs, &pattern, &content))
1729 if (pattern[0] == '\0')
1730 break; /* nothing search */
1732 dirname = filename = NULL;
1733 is_start = FALSE;
1734 v = do_find (start_dir, start_dir_len, ignore_dirs, pattern, content, &dirname, &filename);
1735 g_free (ignore_dirs);
1736 g_free (pattern);
1738 if (v == B_ENTER)
1740 if (dirname != NULL)
1742 vfs_path_t *dirname_vpath;
1744 dirname_vpath = vfs_path_from_str (dirname);
1745 do_cd (dirname_vpath, cd_exact);
1746 vfs_path_free (dirname_vpath);
1747 if (filename != NULL)
1748 try_to_select (current_panel,
1749 filename + (content != NULL
1750 ? strchr (filename + 4, ':') - filename + 1 : 4));
1752 else if (filename != NULL)
1754 vfs_path_t *filename_vpath;
1756 filename_vpath = vfs_path_from_str (filename);
1757 do_cd (filename_vpath, cd_exact);
1758 vfs_path_free (filename_vpath);
1761 g_free (dirname);
1762 g_free (filename);
1763 break;
1766 g_free (content);
1767 g_free (dirname);
1768 g_free (filename);
1770 if (v == B_CANCEL)
1771 break;
1773 if (v == B_PANELIZE)
1775 panel_re_sort (current_panel);
1776 try_to_select (current_panel, NULL);
1777 break;
1782 /* --------------------------------------------------------------------------------------------- */