Ticket #1662: keep empty 'Content:' field in 'Find File' dialog.
[pantumic.git] / src / find.c
blob8955dc7dd600f1095bee13977f3c4f3fe2f7cc58
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 "global.h"
37 #include "../src/tty/tty.h"
38 #include "../src/skin/skin.h"
39 #include "../src/tty/key.h"
41 #include "../src/search/search.h"
43 #include "setup.h"
44 #include "find.h"
45 #include "strutil.h"
46 #include "dialog.h"
47 #include "widget.h"
48 #include "dir.h"
49 #include "panel.h" /* current_panel */
50 #include "main.h" /* do_cd, try_to_select */
51 #include "wtools.h"
52 #include "cmd.h" /* view_file_at_line */
53 #include "boxes.h"
54 #include "history.h" /* MC_HISTORY_SHARED_SEARCH */
55 #include "layout.h" /* mc_refresh() */
57 /* Size of the find parameters window */
58 #if HAVE_CHARSET
59 static int FIND_Y = 16;
60 #else
61 static int FIND_Y = 15;
62 #endif
63 static int FIND_X = 68;
65 /* Size of the find window */
66 #define FIND2_Y (LINES - 4)
68 static int FIND2_X = 64;
69 #define FIND2_X_USE (FIND2_X - 20)
71 /* A couple of extra messages we need */
72 enum {
73 B_STOP = B_USER + 1,
74 B_AGAIN,
75 B_PANELIZE,
76 B_TREE,
77 B_VIEW
80 typedef enum {
81 FIND_CONT = 0,
82 FIND_SUSPEND,
83 FIND_ABORT
84 } FindProgressStatus;
86 /* List of directories to be ignored, separated by ':' */
87 char *find_ignore_dirs = NULL;
89 /* static variables to remember find parameters */
90 static WInput *in_start; /* Start path */
91 static WInput *in_name; /* Filename */
92 static WInput *in_with; /* Text inside filename */
93 static WCheck *file_case_sens_cbox; /* "case sensitive" checkbox */
94 static WCheck *file_pattern_cbox; /* File name is glob or regexp */
95 static WCheck *recursively_cbox;
96 static WCheck *skip_hidden_cbox;
97 static WCheck *content_case_sens_cbox; /* "case sensitive" checkbox */
98 static WCheck *content_regexp_cbox; /* "find regular expression" checkbox */
99 static WCheck *content_first_hit_cbox; /* "First hit" checkbox" */
100 static WCheck *content_whole_words_cbox; /* "whole words" checkbox */
101 #ifdef HAVE_CHARSET
102 static WCheck *file_all_charsets_cbox;
103 static WCheck *content_all_charsets_cbox;
104 #endif
106 static gboolean running = FALSE; /* nice flag */
107 static char *find_pattern = NULL; /* Pattern to search */
108 static char *content_pattern = NULL; /* pattern to search inside files; if
109 content_regexp_flag is true, it contains the
110 regex pattern, else the search string. */
111 static unsigned long matches; /* Number of matches */
112 static gboolean is_start = FALSE; /* Status of the start/stop toggle button */
113 static char *old_dir = NULL;
115 /* Where did we stop */
116 static int resuming;
117 static int last_line;
118 static int last_pos;
120 static Dlg_head *find_dlg; /* The dialog */
121 static WButton *stop_button; /* pointer to the stop button */
122 static WLabel *status_label; /* Finished, Searching etc. */
123 static WLabel *found_num_label; /* Number of found items */
124 static WListbox *find_list; /* Listbox with the file list */
126 /* This keeps track of the directory stack */
127 #if GLIB_CHECK_VERSION (2, 14, 0)
128 static GQueue dir_queue = G_QUEUE_INIT;
129 #else
130 typedef struct dir_stack {
131 char *name;
132 struct dir_stack *prev;
133 } dir_stack;
135 static dir_stack *dir_stack_base = 0;
136 #endif /* GLIB_CHECK_VERSION */
138 static struct {
139 const char* text;
140 int len; /* length including space and brackets */
141 int x;
142 } fbuts [] = {
143 { N_("&Suspend"), 11, 29 },
144 { N_("Con&tinue"), 12, 29 },
145 { N_("&Chdir"), 11, 3 },
146 { N_("&Again"), 9, 17 },
147 { N_("&Quit"), 8, 43 },
148 { N_("Pane&lize"), 12, 3 },
149 { N_("&View - F3"), 13, 20 },
150 { N_("&Edit - F4"), 13, 38 }
153 static char *in_contents = NULL;
154 static char *in_start_dir = INPUT_LAST_TEXT;
156 static mc_search_t *search_file_handle = NULL;
157 static gboolean skip_hidden_flag = FALSE;
158 static gboolean file_pattern_flag = TRUE;
159 static gboolean file_all_charsets_flag = FALSE;
160 static gboolean file_case_sens_flag = TRUE;
161 static gboolean find_recurs_flag = TRUE;
163 static mc_search_t *search_content_handle = NULL;
164 static gboolean content_regexp_flag = FALSE;
165 static gboolean content_all_charsets_flag = FALSE;
166 static gboolean content_case_sens_flag = TRUE;
167 static gboolean content_first_hit_flag = FALSE;
168 static gboolean content_whole_words = FALSE;
170 static inline char *
171 add_to_list (const char *text, void *data)
173 return listbox_add_item (find_list, LISTBOX_APPEND_AT_END, 0, text, data);
176 static inline void
177 stop_idle (void *data)
179 set_idle_proc (data, 0);
182 static inline void
183 status_update (const char *text)
185 label_set_text (status_label, text);
188 static void
189 found_num_update (void)
191 char buffer [BUF_TINY];
192 g_snprintf (buffer, sizeof (buffer), _("Found: %ld"), matches);
193 label_set_text (found_num_label, buffer);
196 static void
197 get_list_info (char **file, char **dir)
199 listbox_get_current (find_list, file, dir);
202 /* check regular expression */
203 gboolean
204 find_check_regexp (const char *r)
206 mc_search_t *search;
207 gboolean regexp_ok = FALSE;
209 search = mc_search_new (r, -1);
211 if (search != NULL) {
212 search->search_type = MC_SEARCH_T_REGEX;
213 regexp_ok = mc_search_prepare (search);
214 mc_search_free (search);
217 return regexp_ok;
221 * Callback for the parameter dialog.
222 * Validate regex, prevent closing the dialog if it's invalid.
224 static cb_ret_t
225 find_parm_callback (struct Dlg_head *h, dlg_msg_t msg, int parm)
227 switch (msg) {
228 case DLG_VALIDATE:
229 if (h->ret_value != B_ENTER)
230 return MSG_HANDLED;
232 /* check filename regexp */
233 if (!(file_pattern_cbox->state & C_BOOL)
234 && (in_name->buffer[0] != '\0')
235 && !find_check_regexp (in_name->buffer)) {
236 message (D_ERROR, MSG_ERROR, _(" Malformed regular expression "));
237 dlg_select_widget (in_name);
238 h->running = 1; /* Don't stop the dialog */
239 return MSG_HANDLED;
242 /* check content regexp */
243 if ((content_regexp_cbox->state & C_BOOL)
244 && (in_with->buffer[0] != '\0')
245 && !find_check_regexp (in_with->buffer)) {
246 message (D_ERROR, MSG_ERROR, _(" Malformed regular expression "));
247 dlg_select_widget (in_with);
248 h->running = 1; /* Don't stop the dialog */
249 return MSG_HANDLED;
252 return MSG_HANDLED;
254 default:
255 return default_dlg_callback (h, msg, parm);
260 * find_parameters: gets information from the user
262 * If the return value is true, then the following holds:
264 * START_DIR and PATTERN are pointers to char * and upon return they
265 * contain the information provided by the user.
267 * CONTENT holds a strdup of the contents specified by the user if he
268 * asked for them or 0 if not (note, this is different from the
269 * behavior for the other two parameters.
272 static int
273 find_parameters (char **start_dir, char **pattern, char **content)
275 int return_value;
276 char *temp_dir = NULL;
278 /* file name */
279 const char *file_case_label = N_("Cas&e sensitive");
280 const char *file_pattern_label = N_("&Using shell patterns");
281 const char *file_recurs_label = N_("&Find recursively");
282 const char *file_skip_hidden_label = N_("S&kip hidden");
283 const char *file_all_charsets_label = N_("&All charsets");
285 /* file content */
286 const char *content_case_label = N_("Case sens&itive");
287 const char *content_regexp_label = N_("Re&gular expression");
288 const char *content_first_hit_label = N_("Fir&st hit");
289 const char *content_whole_words_label = N_("&Whole words");
290 const char *content_all_charsets_label = N_("All cha&rsets");
292 const char *buts[] = { N_("&OK"), N_("&Cancel"), N_("&Tree") };
294 int b0, b1, b2;
296 #ifdef ENABLE_NLS
298 int i = sizeof (buts) / sizeof (buts[0]);
299 while (i-- != 0)
300 buts[i] = _(buts[i]);
302 file_case_label = _(file_case_label);
303 file_pattern_label = _(file_pattern_label);
304 file_recurs_label = _(file_recurs_label);
305 file_skip_hidden_label = _(file_skip_hidden_label);
306 file_all_charsets_label = _(file_all_charsets_label);
307 content_case_label = _(content_case_label);
308 content_regexp_label = _(content_regexp_label);
309 content_first_hit_label = _(content_first_hit_label);
310 content_whole_words_label = _(content_whole_words_label);
311 content_all_charsets_label = _(content_all_charsets_label);
313 #endif /* ENABLE_NLS */
315 b0 = str_term_width1 (buts[0]) + 6; /* default button */
316 b1 = str_term_width1 (buts[1]) + 4;
317 b2 = str_term_width1 (buts[2]) + 4;
319 find_par_start:
320 if (in_contents == NULL)
321 in_contents = INPUT_LAST_TEXT;
323 if (in_start_dir == NULL)
324 in_start_dir = g_strdup (".");
326 find_dlg =
327 create_dlg (0, 0, FIND_Y, FIND_X, dialog_colors,
328 find_parm_callback, "[Find File]", _("Find File"),
329 DLG_CENTER | DLG_REVERSE);
331 add_widget (find_dlg,
332 button_new (FIND_Y - 3, FIND_X * 3/4 - b1/2, B_CANCEL, NORMAL_BUTTON, buts[1], 0));
333 add_widget (find_dlg,
334 button_new (FIND_Y - 3, FIND_X/4 - b0/2, B_ENTER, DEFPUSH_BUTTON, buts[0], 0));
336 #ifdef HAVE_CHARSET
337 content_all_charsets_cbox = check_new (11, FIND_X / 2 + 1,
338 content_all_charsets_flag, content_all_charsets_label);
339 add_widget (find_dlg, content_all_charsets_cbox);
340 #endif
342 content_whole_words_cbox = check_new (10, FIND_X / 2 + 1, content_whole_words, content_whole_words_label);
343 add_widget (find_dlg, content_whole_words_cbox);
345 content_first_hit_cbox = check_new (9, FIND_X / 2 + 1, content_first_hit_flag, content_first_hit_label);
346 add_widget (find_dlg, content_first_hit_cbox);
348 content_regexp_cbox = check_new (8, FIND_X / 2 + 1, content_regexp_flag, content_regexp_label);
349 add_widget (find_dlg, content_regexp_cbox);
351 content_case_sens_cbox = check_new (7, FIND_X / 2 + 1, content_case_sens_flag, content_case_label);
352 add_widget (find_dlg, content_case_sens_cbox);
354 #ifdef HAVE_CHARSET
355 file_all_charsets_cbox = check_new (11, 3,
356 file_all_charsets_flag, file_all_charsets_label);
357 add_widget (find_dlg, file_all_charsets_cbox);
358 #endif
360 skip_hidden_cbox = check_new (10, 3, skip_hidden_flag, file_skip_hidden_label);
361 add_widget (find_dlg, skip_hidden_cbox);
363 recursively_cbox = check_new (9, 3, find_recurs_flag, file_recurs_label);
364 add_widget (find_dlg, recursively_cbox);
366 file_pattern_cbox = check_new (8, 3, file_pattern_flag, file_pattern_label);
367 add_widget (find_dlg, file_pattern_cbox);
369 file_case_sens_cbox = check_new (7, 3, file_case_sens_flag, file_case_label);
370 add_widget (find_dlg, file_case_sens_cbox);
372 in_with = input_new (6, FIND_X / 2 + 1, INPUT_COLOR, FIND_X / 2 - 4, in_contents,
373 MC_HISTORY_SHARED_SEARCH, INPUT_COMPLETE_DEFAULT);
374 add_widget (find_dlg, in_with);
375 add_widget (find_dlg, label_new (5, FIND_X / 2 + 1, _("Content:")));
377 in_name = input_new (6, 3, INPUT_COLOR, FIND_X / 2 - 4, INPUT_LAST_TEXT, "name",
378 INPUT_COMPLETE_DEFAULT);
379 add_widget (find_dlg, in_name);
380 add_widget (find_dlg, label_new (5, 3, _("File name:")));
382 add_widget (find_dlg,
383 button_new (3, FIND_X - b2 - 2, B_TREE, NORMAL_BUTTON, buts[2], 0));
385 in_start = input_new (3, 3, INPUT_COLOR, FIND_X - b2 - 6, in_start_dir, "start",
386 INPUT_COMPLETE_DEFAULT);
387 add_widget (find_dlg, in_start);
388 add_widget (find_dlg, label_new (2, 3, _("Start at:")));
390 dlg_select_widget (in_name);
392 switch (run_dlg (find_dlg)) {
393 case B_CANCEL:
394 return_value = 0;
395 break;
397 case B_TREE:
398 #ifdef HAVE_CHARSET
399 file_all_charsets_flag = file_all_charsets_cbox->state & C_BOOL;
400 content_all_charsets_flag = content_all_charsets_cbox->state & C_BOOL;
401 #endif
402 content_case_sens_flag = content_case_sens_cbox->state & C_BOOL;
403 content_regexp_flag = content_regexp_cbox->state & C_BOOL;
404 content_first_hit_flag = content_first_hit_cbox->state & C_BOOL;
405 content_whole_words = content_whole_words_cbox->state & C_BOOL;
406 file_pattern_flag = file_pattern_cbox->state & C_BOOL;
407 file_case_sens_flag = file_case_sens_cbox->state & C_BOOL;
408 find_recurs_flag = recursively_cbox->state & C_BOOL;
409 skip_hidden_flag = skip_hidden_cbox->state & C_BOOL;
410 destroy_dlg (find_dlg);
411 if (in_start_dir != INPUT_LAST_TEXT)
412 g_free (in_start_dir);
413 temp_dir = g_strdup (in_start->buffer);
414 if ((temp_dir[0] == '.') && (temp_dir[1] == '\0')) {
415 g_free (temp_dir);
416 temp_dir = g_strdup (current_panel->cwd);
418 in_start_dir = tree_box (temp_dir);
419 if (in_start_dir != NULL)
420 g_free (temp_dir);
421 else
422 in_start_dir = temp_dir;
423 /* Warning: Dreadful goto */
424 goto find_par_start;
425 break;
427 default:
428 #ifdef HAVE_CHARSET
429 file_all_charsets_flag = file_all_charsets_cbox->state & C_BOOL;
430 content_all_charsets_flag = content_all_charsets_cbox->state & C_BOOL;
431 #endif
432 content_case_sens_flag = content_case_sens_cbox->state & C_BOOL;
433 content_regexp_flag = content_regexp_cbox->state & C_BOOL;
434 content_first_hit_flag = content_first_hit_cbox->state & C_BOOL;
435 content_whole_words = content_whole_words_cbox->state & C_BOOL;
436 find_recurs_flag = recursively_cbox->state & C_BOOL;
437 file_pattern_flag = file_pattern_cbox->state & C_BOOL;
438 file_case_sens_flag = file_case_sens_cbox->state & C_BOOL;
439 skip_hidden_flag = skip_hidden_cbox->state & C_BOOL;
441 /* keep empty Content field */
442 /* if not empty, fill from history */
443 *content = NULL;
444 in_contents = "";
445 if (in_with->buffer[0] != '\0') {
446 *content = g_strdup (in_with->buffer);
447 in_contents = INPUT_LAST_TEXT;
450 *start_dir = g_strdup ((in_start->buffer[0] != '\0') ? in_start->buffer : ".");
451 *pattern = g_strdup (in_name->buffer);
452 if (in_start_dir != INPUT_LAST_TEXT)
453 g_free (in_start_dir);
454 in_start_dir = g_strdup (*start_dir);
455 return_value = 1;
458 destroy_dlg (find_dlg);
460 return return_value;
463 #if GLIB_CHECK_VERSION (2, 14, 0)
465 static inline void
466 push_directory (const char *dir)
468 g_queue_push_head (&dir_queue, (void *) dir);
471 static inline char *
472 pop_directory (void)
474 return (char *) g_queue_pop_tail (&dir_queue);
477 /* Remove all the items in the stack */
478 static void
479 clear_stack (void)
481 g_queue_foreach (&dir_queue, (GFunc) g_free, NULL);
482 g_queue_clear (&dir_queue);
485 #else /* GLIB_CHAECK_VERSION */
487 static void
488 push_directory (const char *dir)
490 dir_stack *new;
492 new = g_new (dir_stack, 1);
493 new->name = str_unconst (dir);
494 new->prev = dir_stack_base;
495 dir_stack_base = new;
498 static char *
499 pop_directory (void)
501 char *name = NULL;
503 if (dir_stack_base != NULL) {
504 dir_stack *next;
505 name = dir_stack_base->name;
506 next = dir_stack_base->prev;
507 g_free (dir_stack_base);
508 dir_stack_base = next;
511 return name;
514 /* Remove all the items in the stack */
515 static void
516 clear_stack (void)
518 char *dir = NULL;
519 while ((dir = pop_directory ()) != NULL)
520 g_free (dir);
523 #endif /* GLIB_CHAECK_VERSION */
525 static void
526 insert_file (const char *dir, const char *file)
528 char *tmp_name = NULL;
529 static char *dirname = NULL;
531 while (dir [0] == PATH_SEP && dir [1] == PATH_SEP)
532 dir++;
534 if (old_dir){
535 if (strcmp (old_dir, dir)){
536 g_free (old_dir);
537 old_dir = g_strdup (dir);
538 dirname = add_to_list (dir, NULL);
540 } else {
541 old_dir = g_strdup (dir);
542 dirname = add_to_list (dir, NULL);
545 tmp_name = g_strdup_printf (" %s", file);
546 add_to_list (tmp_name, dirname);
547 g_free (tmp_name);
550 static void
551 find_add_match (const char *dir, const char *file)
553 insert_file (dir, file);
555 /* Don't scroll */
556 if (matches == 0)
557 listbox_select_by_number (find_list, 0);
558 send_message (&find_list->widget, WIDGET_DRAW, 0);
560 matches++;
561 found_num_update ();
565 * get_line_at:
567 * Returns malloced null-terminated line from file file_fd.
568 * Input is buffered in buf_size long buffer.
569 * Current pos in buf is stored in pos.
570 * n_read - number of read chars.
571 * has_newline - is there newline ?
573 static char *
574 get_line_at (int file_fd, char *buf, int buf_size, int *pos, int *n_read,
575 gboolean *has_newline)
577 char *buffer = NULL;
578 int buffer_size = 0;
579 char ch = 0;
580 int i = 0;
582 for (;;) {
583 if (*pos >= *n_read) {
584 *pos = 0;
585 *n_read = mc_read (file_fd, buf, buf_size);
586 if (*n_read <= 0)
587 break;
590 ch = buf[(*pos)++];
591 if (ch == '\0') {
592 /* skip possible leading zero(s) */
593 if (i == 0)
594 continue;
595 else
596 break;
599 if (i >= buffer_size - 1) {
600 buffer = g_realloc (buffer, buffer_size += 80);
602 /* Strip newline */
603 if (ch == '\n')
604 break;
606 buffer[i++] = ch;
609 *has_newline = (ch != '\0');
611 if (buffer != NULL)
612 buffer[i] = '\0';
614 return buffer;
617 static FindProgressStatus
618 check_find_events(Dlg_head *h)
620 Gpm_Event event;
621 int c;
623 event.x = -1;
624 c = tty_get_event (&event, h->mouse_status == MOU_REPEAT, FALSE);
625 if (c != EV_NONE) {
626 dlg_process_event (h, c, &event);
627 if (h->ret_value == B_ENTER
628 || h->ret_value == B_CANCEL
629 || h->ret_value == B_AGAIN
630 || h->ret_value == B_PANELIZE) {
631 /* dialog terminated */
632 return FIND_ABORT;
634 if (!(h->flags & DLG_WANT_IDLE)) {
635 /* searching suspended */
636 return FIND_SUSPEND;
640 return FIND_CONT;
644 * search_content:
646 * Search the content_pattern string in the DIRECTORY/FILE.
647 * It will add the found entries to the find listbox.
649 * returns FALSE if do_search should look for another file
650 * TRUE if do_search should exit and proceed to the event handler
652 static gboolean
653 search_content (Dlg_head *h, const char *directory, const char *filename)
655 struct stat s;
656 char buffer [BUF_4K];
657 char *fname = NULL;
658 int file_fd;
659 gboolean ret_val = FALSE;
661 fname = concat_dir_and_file (directory, filename);
663 if (mc_stat (fname, &s) != 0 || !S_ISREG (s.st_mode)){
664 g_free (fname);
665 return 0;
668 file_fd = mc_open (fname, O_RDONLY);
669 g_free (fname);
671 if (file_fd == -1)
672 return 0;
674 g_snprintf (buffer, sizeof (buffer), _("Grepping in %s"), str_trunc (filename, FIND2_X_USE));
676 status_update (buffer);
677 mc_refresh ();
679 tty_enable_interrupt_key ();
680 tty_got_interrupt ();
683 int line = 1;
684 int pos = 0;
685 int n_read = 0;
686 gboolean has_newline;
687 char *p = NULL;
688 gboolean found = FALSE;
689 gsize found_len;
690 char result [BUF_MEDIUM];
692 if (resuming) {
693 /* We've been previously suspended, start from the previous position */
694 resuming = 0;
695 line = last_line;
696 pos = last_pos;
698 while (!ret_val
699 && (p = get_line_at (file_fd, buffer, sizeof (buffer),
700 &pos, &n_read, &has_newline)) != NULL) {
701 if (!found /* Search in binary line once */
702 && mc_search_run (search_content_handle,
703 (const void *) p, 0, strlen (p), &found_len)) {
704 g_snprintf (result, sizeof (result), "%d:%s", line, filename);
705 find_add_match (directory, result);
706 found = TRUE;
708 g_free (p);
710 if (found && content_first_hit_flag)
711 break;
713 if (has_newline) {
714 found = FALSE;
715 line++;
718 if ((line & 0xff) == 0) {
719 FindProgressStatus res;
720 res = check_find_events(h);
721 switch (res) {
722 case FIND_ABORT:
723 stop_idle (h);
724 ret_val = TRUE;
725 break;
726 case FIND_SUSPEND:
727 resuming = 1;
728 last_line = line;
729 last_pos = pos;
730 ret_val = TRUE;
731 break;
732 default:
733 break;
739 tty_disable_interrupt_key ();
740 mc_close (file_fd);
741 return ret_val;
744 static int
745 do_search (struct Dlg_head *h)
747 static struct dirent *dp = NULL;
748 static DIR *dirp = NULL;
749 static char *directory = NULL;
750 struct stat tmp_stat;
751 static int pos = 0;
752 static int subdirs_left = 0;
753 gsize bytes_found;
754 unsigned long count; /* Number of files displayed */
756 if (!h) { /* someone forces me to close dirp */
757 if (dirp) {
758 mc_closedir (dirp);
759 dirp = NULL;
761 g_free (directory);
762 directory = NULL;
763 dp = NULL;
764 return 1;
767 search_content_handle = mc_search_new(content_pattern, -1);
768 if (search_content_handle) {
769 search_content_handle->search_type = (content_regexp_flag) ? MC_SEARCH_T_REGEX : MC_SEARCH_T_NORMAL;
770 search_content_handle->is_case_sentitive = content_case_sens_flag;
771 search_content_handle->whole_words = content_whole_words;
772 search_content_handle->is_all_charsets = content_all_charsets_flag;
774 search_file_handle = mc_search_new(find_pattern, -1);
775 search_file_handle->search_type = (file_pattern_flag) ? MC_SEARCH_T_GLOB : MC_SEARCH_T_REGEX;
776 search_file_handle->is_case_sentitive = file_case_sens_flag;
777 search_file_handle->is_all_charsets = file_all_charsets_flag;
778 search_file_handle->is_entire_line = file_pattern_flag;
780 count = 0;
782 do_search_begin:
783 while (!dp){
784 if (dirp){
785 mc_closedir (dirp);
786 dirp = 0;
789 while (!dirp){
790 char *tmp = NULL;
792 tty_setcolor (REVERSE_COLOR);
793 while (1) {
794 char *temp_dir = NULL;
795 gboolean found;
797 tmp = pop_directory ();
798 if (tmp == NULL) {
799 running = FALSE;
800 status_update (_("Finished"));
801 stop_idle (h);
802 mc_search_free (search_file_handle);
803 search_file_handle = NULL;
804 mc_search_free (search_content_handle);
805 search_content_handle = NULL;
806 return 0;
809 if ((find_ignore_dirs == NULL) || (find_ignore_dirs[0] == '\0'))
810 break;
812 temp_dir = g_strdup_printf (":%s:", tmp);
813 found = strstr (find_ignore_dirs, temp_dir) != 0;
814 g_free (temp_dir);
816 if (!found)
817 break;
819 g_free (tmp);
822 g_free (directory);
823 directory = tmp;
825 if (verbose){
826 char buffer [BUF_SMALL];
828 g_snprintf (buffer, sizeof (buffer), _("Searching %s"),
829 str_trunc (directory, FIND2_X_USE));
830 status_update (buffer);
832 /* mc_stat should not be called after mc_opendir
833 because vfs_s_opendir modifies the st_nlink
835 if (!mc_stat (directory, &tmp_stat))
836 subdirs_left = tmp_stat.st_nlink - 2;
837 else
838 subdirs_left = 0;
840 dirp = mc_opendir (directory);
841 } /* while (!dirp) */
843 /* skip invalid filenames */
844 while ((dp = mc_readdir (dirp)) != NULL
845 && !str_is_valid_string (dp->d_name))
847 } /* while (!dp) */
849 if (strcmp (dp->d_name, ".") == 0 ||
850 strcmp (dp->d_name, "..") == 0){
851 dp = mc_readdir (dirp);
852 /* skip invalid filenames */
853 while (dp != NULL && !str_is_valid_string (dp->d_name))
854 dp = mc_readdir (dirp);
856 mc_search_free(search_file_handle);
857 search_file_handle = NULL;
858 mc_search_free(search_content_handle);
859 search_content_handle = NULL;
860 return 1;
863 if (!(skip_hidden_flag && (dp->d_name[0] == '.'))) {
864 gboolean search_ok;
866 if ((subdirs_left != 0) && find_recurs_flag
867 && (directory != NULL)) { /* Can directory be NULL ? */
868 char *tmp_name = concat_dir_and_file (directory, dp->d_name);
869 if (!mc_lstat (tmp_name, &tmp_stat)
870 && S_ISDIR (tmp_stat.st_mode)) {
871 push_directory (tmp_name);
872 subdirs_left--;
873 } else
874 g_free (tmp_name);
877 search_ok = mc_search_run (search_file_handle, dp->d_name,
878 0, strlen (dp->d_name), &bytes_found);
880 if (search_ok) {
881 if (content_pattern == NULL)
882 find_add_match (directory, dp->d_name);
883 else if (search_content (h, directory, dp->d_name)) {
884 mc_search_free(search_file_handle);
885 search_file_handle = NULL;
886 mc_search_free(search_content_handle);
887 search_content_handle = NULL;
888 return 1;
893 /* skip invalid filenames */
894 while ((dp = mc_readdir (dirp)) != NULL
895 && !str_is_valid_string (dp->d_name))
898 /* Displays the nice dot */
899 count++;
900 if (!(count & 31)){
901 /* For nice updating */
902 const char rotating_dash[] = "|/-\\";
904 if (verbose){
905 pos = (pos + 1) % 4;
906 tty_setcolor (DLG_NORMALC (h));
907 dlg_move (h, FIND2_Y - 7, FIND2_X - 4);
908 tty_print_char (rotating_dash [pos]);
909 mc_refresh ();
911 } else
912 goto do_search_begin;
914 mc_search_free (search_file_handle);
915 search_file_handle = NULL;
916 mc_search_free (search_content_handle);
917 search_content_handle = NULL;
918 return 1;
921 static void
922 init_find_vars (void)
924 g_free (old_dir);
925 old_dir = NULL;
926 matches = 0;
928 /* Remove all the items in the stack */
929 clear_stack ();
932 static char *
933 make_fullname (const char *dirname, const char *filename)
936 if (strcmp(dirname, ".") == 0 || strcmp(dirname, "."PATH_SEP_STR) == 0)
937 return g_strdup (filename);
938 if (strncmp(dirname, "."PATH_SEP_STR, 2) == 0)
939 return concat_dir_and_file (dirname + 2, filename);
940 return concat_dir_and_file (dirname, filename);
943 static void
944 find_do_view_edit (int unparsed_view, int edit, char *dir, char *file)
946 char *fullname = NULL;
947 const char *filename = NULL;
948 int line;
950 if (content_pattern != NULL) {
951 filename = strchr (file + 4, ':') + 1;
952 line = atoi (file + 4);
953 } else {
954 filename = file + 4;
955 line = 0;
958 fullname = make_fullname (dir, filename);
959 if (edit)
960 do_edit_at_line (fullname, line);
961 else
962 view_file_at_line (fullname, unparsed_view, use_internal_view, line);
963 g_free (fullname);
966 static cb_ret_t
967 view_edit_currently_selected_file (int unparsed_view, int edit)
969 WLEntry *entry = find_list->current;
970 char *dir = NULL;
972 if (!entry)
973 return MSG_NOT_HANDLED;
975 dir = entry->data;
977 if (!entry->text || !dir)
978 return MSG_NOT_HANDLED;
980 find_do_view_edit (unparsed_view, edit, dir, entry->text);
981 return MSG_HANDLED;
984 static cb_ret_t
985 find_callback (struct Dlg_head *h, dlg_msg_t msg, int parm)
987 switch (msg) {
988 case DLG_KEY:
989 if (parm == KEY_F (3) || parm == KEY_F (13)) {
990 int unparsed_view = (parm == KEY_F (13));
991 return view_edit_currently_selected_file (unparsed_view, 0);
993 if (parm == KEY_F (4)) {
994 return view_edit_currently_selected_file (0, 1);
996 return MSG_NOT_HANDLED;
998 case DLG_IDLE:
999 do_search (h);
1000 return MSG_HANDLED;
1002 default:
1003 return default_dlg_callback (h, msg, parm);
1007 /* Handles the Stop/Start button in the find window */
1008 static int
1009 start_stop (int button)
1011 (void) button;
1013 running = is_start;
1014 set_idle_proc (find_dlg, running);
1015 is_start = !is_start;
1017 status_update (is_start ? _("Stopped") : _("Searching"));
1018 button_set_text (stop_button, fbuts [is_start ? 1 : 0].text);
1020 return 0;
1023 /* Handle view command, when invoked as a button */
1024 static int
1025 find_do_view_file (int button)
1027 (void) button;
1029 view_edit_currently_selected_file (0, 0);
1030 return 0;
1033 /* Handle edit command, when invoked as a button */
1034 static int
1035 find_do_edit_file (int button)
1037 (void) button;
1039 view_edit_currently_selected_file (0, 1);
1040 return 0;
1043 static void
1044 setup_gui (void)
1046 #ifdef ENABLE_NLS
1047 static gboolean i18n_flag = FALSE;
1049 if (!i18n_flag) {
1050 int i = sizeof (fbuts) / sizeof (fbuts[0]);
1051 while (i-- != 0) {
1052 fbuts[i].text = _(fbuts[i].text);
1053 fbuts[i].len = str_term_width1 (fbuts[i].text) + 3;
1056 fbuts[2].len += 2; /* DEFPUSH_BUTTON */
1057 i18n_flag = TRUE;
1059 #endif /* ENABLE_NLS */
1062 * Dynamically place buttons centered within current window size
1065 int l0 = max (fbuts[0].len, fbuts[1].len);
1066 int l1 = fbuts[2].len + fbuts[3].len + l0 + fbuts[4].len;
1067 int l2 = fbuts[5].len + fbuts[6].len + fbuts[7].len;
1068 int r1, r2;
1070 /* Check, if both button rows fit within FIND2_X */
1071 FIND2_X = max (l1 + 9, COLS - 16);
1072 FIND2_X = max (l2 + 8, FIND2_X);
1074 /* compute amount of space between buttons for each row */
1075 r1 = (FIND2_X - 4 - l1) % 5;
1076 l1 = (FIND2_X - 4 - l1) / 5;
1077 r2 = (FIND2_X - 4 - l2) % 4;
1078 l2 = (FIND2_X - 4 - l2) / 4;
1080 /* ...and finally, place buttons */
1081 fbuts[2].x = 2 + r1 / 2 + l1;
1082 fbuts[3].x = fbuts[2].x + fbuts[2].len + l1;
1083 fbuts[0].x = fbuts[3].x + fbuts[3].len + l1;
1084 fbuts[4].x = fbuts[0].x + l0 + l1;
1085 fbuts[5].x = 2 + r2 / 2 + l2;
1086 fbuts[6].x = fbuts[5].x + fbuts[5].len + l2;
1087 fbuts[7].x = fbuts[6].x + fbuts[6].len + l2;
1090 find_dlg =
1091 create_dlg (0, 0, FIND2_Y, FIND2_X, dialog_colors, find_callback,
1092 "[Find File]", _("Find File"), DLG_CENTER | DLG_REVERSE);
1094 add_widget (find_dlg,
1095 button_new (FIND2_Y - 3, fbuts[7].x, B_VIEW, NORMAL_BUTTON,
1096 fbuts[7].text, find_do_edit_file));
1097 add_widget (find_dlg,
1098 button_new (FIND2_Y - 3, fbuts[6].x, B_VIEW, NORMAL_BUTTON,
1099 fbuts[6].text, find_do_view_file));
1100 add_widget (find_dlg,
1101 button_new (FIND2_Y - 3, fbuts[5].x, B_PANELIZE,
1102 NORMAL_BUTTON, fbuts[5].text, 0));
1104 add_widget (find_dlg,
1105 button_new (FIND2_Y - 4, fbuts[4].x, B_CANCEL,
1106 NORMAL_BUTTON, fbuts[4].text, 0));
1107 stop_button =
1108 button_new (FIND2_Y - 4, fbuts[0].x, B_STOP, NORMAL_BUTTON,
1109 fbuts[0].text, start_stop);
1110 add_widget (find_dlg, stop_button);
1111 add_widget (find_dlg,
1112 button_new (FIND2_Y - 4, fbuts[3].x, B_AGAIN,
1113 NORMAL_BUTTON, fbuts[3].text, 0));
1114 add_widget (find_dlg,
1115 button_new (FIND2_Y - 4, fbuts[2].x, B_ENTER,
1116 DEFPUSH_BUTTON, fbuts[2].text, 0));
1118 status_label = label_new (FIND2_Y - 7, 4, _("Searching"));
1119 add_widget (find_dlg, status_label);
1121 found_num_label = label_new (FIND2_Y - 6, 4, "");
1122 add_widget (find_dlg, found_num_label);
1124 find_list =
1125 listbox_new (2, 2, FIND2_Y - 10, FIND2_X - 4, NULL);
1126 add_widget (find_dlg, find_list);
1129 static int
1130 run_process (void)
1132 resuming = 0;
1133 set_idle_proc (find_dlg, 1);
1134 return run_dlg (find_dlg);
1137 static void
1138 kill_gui (void)
1140 set_idle_proc (find_dlg, 0);
1141 destroy_dlg (find_dlg);
1144 static int
1145 find_file (const char *start_dir, const char *pattern, const char *content,
1146 char **dirname, char **filename)
1148 int return_value = 0;
1149 char *dir_tmp = NULL, *file_tmp = NULL;
1151 setup_gui ();
1153 /* FIXME: Need to cleanup this, this ought to be passed non-globaly */
1154 find_pattern = str_unconst (pattern);
1155 content_pattern = (content != NULL && str_is_valid_string (content))
1156 ? g_strdup(content)
1157 : NULL;
1159 init_find_vars ();
1160 push_directory (start_dir);
1162 return_value = run_process ();
1164 /* Remove all the items in the stack */
1165 clear_stack ();
1167 get_list_info (&file_tmp, &dir_tmp);
1169 if (dir_tmp)
1170 *dirname = g_strdup (dir_tmp);
1171 if (file_tmp)
1172 *filename = g_strdup (file_tmp);
1174 if (return_value == B_PANELIZE && *filename) {
1175 int status, link_to_dir, stale_link;
1176 int next_free = 0;
1177 int i;
1178 struct stat st;
1179 WLEntry *entry = find_list->list;
1180 dir_list *list = &current_panel->dir;
1181 char *name = NULL;
1183 for (i = 0; entry != NULL && i < find_list->count;
1184 entry = entry->next, i++) {
1185 const char *filename = NULL;
1187 if (!entry->text || !entry->data)
1188 continue;
1190 if (content_pattern != NULL)
1191 filename = strchr (entry->text + 4, ':') + 1;
1192 else
1193 filename = entry->text + 4;
1195 name = make_fullname (entry->data, filename);
1196 status =
1197 handle_path (list, name, &st, next_free, &link_to_dir,
1198 &stale_link);
1199 if (status == 0) {
1200 g_free (name);
1201 continue;
1203 if (status == -1) {
1204 g_free (name);
1205 break;
1208 /* don't add files more than once to the panel */
1209 if (content_pattern != NULL && next_free > 0
1210 && strcmp (list->list[next_free - 1].fname, name) == 0) {
1211 g_free (name);
1212 continue;
1215 if (!next_free) /* first turn i.e clean old list */
1216 panel_clean_dir (current_panel);
1217 list->list[next_free].fnamelen = strlen (name);
1218 list->list[next_free].fname = name;
1219 list->list[next_free].f.marked = 0;
1220 list->list[next_free].f.link_to_dir = link_to_dir;
1221 list->list[next_free].f.stale_link = stale_link;
1222 list->list[next_free].f.dir_size_computed = 0;
1223 list->list[next_free].st = st;
1224 list->list[next_free].sort_key = NULL;
1225 list->list[next_free].second_sort_key = NULL;
1226 next_free++;
1227 if (!(next_free & 15))
1228 rotate_dash ();
1231 if (next_free) {
1232 current_panel->count = next_free;
1233 current_panel->is_panelized = 1;
1235 if (start_dir[0] == PATH_SEP) {
1236 strcpy (current_panel->cwd, PATH_SEP_STR);
1237 chdir (PATH_SEP_STR);
1242 g_free (content_pattern);
1243 kill_gui ();
1244 do_search (NULL); /* force do_search to release resources */
1245 g_free (old_dir);
1246 old_dir = NULL;
1248 return return_value;
1251 void
1252 do_find (void)
1254 char *start_dir = NULL, *pattern = NULL, *content = NULL;
1255 char *filename = NULL, *dirname = NULL;
1256 int v;
1257 gboolean dir_and_file_set;
1259 while (find_parameters (&start_dir, &pattern, &content)){
1260 if (pattern [0] == '\0')
1261 break; /* nothing search*/
1263 dirname = filename = NULL;
1264 is_start = FALSE;
1265 v = find_file (start_dir, pattern, content, &dirname, &filename);
1266 g_free (pattern);
1268 if (v == B_ENTER){
1269 if (dirname || filename){
1270 if (dirname){
1271 do_cd (dirname, cd_exact);
1272 if (filename)
1273 try_to_select (current_panel, filename + (content ?
1274 (strchr (filename + 4, ':') - filename + 1) : 4) );
1275 } else if (filename)
1276 do_cd (filename, cd_exact);
1277 select_item (current_panel);
1279 g_free (dirname);
1280 g_free (filename);
1281 break;
1284 g_free (content);
1285 dir_and_file_set = dirname && filename;
1286 g_free (dirname);
1287 g_free (filename);
1289 if (v == B_CANCEL)
1290 break;
1292 if (v == B_PANELIZE){
1293 if (dir_and_file_set){
1294 try_to_select (current_panel, NULL);
1295 panel_re_sort (current_panel);
1296 try_to_select (current_panel, NULL);
1298 break;