1 /* Find file command for the Midnight Commander
2 Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
3 2006, 2007 Free Software Foundation, Inc.
4 Written 1995 by Miguel de Icaza
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
23 * \brief Source: Find file command
35 #include "lib/global.h"
37 #include "lib/tty/tty.h"
38 #include "lib/tty/key.h"
40 #include "lib/search.h"
41 #include "lib/mcconfig.h"
42 #include "lib/vfs/mc-vfs/vfs.h"
43 #include "lib/strutil.h"
45 #include "setup.h" /* verbose */
49 #include "panel.h" /* current_panel */
50 #include "main.h" /* do_cd, try_to_select */
52 #include "cmd.h" /* view_file_at_line */
54 #include "history.h" /* MC_HISTORY_SHARED_SEARCH */
55 #include "layout.h" /* mc_refresh() */
59 /* Size of the find parameters window */
61 static int FIND_Y = 16;
63 static int FIND_Y = 15;
65 static int FIND_X = 68;
67 /* Size of the find window */
68 #define FIND2_Y (LINES - 4)
70 static int FIND2_X = 64;
71 #define FIND2_X_USE (FIND2_X - 20)
73 /* A couple of extra messages we need */
88 /* List of directories to be ignored, separated by ':' */
90 char *find_ignore_dirs = NULL;
92 /* static variables to remember find parameters */
93 static WInput *in_start; /* Start path */
94 static WInput *in_name; /* Filename */
95 static WInput *in_with; /* Text inside filename */
96 static WCheck *file_case_sens_cbox; /* "case sensitive" checkbox */
97 static WCheck *file_pattern_cbox; /* File name is glob or regexp */
98 static WCheck *recursively_cbox;
99 static WCheck *skip_hidden_cbox;
100 static WCheck *content_case_sens_cbox; /* "case sensitive" checkbox */
101 static WCheck *content_regexp_cbox; /* "find regular expression" checkbox */
102 static WCheck *content_first_hit_cbox; /* "First hit" checkbox" */
103 static WCheck *content_whole_words_cbox; /* "whole words" checkbox */
105 static WCheck *file_all_charsets_cbox;
106 static WCheck *content_all_charsets_cbox;
109 static gboolean running = FALSE; /* nice flag */
110 static char *find_pattern = NULL; /* Pattern to search */
111 static char *content_pattern = NULL; /* pattern to search inside files; if
112 content_regexp_flag is true, it contains the
113 regex pattern, else the search string. */
114 static unsigned long matches; /* Number of matches */
115 static gboolean is_start = FALSE; /* Status of the start/stop toggle button */
116 static char *old_dir = NULL;
118 char *find_ignore_dirs = 0;
120 static WInput *in_start; /* Start path */
121 static WInput *in_name; /* Pattern to search */
122 static WInput *in_with; /* Text inside filename */
123 static WCheck *case_sense; /* "case sensitive" checkbox */
124 static WCheck *find_regex_cbox; /* [x] find regular expression */
125 static WCheck *only_dirs_cbox; /* [x] Only directories */
127 static int running = 0; /* nice flag */
128 static char *find_pattern; /* Pattern to search */
129 static char *content_pattern; /* pattern to search inside files; if
130 find_regex_flag is true, it contains the
131 regex pattern, else the search string. */
132 static int count; /* Number of files displayed */
133 static int matches; /* Number of matches */
134 static int is_start; /* Status of the start/stop toggle button */
135 static char *old_dir;
136 >>>>>>> osp/only-directories
138 /* Where did we stop */
140 static int last_line;
143 static Dlg_head *find_dlg; /* The dialog */
144 static WButton *stop_button; /* pointer to the stop button */
145 static WLabel *status_label; /* Finished, Searching etc. */
146 static WLabel *found_num_label; /* Number of found items */
147 static WListbox *find_list; /* Listbox with the file list */
149 /* This keeps track of the directory stack */
150 #if GLIB_CHECK_VERSION (2, 14, 0)
151 static GQueue dir_queue = G_QUEUE_INIT;
153 typedef struct dir_stack {
155 struct dir_stack *prev;
158 static dir_stack *dir_stack_base = 0;
159 #endif /* GLIB_CHECK_VERSION */
163 int len; /* length including space and brackets */
166 { N_("&Suspend"), 11, 29 },
167 { N_("Con&tinue"), 12, 29 },
168 { N_("&Chdir"), 11, 3 },
169 { N_("&Again"), 9, 17 },
170 { N_("&Quit"), 8, 43 },
171 { N_("Pane&lize"), 12, 3 },
172 { N_("&View - F3"), 13, 20 },
173 { N_("&Edit - F4"), 13, 38 }
176 /* find file options */
179 /* file name options */
180 gboolean file_case_sens;
181 gboolean file_pattern;
182 gboolean find_recurs;
183 gboolean skip_hidden;
184 gboolean file_all_charsets;
186 /* file content options */
187 gboolean content_case_sens;
188 gboolean content_regexp;
189 gboolean content_first_hit;
190 gboolean content_whole_words;
191 gboolean content_all_charsets;
192 } find_file_options_t;
194 static find_file_options_t options =
196 TRUE, TRUE, TRUE, FALSE, FALSE,
197 TRUE, FALSE, FALSE, FALSE, FALSE
200 static char *in_start_dir = INPUT_LAST_TEXT;
202 static mc_search_t *search_file_handle = NULL;
203 static mc_search_t *search_content_handle = NULL;
206 find_load_options (void)
208 static gboolean loaded = FALSE;
216 /* Back compatibility: try load old parameter at first */
217 ignore_dirs = mc_config_get_string (mc_main_config, "Misc", "find_ignore_dirs", "");
218 if (ignore_dirs [0] != '\0') {
219 find_ignore_dirs = g_strconcat (":", ignore_dirs, ":", (char *) NULL);
220 mc_config_set_string (mc_main_config, "FindFile", "ignore_dirs", ignore_dirs);
222 g_free (ignore_dirs);
223 mc_config_del_param (mc_main_config, "Misc", "find_ignore_dirs");
225 /* Then load new parameters */
226 ignore_dirs = mc_config_get_string (mc_main_config, "FindFile", "ignore_dirs", "");
227 if (ignore_dirs [0] != '\0') {
228 g_free (find_ignore_dirs);
229 find_ignore_dirs = g_strconcat (":", ignore_dirs, ":", (char *) NULL);
231 g_free (ignore_dirs);
233 options.file_case_sens = mc_config_get_bool (mc_main_config, "FindFile", "file_case_sens", TRUE);
234 options.file_pattern = mc_config_get_bool (mc_main_config, "FindFile", "file_shell_pattern", TRUE);
235 options.find_recurs = mc_config_get_bool (mc_main_config, "FindFile", "file_find_recurs", TRUE);
236 options.skip_hidden = mc_config_get_bool (mc_main_config, "FindFile", "file_skip_hidden", FALSE);
237 options.file_all_charsets = mc_config_get_bool (mc_main_config, "FindFile", "file_all_charsets", FALSE);
238 options.content_case_sens = mc_config_get_bool (mc_main_config, "FindFile", "content_case_sens", TRUE);
239 options.content_regexp = mc_config_get_bool (mc_main_config, "FindFile", "content_regexp", FALSE);
240 options.content_first_hit = mc_config_get_bool (mc_main_config, "FindFile", "content_first_hit", FALSE);
241 options.content_whole_words = mc_config_get_bool (mc_main_config, "FindFile", "content_whole_words", FALSE);
242 options.content_all_charsets = mc_config_get_bool (mc_main_config, "FindFile", "content_all_charsets", FALSE);
246 find_save_options (void)
248 mc_config_set_bool (mc_main_config, "FindFile", "file_case_sens", options.file_case_sens);
249 mc_config_set_bool (mc_main_config, "FindFile", "file_shell_pattern", options.file_pattern);
250 mc_config_set_bool (mc_main_config, "FindFile", "file_find_recurs", options.find_recurs);
251 mc_config_set_bool (mc_main_config, "FindFile", "file_skip_hidden", options.skip_hidden);
252 mc_config_set_bool (mc_main_config, "FindFile", "file_all_charsets", options.file_all_charsets);
253 mc_config_set_bool (mc_main_config, "FindFile", "content_case_sens", options.content_case_sens);
254 mc_config_set_bool (mc_main_config, "FindFile", "content_regexp", options.content_regexp);
255 mc_config_set_bool (mc_main_config, "FindFile", "content_first_hit", options.content_first_hit);
256 mc_config_set_bool (mc_main_config, "FindFile", "content_whole_words", options.content_whole_words);
257 mc_config_set_bool (mc_main_config, "FindFile", "content_all_charsets", options.content_all_charsets);
261 add_to_list (const char *text, void *data)
263 return listbox_add_item (find_list, LISTBOX_APPEND_AT_END, 0, text, data);
267 stop_idle (void *data)
269 set_idle_proc (data, 0);
273 status_update (const char *text)
275 label_set_text (status_label, text);
279 found_num_update (void)
281 char buffer [BUF_TINY];
282 g_snprintf (buffer, sizeof (buffer), _("Found: %ld"), matches);
283 label_set_text (found_num_label, buffer);
288 get_list_info (char **file, char **dir)
290 listbox_get_current (find_list, file, (void **) dir);
293 /* check regular expression */
295 find_check_regexp (const char *r)
298 gboolean regexp_ok = FALSE;
300 search = mc_search_new (r, -1);
302 if (search != NULL) {
303 search->search_type = MC_SEARCH_T_REGEX;
304 regexp_ok = mc_search_prepare (search);
305 mc_search_free (search);
311 /* FIXME: r should be local variable */
312 static regex_t *r; /* Pointer to compiled content_pattern */
314 static int case_sensitive = 1;
315 static gboolean find_regex_flag = TRUE;
316 static gboolean only_dirs_flag = FALSE;
317 static int find_recursively = 1;
318 >>>>>>> osp/only-directories
321 * Callback for the parameter dialog.
322 * Validate regex, prevent closing the dialog if it's invalid.
325 find_parm_callback (Dlg_head *h, Widget *sender,
326 dlg_msg_t msg, int parm, void *data)
330 if (h->ret_value != B_ENTER)
333 /* check filename regexp */
334 if (!(file_pattern_cbox->state & C_BOOL)
335 && (in_name->buffer[0] != '\0')
336 && !find_check_regexp (in_name->buffer)) {
337 message (D_ERROR, MSG_ERROR, _(" Malformed regular expression "));
338 dlg_select_widget (in_name);
339 h->running = 1; /* Don't stop the dialog */
343 /* check content regexp */
344 if ((content_regexp_cbox->state & C_BOOL)
345 && (in_with->buffer[0] != '\0')
346 && !find_check_regexp (in_with->buffer)) {
347 message (D_ERROR, MSG_ERROR, _(" Malformed regular expression "));
348 dlg_select_widget (in_with);
349 h->running = 1; /* Don't stop the dialog */
356 return default_dlg_callback (h, sender, msg, parm, data);
361 * find_parameters: gets information from the user
363 * If the return value is TRUE, then the following holds:
365 * START_DIR and PATTERN are pointers to char * and upon return they
366 * contain the information provided by the user.
368 * CONTENT holds a strdup of the contents specified by the user if he
369 * asked for them or 0 if not (note, this is different from the
370 * behavior for the other two parameters.
374 find_parameters (char **start_dir, char **pattern, char **content)
376 gboolean return_value;
379 const char *file_case_label = N_("Cas&e sensitive");
380 const char *file_pattern_label = N_("&Using shell patterns");
381 const char *file_recurs_label = N_("&Find recursively");
382 const char *file_skip_hidden_label = N_("S&kip hidden");
384 const char *file_all_charsets_label = N_("&All charsets");
388 const char *content_case_label = N_("Case sens&itive");
389 const char *content_regexp_label = N_("Re&gular expression");
390 const char *content_first_hit_label = N_("Fir&st hit");
391 const char *content_whole_words_label = N_("&Whole words");
393 const char *content_all_charsets_label = N_("All cha&rsets");
396 const char *buts[] = { N_("&OK"), N_("&Cancel"), N_("&Tree") };
402 int i = sizeof (buts) / sizeof (buts[0]);
404 buts[i] = _(buts[i]);
406 file_case_label = _(file_case_label);
407 file_pattern_label = _(file_pattern_label);
408 file_recurs_label = _(file_recurs_label);
409 file_skip_hidden_label = _(file_skip_hidden_label);
411 file_all_charsets_label = _(file_all_charsets_label);
412 content_all_charsets_label = _(content_all_charsets_label);
414 content_case_label = _(content_case_label);
415 content_regexp_label = _(content_regexp_label);
416 content_first_hit_label = _(content_first_hit_label);
417 content_whole_words_label = _(content_whole_words_label);
419 #endif /* ENABLE_NLS */
421 b0 = str_term_width1 (buts[0]) + 6; /* default button */
422 b1 = str_term_width1 (buts[1]) + 4;
423 b2 = str_term_width1 (buts[2]) + 4;
425 find_load_options ();
427 if (in_start_dir == NULL)
428 in_start_dir = g_strdup (".");
432 create_dlg (0, 0, FIND_Y, FIND_X, dialog_colors,
433 find_parm_callback, "[Find File]", _("Find File"),
434 DLG_CENTER | DLG_REVERSE);
436 add_widget (find_dlg,
437 button_new (FIND_Y - 3, FIND_X * 3/4 - b1/2, B_CANCEL, NORMAL_BUTTON, buts[1], 0));
438 add_widget (find_dlg,
439 button_new (FIND_Y - 3, FIND_X/4 - b0/2, B_ENTER, DEFPUSH_BUTTON, buts[0], 0));
443 content_all_charsets_cbox = check_new (11, FIND_X / 2 + 1,
444 options.content_all_charsets, content_all_charsets_label);
445 add_widget (find_dlg, content_all_charsets_cbox);
449 check_new (6, istart, find_recursively, recurs_label);
451 check_new (7, istart, only_dirs_flag, _("Only &directories"));
453 find_regex_cbox = check_new (10, istart, find_regex_flag, _("&Regular expression"));
454 add_widget (find_dlg, find_regex_cbox);
455 >>>>>>> osp/only-directories
457 content_whole_words_cbox = check_new (10, FIND_X / 2 + 1, options.content_whole_words, content_whole_words_label);
458 add_widget (find_dlg, content_whole_words_cbox);
460 content_first_hit_cbox = check_new (9, FIND_X / 2 + 1, options.content_first_hit, content_first_hit_label);
461 add_widget (find_dlg, content_first_hit_cbox);
463 content_regexp_cbox = check_new (8, FIND_X / 2 + 1, options.content_regexp, content_regexp_label);
464 add_widget (find_dlg, content_regexp_cbox);
466 content_case_sens_cbox = check_new (7, FIND_X / 2 + 1, options.content_case_sens, content_case_label);
467 add_widget (find_dlg, content_case_sens_cbox);
470 file_all_charsets_cbox = check_new (11, 3,
471 options.file_all_charsets, file_all_charsets_label);
472 add_widget (find_dlg, file_all_charsets_cbox);
477 skip_hidden_cbox = check_new (10, 3, options.skip_hidden, file_skip_hidden_label);
478 add_widget (find_dlg, skip_hidden_cbox);
480 recursively_cbox = check_new (9, 3, options.find_recurs, file_recurs_label);
482 add_widget (find_dlg, only_dirs_cbox);
483 >>>>>>> osp/only-directories
485 add_widget (find_dlg, only_dirs_cbox);
486 >>>>>>> osp/only-directories
487 add_widget (find_dlg, recursively_cbox);
489 file_pattern_cbox = check_new (8, 3, options.file_pattern, file_pattern_label);
490 add_widget (find_dlg, file_pattern_cbox);
492 file_case_sens_cbox = check_new (7, 3, options.file_case_sens, file_case_label);
493 add_widget (find_dlg, file_case_sens_cbox);
495 in_with = input_new (6, FIND_X / 2 + 1, INPUT_COLOR, FIND_X / 2 - 4, INPUT_LAST_TEXT,
496 MC_HISTORY_SHARED_SEARCH, INPUT_COMPLETE_DEFAULT);
497 add_widget (find_dlg, in_with);
498 add_widget (find_dlg, label_new (5, FIND_X / 2 + 1, _("Content:")));
500 in_name = input_new (6, 3, INPUT_COLOR, FIND_X / 2 - 4, INPUT_LAST_TEXT, "name",
501 INPUT_COMPLETE_DEFAULT);
502 add_widget (find_dlg, in_name);
503 add_widget (find_dlg, label_new (5, 3, _("File name:")));
505 add_widget (find_dlg,
506 button_new (3, FIND_X - b2 - 2, B_TREE, NORMAL_BUTTON, buts[2], 0));
508 in_start = input_new (3, 3, INPUT_COLOR, FIND_X - b2 - 6, in_start_dir, "start",
509 INPUT_COMPLETE_DEFAULT);
510 add_widget (find_dlg, in_start);
511 add_widget (find_dlg, label_new (2, 3, _("Start at:")));
513 dlg_select_widget (in_name);
515 switch (run_dlg (find_dlg)) {
517 return_value = FALSE;
523 char temp_dir[MC_MAXPATHLEN];
525 g_strlcpy (temp_dir, in_start->buffer, sizeof (temp_dir));
527 options.file_all_charsets = file_all_charsets_cbox->state & C_BOOL;
528 options.content_all_charsets = content_all_charsets_cbox->state & C_BOOL;
530 options.content_case_sens = content_case_sens_cbox->state & C_BOOL;
531 options.content_regexp = content_regexp_cbox->state & C_BOOL;
532 options.content_first_hit = content_first_hit_cbox->state & C_BOOL;
533 options.content_whole_words = content_whole_words_cbox->state & C_BOOL;
534 options.file_pattern = file_pattern_cbox->state & C_BOOL;
535 options.file_case_sens = file_case_sens_cbox->state & C_BOOL;
536 options.find_recurs = recursively_cbox->state & C_BOOL;
537 options.skip_hidden = skip_hidden_cbox->state & C_BOOL;
539 temp_dir = g_strdup (in_start->buffer);
540 case_sensitive = case_sense->state & C_BOOL;
541 find_regex_flag = find_regex_cbox->state & C_BOOL;
542 find_recursively = recursively_cbox->state & C_BOOL;
543 only_dirs_flag = only_dirs_cbox->state & C_BOOL;
545 >>>>>>> osp/only-directories
547 >>>>>>> osp/only-directories
548 destroy_dlg (find_dlg);
550 if ((temp_dir[0] == '\0')
551 || ((temp_dir[0] == '.') && (temp_dir[1] == '\0')))
552 g_strlcpy (temp_dir, current_panel->cwd, sizeof (temp_dir));
554 if (in_start_dir != INPUT_LAST_TEXT)
555 g_free (in_start_dir);
556 in_start_dir = tree_box (temp_dir);
557 if (in_start_dir == NULL)
558 in_start_dir = g_strdup (temp_dir);
559 /* Warning: Dreadful goto */
567 options.file_all_charsets = file_all_charsets_cbox->state & C_BOOL;
568 options.content_all_charsets = content_all_charsets_cbox->state & C_BOOL;
570 options.content_case_sens = content_case_sens_cbox->state & C_BOOL;
571 options.content_regexp = content_regexp_cbox->state & C_BOOL;
572 options.content_first_hit = content_first_hit_cbox->state & C_BOOL;
573 options.content_whole_words = content_whole_words_cbox->state & C_BOOL;
574 options.find_recurs = recursively_cbox->state & C_BOOL;
575 options.file_pattern = file_pattern_cbox->state & C_BOOL;
576 options.file_case_sens = file_case_sens_cbox->state & C_BOOL;
577 options.skip_hidden = skip_hidden_cbox->state & C_BOOL;
579 *content = (in_with->buffer[0] != '\0') ? g_strdup (in_with->buffer) : NULL;
580 *start_dir = g_strdup ((in_start->buffer[0] != '\0') ? in_start->buffer : ".");
582 g_free (in_contents);
583 if (in_with->buffer[0]) {
584 *content = g_strdup (in_with->buffer);
585 in_contents = g_strdup (*content);
587 *content = in_contents = NULL;
591 case_sensitive = case_sense->state & C_BOOL;
592 find_regex_flag = find_regex_cbox->state & C_BOOL;
593 find_recursively = recursively_cbox->state & C_BOOL;
594 only_dirs_flag = only_dirs_cbox->state & C_BOOL;
596 *start_dir = g_strdup (in_start->buffer);
597 >>>>>>> osp/only-directories
598 *pattern = g_strdup (in_name->buffer);
599 if (in_start_dir != INPUT_LAST_TEXT)
600 g_free (in_start_dir);
601 in_start_dir = g_strdup (*start_dir);
603 find_save_options ();
608 destroy_dlg (find_dlg);
613 #if GLIB_CHECK_VERSION (2, 14, 0)
616 push_directory (const char *dir)
618 g_queue_push_head (&dir_queue, (void *) dir);
624 return (char *) g_queue_pop_tail (&dir_queue);
627 /* Remove all the items in the stack */
631 g_queue_foreach (&dir_queue, (GFunc) g_free, NULL);
632 g_queue_clear (&dir_queue);
635 #else /* GLIB_CHAECK_VERSION */
638 push_directory (const char *dir)
642 new = g_new (dir_stack, 1);
643 new->name = str_unconst (dir);
644 new->prev = dir_stack_base;
645 dir_stack_base = new;
653 if (dir_stack_base != NULL) {
655 name = dir_stack_base->name;
656 next = dir_stack_base->prev;
657 g_free (dir_stack_base);
658 dir_stack_base = next;
664 /* Remove all the items in the stack */
669 while ((dir = pop_directory ()) != NULL)
673 #endif /* GLIB_CHAECK_VERSION */
676 insert_file (const char *dir, const char *file)
678 char *tmp_name = NULL;
679 static char *dirname = NULL;
681 while (dir [0] == PATH_SEP && dir [1] == PATH_SEP)
685 if (strcmp (old_dir, dir)){
687 old_dir = g_strdup (dir);
688 dirname = add_to_list (dir, NULL);
691 old_dir = g_strdup (dir);
692 dirname = add_to_list (dir, NULL);
695 tmp_name = g_strdup_printf (" %s", file);
696 add_to_list (tmp_name, dirname);
701 find_add_match (const char *dir, const char *file)
703 insert_file (dir, file);
707 listbox_select_first (find_list);
708 send_message (&find_list->widget, WIDGET_DRAW, 0);
717 * Returns malloced null-terminated line from file file_fd.
718 * Input is buffered in buf_size long buffer.
719 * Current pos in buf is stored in pos.
720 * n_read - number of read chars.
721 * has_newline - is there newline ?
724 get_line_at (int file_fd, char *buf, int buf_size, int *pos, int *n_read,
725 gboolean *has_newline)
733 if (*pos >= *n_read) {
735 *n_read = mc_read (file_fd, buf, buf_size);
742 /* skip possible leading zero(s) */
749 if (i >= buffer_size - 1) {
750 buffer = g_realloc (buffer, buffer_size += 80);
759 *has_newline = (ch != '\0');
767 static FindProgressStatus
768 check_find_events(Dlg_head *h)
774 c = tty_get_event (&event, h->mouse_status == MOU_REPEAT, FALSE);
776 dlg_process_event (h, c, &event);
777 if (h->ret_value == B_ENTER
778 || h->ret_value == B_CANCEL
779 || h->ret_value == B_AGAIN
780 || h->ret_value == B_PANELIZE) {
781 /* dialog terminated */
784 if (!(h->flags & DLG_WANT_IDLE)) {
785 /* searching suspended */
796 * Search the content_pattern string in the DIRECTORY/FILE.
797 * It will add the found entries to the find listbox.
799 * returns FALSE if do_search should look for another file
800 * TRUE if do_search should exit and proceed to the event handler
803 search_content (Dlg_head *h, const char *directory, const char *filename)
806 char buffer [BUF_4K];
809 gboolean ret_val = FALSE;
811 fname = concat_dir_and_file (directory, filename);
813 if (mc_stat (fname, &s) != 0 || !S_ISREG (s.st_mode)){
818 file_fd = mc_open (fname, O_RDONLY);
824 g_snprintf (buffer, sizeof (buffer), _("Grepping in %s"), str_trunc (filename, FIND2_X_USE));
826 status_update (buffer);
829 tty_enable_interrupt_key ();
830 tty_got_interrupt ();
836 gboolean has_newline;
838 gboolean found = FALSE;
840 char result [BUF_MEDIUM];
843 /* We've been previously suspended, start from the previous position */
849 && (p = get_line_at (file_fd, buffer, sizeof (buffer),
850 &pos, &n_read, &has_newline)) != NULL) {
851 if (!found /* Search in binary line once */
852 && mc_search_run (search_content_handle,
853 (const void *) p, 0, strlen (p), &found_len)) {
854 g_snprintf (result, sizeof (result), "%d:%s", line, filename);
855 find_add_match (directory, result);
860 if (found && options.content_first_hit)
868 if ((line & 0xff) == 0) {
869 FindProgressStatus res;
870 res = check_find_events(h);
889 tty_disable_interrupt_key ();
895 do_search (struct Dlg_head *h)
897 static struct dirent *dp = NULL;
898 static DIR *dirp = NULL;
899 static char *directory = NULL;
900 struct stat tmp_stat;
902 static int subdirs_left = 0;
904 unsigned long count; /* Number of files displayed */
906 if (!h) { /* someone forces me to close dirp */
917 search_content_handle = mc_search_new(content_pattern, -1);
918 if (search_content_handle) {
919 search_content_handle->search_type = options.content_regexp ? MC_SEARCH_T_REGEX : MC_SEARCH_T_NORMAL;
920 search_content_handle->is_case_sentitive = options.content_case_sens;
921 search_content_handle->whole_words = options.content_whole_words;
922 search_content_handle->is_all_charsets = options.content_all_charsets;
924 search_file_handle = mc_search_new(find_pattern, -1);
925 search_file_handle->search_type = options.file_pattern ? MC_SEARCH_T_GLOB : MC_SEARCH_T_REGEX;
926 search_file_handle->is_case_sentitive = options.file_case_sens;
927 search_file_handle->is_all_charsets = options.file_all_charsets;
928 search_file_handle->is_entire_line = options.file_pattern;
942 tty_setcolor (REVERSE_COLOR);
944 char *temp_dir = NULL;
947 tmp = pop_directory ();
950 status_update (_("Finished"));
952 mc_search_free (search_file_handle);
953 search_file_handle = NULL;
954 mc_search_free (search_content_handle);
955 search_content_handle = NULL;
959 if ((find_ignore_dirs == NULL) || (find_ignore_dirs[0] == '\0'))
962 temp_dir = g_strdup_printf (":%s:", tmp);
963 found = strstr (find_ignore_dirs, temp_dir) != 0;
976 char buffer [BUF_SMALL];
978 g_snprintf (buffer, sizeof (buffer), _("Searching %s"),
979 str_trunc (directory, FIND2_X_USE));
980 status_update (buffer);
982 /* mc_stat should not be called after mc_opendir
983 because vfs_s_opendir modifies the st_nlink
985 if (!mc_stat (directory, &tmp_stat))
986 subdirs_left = tmp_stat.st_nlink - 2;
990 dirp = mc_opendir (directory);
991 } /* while (!dirp) */
993 /* skip invalid filenames */
994 while ((dp = mc_readdir (dirp)) != NULL
995 && !str_is_valid_string (dp->d_name))
999 if (strcmp (dp->d_name, ".") == 0 ||
1000 strcmp (dp->d_name, "..") == 0){
1001 dp = mc_readdir (dirp);
1002 /* skip invalid filenames */
1003 while (dp != NULL && !str_is_valid_string (dp->d_name))
1004 dp = mc_readdir (dirp);
1006 mc_search_free(search_file_handle);
1007 search_file_handle = NULL;
1008 mc_search_free(search_content_handle);
1009 search_content_handle = NULL;
1014 if (!(options.skip_hidden && (dp->d_name[0] == '.'))) {
1017 if ((subdirs_left != 0) && options.find_recurs
1018 && (directory != NULL)) { /* Can directory be NULL ? */
1019 char *tmp_name = concat_dir_and_file (directory, dp->d_name);
1020 if (!mc_lstat (tmp_name, &tmp_stat)
1021 && S_ISDIR (tmp_stat.st_mode)) {
1022 push_directory (tmp_name);
1028 search_ok = mc_search_run (search_file_handle, dp->d_name,
1029 0, strlen (dp->d_name), &bytes_found);
1032 if (content_pattern == NULL)
1033 find_add_match (directory, dp->d_name);
1034 else if (search_content (h, directory, dp->d_name)) {
1035 mc_search_free(search_file_handle);
1036 search_file_handle = NULL;
1037 mc_search_free(search_content_handle);
1038 search_content_handle = NULL;
1044 /* skip invalid filenames */
1045 while ((dp = mc_readdir (dirp)) != NULL
1046 && !str_is_valid_string (dp->d_name))
1049 if (subdirs_left && find_recursively && directory) { /* Can directory be NULL ? */
1050 char *tmp_name = mhl_str_dir_plus_file (directory, dp->d_name);
1051 if (!mc_lstat (tmp_name, &tmp_stat)
1052 && S_ISDIR (tmp_stat.st_mode)) {
1053 push_directory (tmp_name);
1055 if (only_dirs_flag && regexp_match (find_pattern, dp->d_name, match_file)) {
1056 find_add_match (h, directory, dp->d_name);
1062 if (!only_dirs_flag && regexp_match (find_pattern, dp->d_name, match_file)){
1063 if (content_pattern) {
1064 if (search_content (h, directory, dp->d_name)) {
1068 find_add_match (h, directory, dp->d_name);
1071 dp = mc_readdir (dirp);
1072 >>>>>>> osp/only-directories
1074 /* Displays the nice dot */
1077 /* For nice updating */
1078 const char rotating_dash[] = "|/-\\";
1081 pos = (pos + 1) % 4;
1082 tty_setcolor (DLG_NORMALC (h));
1083 dlg_move (h, FIND2_Y - 7, FIND2_X - 4);
1084 tty_print_char (rotating_dash [pos]);
1088 goto do_search_begin;
1090 mc_search_free (search_file_handle);
1091 search_file_handle = NULL;
1092 mc_search_free (search_content_handle);
1093 search_content_handle = NULL;
1098 init_find_vars (void)
1104 /* Remove all the items in the stack */
1109 make_fullname (const char *dirname, const char *filename)
1112 if (strcmp(dirname, ".") == 0 || strcmp(dirname, "."PATH_SEP_STR) == 0)
1113 return g_strdup (filename);
1114 if (strncmp(dirname, "."PATH_SEP_STR, 2) == 0)
1115 return concat_dir_and_file (dirname + 2, filename);
1116 return concat_dir_and_file (dirname, filename);
1120 find_do_view_edit (int unparsed_view, int edit, char *dir, char *file)
1122 char *fullname = NULL;
1123 const char *filename = NULL;
1126 if (content_pattern != NULL) {
1127 filename = strchr (file + 4, ':') + 1;
1128 line = atoi (file + 4);
1130 filename = file + 4;
1134 fullname = make_fullname (dir, filename);
1136 do_edit_at_line (fullname, line);
1138 view_file_at_line (fullname, unparsed_view, use_internal_view, line);
1143 view_edit_currently_selected_file (int unparsed_view, int edit)
1148 listbox_get_current (find_list, &text, (void **) &dir);
1150 if ((text == NULL) || (dir == NULL))
1151 return MSG_NOT_HANDLED;
1153 find_do_view_edit (unparsed_view, edit, dir, text);
1158 find_callback (struct Dlg_head *h, Widget *sender,
1159 dlg_msg_t msg, int parm, void *data)
1163 if (parm == KEY_F (3) || parm == KEY_F (13)) {
1164 int unparsed_view = (parm == KEY_F (13));
1165 return view_edit_currently_selected_file (unparsed_view, 0);
1167 if (parm == KEY_F (4)) {
1168 return view_edit_currently_selected_file (0, 1);
1170 return MSG_NOT_HANDLED;
1177 return default_dlg_callback (h, sender, msg, parm, data);
1181 /* Handles the Stop/Start button in the find window */
1183 start_stop (int button)
1188 set_idle_proc (find_dlg, running);
1189 is_start = !is_start;
1191 status_update (is_start ? _("Stopped") : _("Searching"));
1192 button_set_text (stop_button, fbuts [is_start ? 1 : 0].text);
1197 /* Handle view command, when invoked as a button */
1199 find_do_view_file (int button)
1203 view_edit_currently_selected_file (0, 0);
1207 /* Handle edit command, when invoked as a button */
1209 find_do_edit_file (int button)
1213 view_edit_currently_selected_file (0, 1);
1221 static gboolean i18n_flag = FALSE;
1224 int i = sizeof (fbuts) / sizeof (fbuts[0]);
1226 fbuts[i].text = _(fbuts[i].text);
1227 fbuts[i].len = str_term_width1 (fbuts[i].text) + 3;
1230 fbuts[2].len += 2; /* DEFPUSH_BUTTON */
1233 #endif /* ENABLE_NLS */
1236 * Dynamically place buttons centered within current window size
1239 int l0 = max (fbuts[0].len, fbuts[1].len);
1240 int l1 = fbuts[2].len + fbuts[3].len + l0 + fbuts[4].len;
1241 int l2 = fbuts[5].len + fbuts[6].len + fbuts[7].len;
1244 /* Check, if both button rows fit within FIND2_X */
1245 FIND2_X = max (l1 + 9, COLS - 16);
1246 FIND2_X = max (l2 + 8, FIND2_X);
1248 /* compute amount of space between buttons for each row */
1249 r1 = (FIND2_X - 4 - l1) % 5;
1250 l1 = (FIND2_X - 4 - l1) / 5;
1251 r2 = (FIND2_X - 4 - l2) % 4;
1252 l2 = (FIND2_X - 4 - l2) / 4;
1254 /* ...and finally, place buttons */
1255 fbuts[2].x = 2 + r1 / 2 + l1;
1256 fbuts[3].x = fbuts[2].x + fbuts[2].len + l1;
1257 fbuts[0].x = fbuts[3].x + fbuts[3].len + l1;
1258 fbuts[4].x = fbuts[0].x + l0 + l1;
1259 fbuts[5].x = 2 + r2 / 2 + l2;
1260 fbuts[6].x = fbuts[5].x + fbuts[5].len + l2;
1261 fbuts[7].x = fbuts[6].x + fbuts[6].len + l2;
1265 create_dlg (0, 0, FIND2_Y, FIND2_X, dialog_colors, find_callback,
1266 "[Find File]", _("Find File"), DLG_CENTER | DLG_REVERSE);
1268 add_widget (find_dlg,
1269 button_new (FIND2_Y - 3, fbuts[7].x, B_VIEW, NORMAL_BUTTON,
1270 fbuts[7].text, find_do_edit_file));
1271 add_widget (find_dlg,
1272 button_new (FIND2_Y - 3, fbuts[6].x, B_VIEW, NORMAL_BUTTON,
1273 fbuts[6].text, find_do_view_file));
1274 add_widget (find_dlg,
1275 button_new (FIND2_Y - 3, fbuts[5].x, B_PANELIZE,
1276 NORMAL_BUTTON, fbuts[5].text, 0));
1278 add_widget (find_dlg,
1279 button_new (FIND2_Y - 4, fbuts[4].x, B_CANCEL,
1280 NORMAL_BUTTON, fbuts[4].text, 0));
1282 button_new (FIND2_Y - 4, fbuts[0].x, B_STOP, NORMAL_BUTTON,
1283 fbuts[0].text, start_stop);
1284 add_widget (find_dlg, stop_button);
1285 add_widget (find_dlg,
1286 button_new (FIND2_Y - 4, fbuts[3].x, B_AGAIN,
1287 NORMAL_BUTTON, fbuts[3].text, 0));
1288 add_widget (find_dlg,
1289 button_new (FIND2_Y - 4, fbuts[2].x, B_ENTER,
1290 DEFPUSH_BUTTON, fbuts[2].text, 0));
1292 status_label = label_new (FIND2_Y - 7, 4, _("Searching"));
1293 add_widget (find_dlg, status_label);
1295 found_num_label = label_new (FIND2_Y - 6, 4, "");
1296 add_widget (find_dlg, found_num_label);
1299 listbox_new (2, 2, FIND2_Y - 10, FIND2_X - 4, FALSE, NULL);
1300 add_widget (find_dlg, find_list);
1307 set_idle_proc (find_dlg, 1);
1308 return run_dlg (find_dlg);
1314 set_idle_proc (find_dlg, 0);
1315 destroy_dlg (find_dlg);
1319 find_file (const char *start_dir, const char *pattern, const char *content,
1320 char **dirname, char **filename)
1322 int return_value = 0;
1323 char *dir_tmp = NULL, *file_tmp = NULL;
1327 /* FIXME: Need to cleanup this, this ought to be passed non-globaly */
1328 find_pattern = str_unconst (pattern);
1329 content_pattern = (content != NULL && str_is_valid_string (content))
1334 push_directory (start_dir);
1336 return_value = run_process ();
1338 /* Remove all the items in the stack */
1341 get_list_info (&file_tmp, &dir_tmp);
1344 *dirname = g_strdup (dir_tmp);
1346 *filename = g_strdup (file_tmp);
1348 if (return_value == B_PANELIZE && *filename) {
1349 int status, link_to_dir, stale_link;
1354 dir_list *list = ¤t_panel->dir;
1357 for (i = 0, entry = find_list->list; entry != NULL;
1358 i++, entry = g_list_next (entry)) {
1359 const char *lc_filename = NULL;
1360 WLEntry *le = (WLEntry *) entry->data;
1362 if ((le->text == NULL) || (le->data == NULL))
1365 if (content_pattern != NULL)
1366 lc_filename = strchr (le->text + 4, ':') + 1;
1368 lc_filename = le->text + 4;
1370 name = make_fullname (le->data, lc_filename);
1372 handle_path (list, name, &st, next_free, &link_to_dir,
1383 /* don't add files more than once to the panel */
1384 if (content_pattern != NULL && next_free > 0
1385 && strcmp (list->list[next_free - 1].fname, name) == 0) {
1390 if (!next_free) /* first turn i.e clean old list */
1391 panel_clean_dir (current_panel);
1392 list->list[next_free].fnamelen = strlen (name);
1393 list->list[next_free].fname = name;
1394 list->list[next_free].f.marked = 0;
1395 list->list[next_free].f.link_to_dir = link_to_dir;
1396 list->list[next_free].f.stale_link = stale_link;
1397 list->list[next_free].f.dir_size_computed = 0;
1398 list->list[next_free].st = st;
1399 list->list[next_free].sort_key = NULL;
1400 list->list[next_free].second_sort_key = NULL;
1402 if (!(next_free & 15))
1407 current_panel->count = next_free;
1408 current_panel->is_panelized = 1;
1410 if (start_dir[0] == PATH_SEP) {
1411 strcpy (current_panel->cwd, PATH_SEP_STR);
1412 chdir (PATH_SEP_STR);
1417 g_free (content_pattern);
1419 do_search (NULL); /* force do_search to release resources */
1423 return return_value;
1429 char *start_dir = NULL, *pattern = NULL, *content = NULL;
1430 char *filename = NULL, *dirname = NULL;
1432 gboolean dir_and_file_set;
1434 while (find_parameters (&start_dir, &pattern, &content)){
1435 if (pattern [0] == '\0')
1436 break; /* nothing search*/
1438 dirname = filename = NULL;
1440 v = find_file (start_dir, pattern, content, &dirname, &filename);
1444 if (dirname || filename){
1446 do_cd (dirname, cd_exact);
1448 try_to_select (current_panel, filename + (content ?
1449 (strchr (filename + 4, ':') - filename + 1) : 4) );
1450 } else if (filename)
1451 do_cd (filename, cd_exact);
1452 select_item (current_panel);
1460 dir_and_file_set = dirname && filename;
1467 if (v == B_PANELIZE){
1468 if (dir_and_file_set){
1469 try_to_select (current_panel, NULL);
1470 panel_re_sort (current_panel);
1471 try_to_select (current_panel, NULL);