Free FS info before closing panel.
[midnight-commander.git] / src / find.c
blobfb7277bfdaeaf5cbbd8a11898f06487c658f7a3b
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
6 Complete rewrote.
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. */
22 /** \file find.c
23 * \brief Source: Find file command
26 #include <config.h>
28 #include <ctype.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <fcntl.h>
33 #include <sys/stat.h>
35 #include "lib/global.h"
37 #include "lib/tty/tty.h"
38 #include "lib/tty/key.h"
39 #include "lib/skin.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"
46 #include "setup.h" /* verbose */
47 #include "find.h"
48 #include "dialog.h"
49 #include "widget.h"
50 #include "dir.h"
51 #include "panel.h" /* current_panel */
52 #include "main.h" /* do_cd, try_to_select */
53 #include "wtools.h"
54 #include "cmd.h" /* view_file_at_line */
55 #include "boxes.h"
56 #include "history.h" /* MC_HISTORY_SHARED_SEARCH */
57 #include "layout.h" /* mc_refresh() */
59 /* Size of the find parameters window */
60 #if HAVE_CHARSET
61 static int FIND_Y = 16;
62 #else
63 static int FIND_Y = 15;
64 #endif
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 */
74 enum {
75 B_STOP = B_USER + 1,
76 B_AGAIN,
77 B_PANELIZE,
78 B_TREE,
79 B_VIEW
82 typedef enum {
83 FIND_CONT = 0,
84 FIND_SUSPEND,
85 FIND_ABORT
86 } FindProgressStatus;
88 /* List of directories to be ignored, separated by ':' */
89 char *find_ignore_dirs = NULL;
91 /* static variables to remember find parameters */
92 static WInput *in_start; /* Start path */
93 static WInput *in_name; /* Filename */
94 static WInput *in_with; /* Text inside filename */
95 static WCheck *file_case_sens_cbox; /* "case sensitive" checkbox */
96 static WCheck *file_pattern_cbox; /* File name is glob or regexp */
97 static WCheck *recursively_cbox;
98 static WCheck *skip_hidden_cbox;
99 static WCheck *content_case_sens_cbox; /* "case sensitive" checkbox */
100 static WCheck *content_regexp_cbox; /* "find regular expression" checkbox */
101 static WCheck *content_first_hit_cbox; /* "First hit" checkbox" */
102 static WCheck *content_whole_words_cbox; /* "whole words" checkbox */
103 #ifdef HAVE_CHARSET
104 static WCheck *file_all_charsets_cbox;
105 static WCheck *content_all_charsets_cbox;
106 #endif
108 static gboolean running = FALSE; /* nice flag */
109 static char *find_pattern = NULL; /* Pattern to search */
110 static char *content_pattern = NULL; /* pattern to search inside files; if
111 content_regexp_flag is true, it contains the
112 regex pattern, else the search string. */
113 static unsigned long matches; /* Number of matches */
114 static gboolean is_start = FALSE; /* Status of the start/stop toggle button */
115 static char *old_dir = NULL;
117 /* Where did we stop */
118 static int resuming;
119 static int last_line;
120 static int last_pos;
122 static Dlg_head *find_dlg; /* The dialog */
123 static WButton *stop_button; /* pointer to the stop button */
124 static WLabel *status_label; /* Finished, Searching etc. */
125 static WLabel *found_num_label; /* Number of found items */
126 static WListbox *find_list; /* Listbox with the file list */
128 /* This keeps track of the directory stack */
129 #if GLIB_CHECK_VERSION (2, 14, 0)
130 static GQueue dir_queue = G_QUEUE_INIT;
131 #else
132 typedef struct dir_stack {
133 char *name;
134 struct dir_stack *prev;
135 } dir_stack;
137 static dir_stack *dir_stack_base = 0;
138 #endif /* GLIB_CHECK_VERSION */
140 static struct {
141 const char* text;
142 int len; /* length including space and brackets */
143 int x;
144 } fbuts [] = {
145 { N_("&Suspend"), 11, 29 },
146 { N_("Con&tinue"), 12, 29 },
147 { N_("&Chdir"), 11, 3 },
148 { N_("&Again"), 9, 17 },
149 { N_("&Quit"), 8, 43 },
150 { N_("Pane&lize"), 12, 3 },
151 { N_("&View - F3"), 13, 20 },
152 { N_("&Edit - F4"), 13, 38 }
155 static char *in_start_dir = INPUT_LAST_TEXT;
157 static mc_search_t *search_file_handle = NULL;
158 static gboolean skip_hidden_flag = FALSE;
159 static gboolean file_pattern_flag = TRUE;
160 static gboolean file_all_charsets_flag = FALSE;
161 static gboolean file_case_sens_flag = TRUE;
162 static gboolean find_recurs_flag = TRUE;
164 static mc_search_t *search_content_handle = NULL;
165 static gboolean content_regexp_flag = FALSE;
166 static gboolean content_all_charsets_flag = FALSE;
167 static gboolean content_case_sens_flag = TRUE;
168 static gboolean content_first_hit_flag = FALSE;
169 static gboolean content_whole_words = FALSE;
171 static inline char *
172 add_to_list (const char *text, void *data)
174 return listbox_add_item (find_list, LISTBOX_APPEND_AT_END, 0, text, data);
177 static inline void
178 stop_idle (void *data)
180 set_idle_proc (data, 0);
183 static inline void
184 status_update (const char *text)
186 label_set_text (status_label, text);
189 static void
190 found_num_update (void)
192 char buffer [BUF_TINY];
193 g_snprintf (buffer, sizeof (buffer), _("Found: %ld"), matches);
194 label_set_text (found_num_label, buffer);
197 static void
198 get_list_info (char **file, char **dir)
200 listbox_get_current (find_list, file, dir);
203 /* check regular expression */
204 static gboolean
205 find_check_regexp (const char *r)
207 mc_search_t *search;
208 gboolean regexp_ok = FALSE;
210 search = mc_search_new (r, -1);
212 if (search != NULL) {
213 search->search_type = MC_SEARCH_T_REGEX;
214 regexp_ok = mc_search_prepare (search);
215 mc_search_free (search);
218 return regexp_ok;
222 * Callback for the parameter dialog.
223 * Validate regex, prevent closing the dialog if it's invalid.
225 static cb_ret_t
226 find_parm_callback (Dlg_head *h, Widget *sender,
227 dlg_msg_t msg, int parm, void *data)
229 switch (msg) {
230 case DLG_VALIDATE:
231 if (h->ret_value != B_ENTER)
232 return MSG_HANDLED;
234 /* check filename regexp */
235 if (!(file_pattern_cbox->state & C_BOOL)
236 && (in_name->buffer[0] != '\0')
237 && !find_check_regexp (in_name->buffer)) {
238 message (D_ERROR, MSG_ERROR, _(" Malformed regular expression "));
239 dlg_select_widget (in_name);
240 h->running = 1; /* Don't stop the dialog */
241 return MSG_HANDLED;
244 /* check content regexp */
245 if ((content_regexp_cbox->state & C_BOOL)
246 && (in_with->buffer[0] != '\0')
247 && !find_check_regexp (in_with->buffer)) {
248 message (D_ERROR, MSG_ERROR, _(" Malformed regular expression "));
249 dlg_select_widget (in_with);
250 h->running = 1; /* Don't stop the dialog */
251 return MSG_HANDLED;
254 return MSG_HANDLED;
256 default:
257 return default_dlg_callback (h, sender, msg, parm, data);
262 * find_parameters: gets information from the user
264 * If the return value is true, then the following holds:
266 * START_DIR and PATTERN are pointers to char * and upon return they
267 * contain the information provided by the user.
269 * CONTENT holds a strdup of the contents specified by the user if he
270 * asked for them or 0 if not (note, this is different from the
271 * behavior for the other two parameters.
274 static int
275 find_parameters (char **start_dir, char **pattern, char **content)
277 int return_value;
279 /* file name */
280 const char *file_case_label = N_("Cas&e sensitive");
281 const char *file_pattern_label = N_("&Using shell patterns");
282 const char *file_recurs_label = N_("&Find recursively");
283 const char *file_skip_hidden_label = N_("S&kip hidden");
284 #ifdef HAVE_CHARSET
285 const char *file_all_charsets_label = N_("&All charsets");
286 #endif
288 /* file content */
289 const char *content_case_label = N_("Case sens&itive");
290 const char *content_regexp_label = N_("Re&gular expression");
291 const char *content_first_hit_label = N_("Fir&st hit");
292 const char *content_whole_words_label = N_("&Whole words");
293 #ifdef HAVE_CHARSET
294 const char *content_all_charsets_label = N_("All cha&rsets");
295 #endif
297 const char *buts[] = { N_("&OK"), N_("&Cancel"), N_("&Tree") };
299 int b0, b1, b2;
301 #ifdef ENABLE_NLS
303 int i = sizeof (buts) / sizeof (buts[0]);
304 while (i-- != 0)
305 buts[i] = _(buts[i]);
307 file_case_label = _(file_case_label);
308 file_pattern_label = _(file_pattern_label);
309 file_recurs_label = _(file_recurs_label);
310 file_skip_hidden_label = _(file_skip_hidden_label);
311 #ifdef HAVE_CHARSET
312 file_all_charsets_label = _(file_all_charsets_label);
313 content_all_charsets_label = _(content_all_charsets_label);
314 #endif
315 content_case_label = _(content_case_label);
316 content_regexp_label = _(content_regexp_label);
317 content_first_hit_label = _(content_first_hit_label);
318 content_whole_words_label = _(content_whole_words_label);
320 #endif /* ENABLE_NLS */
322 b0 = str_term_width1 (buts[0]) + 6; /* default button */
323 b1 = str_term_width1 (buts[1]) + 4;
324 b2 = str_term_width1 (buts[2]) + 4;
326 if (in_start_dir == NULL)
327 in_start_dir = g_strdup (".");
329 find_par_start:
330 find_dlg =
331 create_dlg (0, 0, FIND_Y, FIND_X, dialog_colors,
332 find_parm_callback, "[Find File]", _("Find File"),
333 DLG_CENTER | DLG_REVERSE);
335 add_widget (find_dlg,
336 button_new (FIND_Y - 3, FIND_X * 3/4 - b1/2, B_CANCEL, NORMAL_BUTTON, buts[1], 0));
337 add_widget (find_dlg,
338 button_new (FIND_Y - 3, FIND_X/4 - b0/2, B_ENTER, DEFPUSH_BUTTON, buts[0], 0));
340 #ifdef HAVE_CHARSET
341 content_all_charsets_cbox = check_new (11, FIND_X / 2 + 1,
342 content_all_charsets_flag, content_all_charsets_label);
343 add_widget (find_dlg, content_all_charsets_cbox);
344 #endif
346 content_whole_words_cbox = check_new (10, FIND_X / 2 + 1, content_whole_words, content_whole_words_label);
347 add_widget (find_dlg, content_whole_words_cbox);
349 content_first_hit_cbox = check_new (9, FIND_X / 2 + 1, content_first_hit_flag, content_first_hit_label);
350 add_widget (find_dlg, content_first_hit_cbox);
352 content_regexp_cbox = check_new (8, FIND_X / 2 + 1, content_regexp_flag, content_regexp_label);
353 add_widget (find_dlg, content_regexp_cbox);
355 content_case_sens_cbox = check_new (7, FIND_X / 2 + 1, content_case_sens_flag, content_case_label);
356 add_widget (find_dlg, content_case_sens_cbox);
358 #ifdef HAVE_CHARSET
359 file_all_charsets_cbox = check_new (11, 3,
360 file_all_charsets_flag, file_all_charsets_label);
361 add_widget (find_dlg, file_all_charsets_cbox);
362 #endif
364 skip_hidden_cbox = check_new (10, 3, skip_hidden_flag, file_skip_hidden_label);
365 add_widget (find_dlg, skip_hidden_cbox);
367 recursively_cbox = check_new (9, 3, find_recurs_flag, file_recurs_label);
368 add_widget (find_dlg, recursively_cbox);
370 file_pattern_cbox = check_new (8, 3, file_pattern_flag, file_pattern_label);
371 add_widget (find_dlg, file_pattern_cbox);
373 file_case_sens_cbox = check_new (7, 3, file_case_sens_flag, file_case_label);
374 add_widget (find_dlg, file_case_sens_cbox);
376 in_with = input_new (6, FIND_X / 2 + 1, INPUT_COLOR, FIND_X / 2 - 4, INPUT_LAST_TEXT,
377 MC_HISTORY_SHARED_SEARCH, INPUT_COMPLETE_DEFAULT);
378 add_widget (find_dlg, in_with);
379 add_widget (find_dlg, label_new (5, FIND_X / 2 + 1, _("Content:")));
381 in_name = input_new (6, 3, INPUT_COLOR, FIND_X / 2 - 4, INPUT_LAST_TEXT, "name",
382 INPUT_COMPLETE_DEFAULT);
383 add_widget (find_dlg, in_name);
384 add_widget (find_dlg, label_new (5, 3, _("File name:")));
386 add_widget (find_dlg,
387 button_new (3, FIND_X - b2 - 2, B_TREE, NORMAL_BUTTON, buts[2], 0));
389 in_start = input_new (3, 3, INPUT_COLOR, FIND_X - b2 - 6, in_start_dir, "start",
390 INPUT_COMPLETE_DEFAULT);
391 add_widget (find_dlg, in_start);
392 add_widget (find_dlg, label_new (2, 3, _("Start at:")));
394 dlg_select_widget (in_name);
396 switch (run_dlg (find_dlg)) {
397 case B_CANCEL:
398 return_value = 0;
399 break;
401 case B_TREE:
403 char temp_dir[MC_MAXPATHLEN];
404 g_strlcpy (temp_dir, in_start->buffer, sizeof (temp_dir));
405 #ifdef HAVE_CHARSET
406 file_all_charsets_flag = file_all_charsets_cbox->state & C_BOOL;
407 content_all_charsets_flag = content_all_charsets_cbox->state & C_BOOL;
408 #endif
409 content_case_sens_flag = content_case_sens_cbox->state & C_BOOL;
410 content_regexp_flag = content_regexp_cbox->state & C_BOOL;
411 content_first_hit_flag = content_first_hit_cbox->state & C_BOOL;
412 content_whole_words = content_whole_words_cbox->state & C_BOOL;
413 file_pattern_flag = file_pattern_cbox->state & C_BOOL;
414 file_case_sens_flag = file_case_sens_cbox->state & C_BOOL;
415 find_recurs_flag = recursively_cbox->state & C_BOOL;
416 skip_hidden_flag = skip_hidden_cbox->state & C_BOOL;
417 destroy_dlg (find_dlg);
419 if ((temp_dir[0] == '\0')
420 || ((temp_dir[0] == '.') && (temp_dir[1] == '\0')))
421 g_strlcpy (temp_dir, current_panel->cwd, sizeof (temp_dir));
423 if (in_start_dir != INPUT_LAST_TEXT)
424 g_free (in_start_dir);
425 in_start_dir = tree_box (temp_dir);
426 /* Warning: Dreadful goto */
427 goto find_par_start;
428 break;
430 default:
431 #ifdef HAVE_CHARSET
432 file_all_charsets_flag = file_all_charsets_cbox->state & C_BOOL;
433 content_all_charsets_flag = content_all_charsets_cbox->state & C_BOOL;
434 #endif
435 content_case_sens_flag = content_case_sens_cbox->state & C_BOOL;
436 content_regexp_flag = content_regexp_cbox->state & C_BOOL;
437 content_first_hit_flag = content_first_hit_cbox->state & C_BOOL;
438 content_whole_words = content_whole_words_cbox->state & C_BOOL;
439 find_recurs_flag = recursively_cbox->state & C_BOOL;
440 file_pattern_flag = file_pattern_cbox->state & C_BOOL;
441 file_case_sens_flag = file_case_sens_cbox->state & C_BOOL;
442 skip_hidden_flag = skip_hidden_cbox->state & C_BOOL;
444 *content = (in_with->buffer[0] != '\0') ? g_strdup (in_with->buffer) : NULL;
445 *start_dir = g_strdup ((in_start->buffer[0] != '\0') ? in_start->buffer : ".");
446 *pattern = g_strdup (in_name->buffer);
447 if (in_start_dir != INPUT_LAST_TEXT)
448 g_free (in_start_dir);
449 in_start_dir = g_strdup (*start_dir);
450 return_value = 1;
453 destroy_dlg (find_dlg);
455 return return_value;
458 #if GLIB_CHECK_VERSION (2, 14, 0)
460 static inline void
461 push_directory (const char *dir)
463 g_queue_push_head (&dir_queue, (void *) dir);
466 static inline char *
467 pop_directory (void)
469 return (char *) g_queue_pop_tail (&dir_queue);
472 /* Remove all the items in the stack */
473 static void
474 clear_stack (void)
476 g_queue_foreach (&dir_queue, (GFunc) g_free, NULL);
477 g_queue_clear (&dir_queue);
480 #else /* GLIB_CHAECK_VERSION */
482 static void
483 push_directory (const char *dir)
485 dir_stack *new;
487 new = g_new (dir_stack, 1);
488 new->name = str_unconst (dir);
489 new->prev = dir_stack_base;
490 dir_stack_base = new;
493 static char *
494 pop_directory (void)
496 char *name = NULL;
498 if (dir_stack_base != NULL) {
499 dir_stack *next;
500 name = dir_stack_base->name;
501 next = dir_stack_base->prev;
502 g_free (dir_stack_base);
503 dir_stack_base = next;
506 return name;
509 /* Remove all the items in the stack */
510 static void
511 clear_stack (void)
513 char *dir = NULL;
514 while ((dir = pop_directory ()) != NULL)
515 g_free (dir);
518 #endif /* GLIB_CHAECK_VERSION */
520 static void
521 insert_file (const char *dir, const char *file)
523 char *tmp_name = NULL;
524 static char *dirname = NULL;
526 while (dir [0] == PATH_SEP && dir [1] == PATH_SEP)
527 dir++;
529 if (old_dir){
530 if (strcmp (old_dir, dir)){
531 g_free (old_dir);
532 old_dir = g_strdup (dir);
533 dirname = add_to_list (dir, NULL);
535 } else {
536 old_dir = g_strdup (dir);
537 dirname = add_to_list (dir, NULL);
540 tmp_name = g_strdup_printf (" %s", file);
541 add_to_list (tmp_name, dirname);
542 g_free (tmp_name);
545 static void
546 find_add_match (const char *dir, const char *file)
548 insert_file (dir, file);
550 /* Don't scroll */
551 if (matches == 0)
552 listbox_select_by_number (find_list, 0);
553 send_message (&find_list->widget, WIDGET_DRAW, 0);
555 matches++;
556 found_num_update ();
560 * get_line_at:
562 * Returns malloced null-terminated line from file file_fd.
563 * Input is buffered in buf_size long buffer.
564 * Current pos in buf is stored in pos.
565 * n_read - number of read chars.
566 * has_newline - is there newline ?
568 static char *
569 get_line_at (int file_fd, char *buf, int buf_size, int *pos, int *n_read,
570 gboolean *has_newline)
572 char *buffer = NULL;
573 int buffer_size = 0;
574 char ch = 0;
575 int i = 0;
577 for (;;) {
578 if (*pos >= *n_read) {
579 *pos = 0;
580 *n_read = mc_read (file_fd, buf, buf_size);
581 if (*n_read <= 0)
582 break;
585 ch = buf[(*pos)++];
586 if (ch == '\0') {
587 /* skip possible leading zero(s) */
588 if (i == 0)
589 continue;
590 else
591 break;
594 if (i >= buffer_size - 1) {
595 buffer = g_realloc (buffer, buffer_size += 80);
597 /* Strip newline */
598 if (ch == '\n')
599 break;
601 buffer[i++] = ch;
604 *has_newline = (ch != '\0');
606 if (buffer != NULL)
607 buffer[i] = '\0';
609 return buffer;
612 static FindProgressStatus
613 check_find_events(Dlg_head *h)
615 Gpm_Event event;
616 int c;
618 event.x = -1;
619 c = tty_get_event (&event, h->mouse_status == MOU_REPEAT, FALSE);
620 if (c != EV_NONE) {
621 dlg_process_event (h, c, &event);
622 if (h->ret_value == B_ENTER
623 || h->ret_value == B_CANCEL
624 || h->ret_value == B_AGAIN
625 || h->ret_value == B_PANELIZE) {
626 /* dialog terminated */
627 return FIND_ABORT;
629 if (!(h->flags & DLG_WANT_IDLE)) {
630 /* searching suspended */
631 return FIND_SUSPEND;
635 return FIND_CONT;
639 * search_content:
641 * Search the content_pattern string in the DIRECTORY/FILE.
642 * It will add the found entries to the find listbox.
644 * returns FALSE if do_search should look for another file
645 * TRUE if do_search should exit and proceed to the event handler
647 static gboolean
648 search_content (Dlg_head *h, const char *directory, const char *filename)
650 struct stat s;
651 char buffer [BUF_4K];
652 char *fname = NULL;
653 int file_fd;
654 gboolean ret_val = FALSE;
656 fname = concat_dir_and_file (directory, filename);
658 if (mc_stat (fname, &s) != 0 || !S_ISREG (s.st_mode)){
659 g_free (fname);
660 return 0;
663 file_fd = mc_open (fname, O_RDONLY);
664 g_free (fname);
666 if (file_fd == -1)
667 return 0;
669 g_snprintf (buffer, sizeof (buffer), _("Grepping in %s"), str_trunc (filename, FIND2_X_USE));
671 status_update (buffer);
672 mc_refresh ();
674 tty_enable_interrupt_key ();
675 tty_got_interrupt ();
678 int line = 1;
679 int pos = 0;
680 int n_read = 0;
681 gboolean has_newline;
682 char *p = NULL;
683 gboolean found = FALSE;
684 gsize found_len;
685 char result [BUF_MEDIUM];
687 if (resuming) {
688 /* We've been previously suspended, start from the previous position */
689 resuming = 0;
690 line = last_line;
691 pos = last_pos;
693 while (!ret_val
694 && (p = get_line_at (file_fd, buffer, sizeof (buffer),
695 &pos, &n_read, &has_newline)) != NULL) {
696 if (!found /* Search in binary line once */
697 && mc_search_run (search_content_handle,
698 (const void *) p, 0, strlen (p), &found_len)) {
699 g_snprintf (result, sizeof (result), "%d:%s", line, filename);
700 find_add_match (directory, result);
701 found = TRUE;
703 g_free (p);
705 if (found && content_first_hit_flag)
706 break;
708 if (has_newline) {
709 found = FALSE;
710 line++;
713 if ((line & 0xff) == 0) {
714 FindProgressStatus res;
715 res = check_find_events(h);
716 switch (res) {
717 case FIND_ABORT:
718 stop_idle (h);
719 ret_val = TRUE;
720 break;
721 case FIND_SUSPEND:
722 resuming = 1;
723 last_line = line;
724 last_pos = pos;
725 ret_val = TRUE;
726 break;
727 default:
728 break;
734 tty_disable_interrupt_key ();
735 mc_close (file_fd);
736 return ret_val;
739 static int
740 do_search (struct Dlg_head *h)
742 static struct dirent *dp = NULL;
743 static DIR *dirp = NULL;
744 static char *directory = NULL;
745 struct stat tmp_stat;
746 static int pos = 0;
747 static int subdirs_left = 0;
748 gsize bytes_found;
749 unsigned long count; /* Number of files displayed */
751 if (!h) { /* someone forces me to close dirp */
752 if (dirp) {
753 mc_closedir (dirp);
754 dirp = NULL;
756 g_free (directory);
757 directory = NULL;
758 dp = NULL;
759 return 1;
762 search_content_handle = mc_search_new(content_pattern, -1);
763 if (search_content_handle) {
764 search_content_handle->search_type = (content_regexp_flag) ? MC_SEARCH_T_REGEX : MC_SEARCH_T_NORMAL;
765 search_content_handle->is_case_sentitive = content_case_sens_flag;
766 search_content_handle->whole_words = content_whole_words;
767 search_content_handle->is_all_charsets = content_all_charsets_flag;
769 search_file_handle = mc_search_new(find_pattern, -1);
770 search_file_handle->search_type = (file_pattern_flag) ? MC_SEARCH_T_GLOB : MC_SEARCH_T_REGEX;
771 search_file_handle->is_case_sentitive = file_case_sens_flag;
772 search_file_handle->is_all_charsets = file_all_charsets_flag;
773 search_file_handle->is_entire_line = file_pattern_flag;
775 count = 0;
777 do_search_begin:
778 while (!dp){
779 if (dirp){
780 mc_closedir (dirp);
781 dirp = 0;
784 while (!dirp){
785 char *tmp = NULL;
787 tty_setcolor (REVERSE_COLOR);
788 while (1) {
789 char *temp_dir = NULL;
790 gboolean found;
792 tmp = pop_directory ();
793 if (tmp == NULL) {
794 running = FALSE;
795 status_update (_("Finished"));
796 stop_idle (h);
797 mc_search_free (search_file_handle);
798 search_file_handle = NULL;
799 mc_search_free (search_content_handle);
800 search_content_handle = NULL;
801 return 0;
804 if ((find_ignore_dirs == NULL) || (find_ignore_dirs[0] == '\0'))
805 break;
807 temp_dir = g_strdup_printf (":%s:", tmp);
808 found = strstr (find_ignore_dirs, temp_dir) != 0;
809 g_free (temp_dir);
811 if (!found)
812 break;
814 g_free (tmp);
817 g_free (directory);
818 directory = tmp;
820 if (verbose){
821 char buffer [BUF_SMALL];
823 g_snprintf (buffer, sizeof (buffer), _("Searching %s"),
824 str_trunc (directory, FIND2_X_USE));
825 status_update (buffer);
827 /* mc_stat should not be called after mc_opendir
828 because vfs_s_opendir modifies the st_nlink
830 if (!mc_stat (directory, &tmp_stat))
831 subdirs_left = tmp_stat.st_nlink - 2;
832 else
833 subdirs_left = 0;
835 dirp = mc_opendir (directory);
836 } /* while (!dirp) */
838 /* skip invalid filenames */
839 while ((dp = mc_readdir (dirp)) != NULL
840 && !str_is_valid_string (dp->d_name))
842 } /* while (!dp) */
844 if (strcmp (dp->d_name, ".") == 0 ||
845 strcmp (dp->d_name, "..") == 0){
846 dp = mc_readdir (dirp);
847 /* skip invalid filenames */
848 while (dp != NULL && !str_is_valid_string (dp->d_name))
849 dp = mc_readdir (dirp);
851 mc_search_free(search_file_handle);
852 search_file_handle = NULL;
853 mc_search_free(search_content_handle);
854 search_content_handle = NULL;
855 return 1;
858 if (!(skip_hidden_flag && (dp->d_name[0] == '.'))) {
859 gboolean search_ok;
861 if ((subdirs_left != 0) && find_recurs_flag
862 && (directory != NULL)) { /* Can directory be NULL ? */
863 char *tmp_name = concat_dir_and_file (directory, dp->d_name);
864 if (!mc_lstat (tmp_name, &tmp_stat)
865 && S_ISDIR (tmp_stat.st_mode)) {
866 push_directory (tmp_name);
867 subdirs_left--;
868 } else
869 g_free (tmp_name);
872 search_ok = mc_search_run (search_file_handle, dp->d_name,
873 0, strlen (dp->d_name), &bytes_found);
875 if (search_ok) {
876 if (content_pattern == NULL)
877 find_add_match (directory, dp->d_name);
878 else if (search_content (h, directory, dp->d_name)) {
879 mc_search_free(search_file_handle);
880 search_file_handle = NULL;
881 mc_search_free(search_content_handle);
882 search_content_handle = NULL;
883 return 1;
888 /* skip invalid filenames */
889 while ((dp = mc_readdir (dirp)) != NULL
890 && !str_is_valid_string (dp->d_name))
893 /* Displays the nice dot */
894 count++;
895 if (!(count & 31)){
896 /* For nice updating */
897 const char rotating_dash[] = "|/-\\";
899 if (verbose){
900 pos = (pos + 1) % 4;
901 tty_setcolor (DLG_NORMALC (h));
902 dlg_move (h, FIND2_Y - 7, FIND2_X - 4);
903 tty_print_char (rotating_dash [pos]);
904 mc_refresh ();
906 } else
907 goto do_search_begin;
909 mc_search_free (search_file_handle);
910 search_file_handle = NULL;
911 mc_search_free (search_content_handle);
912 search_content_handle = NULL;
913 return 1;
916 static void
917 init_find_vars (void)
919 g_free (old_dir);
920 old_dir = NULL;
921 matches = 0;
923 /* Remove all the items in the stack */
924 clear_stack ();
927 static char *
928 make_fullname (const char *dirname, const char *filename)
931 if (strcmp(dirname, ".") == 0 || strcmp(dirname, "."PATH_SEP_STR) == 0)
932 return g_strdup (filename);
933 if (strncmp(dirname, "."PATH_SEP_STR, 2) == 0)
934 return concat_dir_and_file (dirname + 2, filename);
935 return concat_dir_and_file (dirname, filename);
938 static void
939 find_do_view_edit (int unparsed_view, int edit, char *dir, char *file)
941 char *fullname = NULL;
942 const char *filename = NULL;
943 int line;
945 if (content_pattern != NULL) {
946 filename = strchr (file + 4, ':') + 1;
947 line = atoi (file + 4);
948 } else {
949 filename = file + 4;
950 line = 0;
953 fullname = make_fullname (dir, filename);
954 if (edit)
955 do_edit_at_line (fullname, line);
956 else
957 view_file_at_line (fullname, unparsed_view, use_internal_view, line);
958 g_free (fullname);
961 static cb_ret_t
962 view_edit_currently_selected_file (int unparsed_view, int edit)
964 WLEntry *entry = find_list->current;
965 char *dir = NULL;
967 if (!entry)
968 return MSG_NOT_HANDLED;
970 dir = entry->data;
972 if (!entry->text || !dir)
973 return MSG_NOT_HANDLED;
975 find_do_view_edit (unparsed_view, edit, dir, entry->text);
976 return MSG_HANDLED;
979 static cb_ret_t
980 find_callback (struct Dlg_head *h, Widget *sender,
981 dlg_msg_t msg, int parm, void *data)
983 switch (msg) {
984 case DLG_KEY:
985 if (parm == KEY_F (3) || parm == KEY_F (13)) {
986 int unparsed_view = (parm == KEY_F (13));
987 return view_edit_currently_selected_file (unparsed_view, 0);
989 if (parm == KEY_F (4)) {
990 return view_edit_currently_selected_file (0, 1);
992 return MSG_NOT_HANDLED;
994 case DLG_IDLE:
995 do_search (h);
996 return MSG_HANDLED;
998 default:
999 return default_dlg_callback (h, sender, msg, parm, data);
1003 /* Handles the Stop/Start button in the find window */
1004 static int
1005 start_stop (int button)
1007 (void) button;
1009 running = is_start;
1010 set_idle_proc (find_dlg, running);
1011 is_start = !is_start;
1013 status_update (is_start ? _("Stopped") : _("Searching"));
1014 button_set_text (stop_button, fbuts [is_start ? 1 : 0].text);
1016 return 0;
1019 /* Handle view command, when invoked as a button */
1020 static int
1021 find_do_view_file (int button)
1023 (void) button;
1025 view_edit_currently_selected_file (0, 0);
1026 return 0;
1029 /* Handle edit command, when invoked as a button */
1030 static int
1031 find_do_edit_file (int button)
1033 (void) button;
1035 view_edit_currently_selected_file (0, 1);
1036 return 0;
1039 static void
1040 setup_gui (void)
1042 #ifdef ENABLE_NLS
1043 static gboolean i18n_flag = FALSE;
1045 if (!i18n_flag) {
1046 int i = sizeof (fbuts) / sizeof (fbuts[0]);
1047 while (i-- != 0) {
1048 fbuts[i].text = _(fbuts[i].text);
1049 fbuts[i].len = str_term_width1 (fbuts[i].text) + 3;
1052 fbuts[2].len += 2; /* DEFPUSH_BUTTON */
1053 i18n_flag = TRUE;
1055 #endif /* ENABLE_NLS */
1058 * Dynamically place buttons centered within current window size
1061 int l0 = max (fbuts[0].len, fbuts[1].len);
1062 int l1 = fbuts[2].len + fbuts[3].len + l0 + fbuts[4].len;
1063 int l2 = fbuts[5].len + fbuts[6].len + fbuts[7].len;
1064 int r1, r2;
1066 /* Check, if both button rows fit within FIND2_X */
1067 FIND2_X = max (l1 + 9, COLS - 16);
1068 FIND2_X = max (l2 + 8, FIND2_X);
1070 /* compute amount of space between buttons for each row */
1071 r1 = (FIND2_X - 4 - l1) % 5;
1072 l1 = (FIND2_X - 4 - l1) / 5;
1073 r2 = (FIND2_X - 4 - l2) % 4;
1074 l2 = (FIND2_X - 4 - l2) / 4;
1076 /* ...and finally, place buttons */
1077 fbuts[2].x = 2 + r1 / 2 + l1;
1078 fbuts[3].x = fbuts[2].x + fbuts[2].len + l1;
1079 fbuts[0].x = fbuts[3].x + fbuts[3].len + l1;
1080 fbuts[4].x = fbuts[0].x + l0 + l1;
1081 fbuts[5].x = 2 + r2 / 2 + l2;
1082 fbuts[6].x = fbuts[5].x + fbuts[5].len + l2;
1083 fbuts[7].x = fbuts[6].x + fbuts[6].len + l2;
1086 find_dlg =
1087 create_dlg (0, 0, FIND2_Y, FIND2_X, dialog_colors, find_callback,
1088 "[Find File]", _("Find File"), DLG_CENTER | DLG_REVERSE);
1090 add_widget (find_dlg,
1091 button_new (FIND2_Y - 3, fbuts[7].x, B_VIEW, NORMAL_BUTTON,
1092 fbuts[7].text, find_do_edit_file));
1093 add_widget (find_dlg,
1094 button_new (FIND2_Y - 3, fbuts[6].x, B_VIEW, NORMAL_BUTTON,
1095 fbuts[6].text, find_do_view_file));
1096 add_widget (find_dlg,
1097 button_new (FIND2_Y - 3, fbuts[5].x, B_PANELIZE,
1098 NORMAL_BUTTON, fbuts[5].text, 0));
1100 add_widget (find_dlg,
1101 button_new (FIND2_Y - 4, fbuts[4].x, B_CANCEL,
1102 NORMAL_BUTTON, fbuts[4].text, 0));
1103 stop_button =
1104 button_new (FIND2_Y - 4, fbuts[0].x, B_STOP, NORMAL_BUTTON,
1105 fbuts[0].text, start_stop);
1106 add_widget (find_dlg, stop_button);
1107 add_widget (find_dlg,
1108 button_new (FIND2_Y - 4, fbuts[3].x, B_AGAIN,
1109 NORMAL_BUTTON, fbuts[3].text, 0));
1110 add_widget (find_dlg,
1111 button_new (FIND2_Y - 4, fbuts[2].x, B_ENTER,
1112 DEFPUSH_BUTTON, fbuts[2].text, 0));
1114 status_label = label_new (FIND2_Y - 7, 4, _("Searching"));
1115 add_widget (find_dlg, status_label);
1117 found_num_label = label_new (FIND2_Y - 6, 4, "");
1118 add_widget (find_dlg, found_num_label);
1120 find_list =
1121 listbox_new (2, 2, FIND2_Y - 10, FIND2_X - 4, NULL);
1122 add_widget (find_dlg, find_list);
1125 static int
1126 run_process (void)
1128 resuming = 0;
1129 set_idle_proc (find_dlg, 1);
1130 return run_dlg (find_dlg);
1133 static void
1134 kill_gui (void)
1136 set_idle_proc (find_dlg, 0);
1137 destroy_dlg (find_dlg);
1140 static int
1141 find_file (const char *start_dir, const char *pattern, const char *content,
1142 char **dirname, char **filename)
1144 int return_value = 0;
1145 char *dir_tmp = NULL, *file_tmp = NULL;
1147 setup_gui ();
1149 /* FIXME: Need to cleanup this, this ought to be passed non-globaly */
1150 find_pattern = str_unconst (pattern);
1151 content_pattern = (content != NULL && str_is_valid_string (content))
1152 ? g_strdup(content)
1153 : NULL;
1155 init_find_vars ();
1156 push_directory (start_dir);
1158 return_value = run_process ();
1160 /* Remove all the items in the stack */
1161 clear_stack ();
1163 get_list_info (&file_tmp, &dir_tmp);
1165 if (dir_tmp)
1166 *dirname = g_strdup (dir_tmp);
1167 if (file_tmp)
1168 *filename = g_strdup (file_tmp);
1170 if (return_value == B_PANELIZE && *filename) {
1171 int status, link_to_dir, stale_link;
1172 int next_free = 0;
1173 int i;
1174 struct stat st;
1175 WLEntry *entry = find_list->list;
1176 dir_list *list = &current_panel->dir;
1177 char *name = NULL;
1179 for (i = 0; entry != NULL && i < find_list->count;
1180 entry = entry->next, i++) {
1181 const char *lc_filename = NULL;
1183 if (!entry->text || !entry->data)
1184 continue;
1186 if (content_pattern != NULL)
1187 lc_filename = strchr (entry->text + 4, ':') + 1;
1188 else
1189 lc_filename = entry->text + 4;
1191 name = make_fullname (entry->data, lc_filename);
1192 status =
1193 handle_path (list, name, &st, next_free, &link_to_dir,
1194 &stale_link);
1195 if (status == 0) {
1196 g_free (name);
1197 continue;
1199 if (status == -1) {
1200 g_free (name);
1201 break;
1204 /* don't add files more than once to the panel */
1205 if (content_pattern != NULL && next_free > 0
1206 && strcmp (list->list[next_free - 1].fname, name) == 0) {
1207 g_free (name);
1208 continue;
1211 if (!next_free) /* first turn i.e clean old list */
1212 panel_clean_dir (current_panel);
1213 list->list[next_free].fnamelen = strlen (name);
1214 list->list[next_free].fname = name;
1215 list->list[next_free].f.marked = 0;
1216 list->list[next_free].f.link_to_dir = link_to_dir;
1217 list->list[next_free].f.stale_link = stale_link;
1218 list->list[next_free].f.dir_size_computed = 0;
1219 list->list[next_free].st = st;
1220 list->list[next_free].sort_key = NULL;
1221 list->list[next_free].second_sort_key = NULL;
1222 next_free++;
1223 if (!(next_free & 15))
1224 rotate_dash ();
1227 if (next_free) {
1228 current_panel->count = next_free;
1229 current_panel->is_panelized = 1;
1231 if (start_dir[0] == PATH_SEP) {
1232 strcpy (current_panel->cwd, PATH_SEP_STR);
1233 chdir (PATH_SEP_STR);
1238 g_free (content_pattern);
1239 kill_gui ();
1240 do_search (NULL); /* force do_search to release resources */
1241 g_free (old_dir);
1242 old_dir = NULL;
1244 return return_value;
1247 void
1248 do_find (void)
1250 char *start_dir = NULL, *pattern = NULL, *content = NULL;
1251 char *filename = NULL, *dirname = NULL;
1252 int v;
1253 gboolean dir_and_file_set;
1255 while (find_parameters (&start_dir, &pattern, &content)){
1256 if (pattern [0] == '\0')
1257 break; /* nothing search*/
1259 dirname = filename = NULL;
1260 is_start = FALSE;
1261 v = find_file (start_dir, pattern, content, &dirname, &filename);
1262 g_free (pattern);
1264 if (v == B_ENTER){
1265 if (dirname || filename){
1266 if (dirname){
1267 do_cd (dirname, cd_exact);
1268 if (filename)
1269 try_to_select (current_panel, filename + (content ?
1270 (strchr (filename + 4, ':') - filename + 1) : 4) );
1271 } else if (filename)
1272 do_cd (filename, cd_exact);
1273 select_item (current_panel);
1275 g_free (dirname);
1276 g_free (filename);
1277 break;
1280 g_free (content);
1281 dir_and_file_set = dirname && filename;
1282 g_free (dirname);
1283 g_free (filename);
1285 if (v == B_CANCEL)
1286 break;
1288 if (v == B_PANELIZE){
1289 if (dir_and_file_set){
1290 try_to_select (current_panel, NULL);
1291 panel_re_sort (current_panel);
1292 try_to_select (current_panel, NULL);
1294 break;