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 */
90 /* List of directories to be ignored, separated by ':' */
91 char *find_ignore_dirs
= NULL
;
93 /* static variables to remember find parameters */
94 static WInput
*in_start
; /* Start path */
95 static WInput
*in_name
; /* Filename */
96 static WInput
*in_with
; /* Text inside filename */
97 static WCheck
*file_case_sens_cbox
; /* "case sensitive" checkbox */
98 static WCheck
*file_pattern_cbox
; /* File name is glob or regexp */
99 static WCheck
*recursively_cbox
;
100 static WCheck
*skip_hidden_cbox
;
101 static WCheck
*content_case_sens_cbox
; /* "case sensitive" checkbox */
102 static WCheck
*content_regexp_cbox
; /* "find regular expression" checkbox */
103 static WCheck
*content_first_hit_cbox
; /* "First hit" checkbox" */
104 static WCheck
*content_whole_words_cbox
; /* "whole words" checkbox */
106 static WCheck
*file_all_charsets_cbox
;
107 static WCheck
*content_all_charsets_cbox
;
110 static gboolean running
= FALSE
; /* nice flag */
111 static char *find_pattern
= NULL
; /* Pattern to search */
112 static char *content_pattern
= NULL
; /* pattern to search inside files; if
113 content_regexp_flag is true, it contains the
114 regex pattern, else the search string. */
115 static unsigned long matches
; /* Number of matches */
116 static gboolean is_start
= FALSE
; /* Status of the start/stop toggle button */
117 static char *old_dir
= NULL
;
119 /* Where did we stop */
121 static int last_line
;
124 static Dlg_head
*find_dlg
; /* The dialog */
125 static WButton
*stop_button
; /* pointer to the stop button */
126 static WLabel
*status_label
; /* Finished, Searching etc. */
127 static WLabel
*found_num_label
; /* Number of found items */
128 static WListbox
*find_list
; /* Listbox with the file list */
130 /* This keeps track of the directory stack */
131 #if GLIB_CHECK_VERSION (2, 14, 0)
132 static GQueue dir_queue
= G_QUEUE_INIT
;
134 typedef struct dir_stack
137 struct dir_stack
*prev
;
140 static dir_stack
*dir_stack_base
= 0;
141 #endif /* GLIB_CHECK_VERSION */
147 int len
; /* length including space and brackets */
151 {N_("&Suspend"), 11, 29},
152 {N_("Con&tinue"), 12, 29},
153 {N_("&Chdir"), 11, 3},
154 {N_("&Again"), 9, 17},
155 {N_("&Quit"), 8, 43},
156 {N_("Pane&lize"), 12, 3},
157 {N_("&View - F3"), 13, 20},
158 {N_("&Edit - F4"), 13, 38}
162 /* find file options */
165 /* file name options */
166 gboolean file_case_sens
;
167 gboolean file_pattern
;
168 gboolean find_recurs
;
169 gboolean skip_hidden
;
170 gboolean file_all_charsets
;
172 /* file content options */
173 gboolean content_case_sens
;
174 gboolean content_regexp
;
175 gboolean content_first_hit
;
176 gboolean content_whole_words
;
177 gboolean content_all_charsets
;
178 } find_file_options_t
;
180 static find_file_options_t options
= {
181 TRUE
, TRUE
, TRUE
, FALSE
, FALSE
,
182 TRUE
, FALSE
, FALSE
, FALSE
, FALSE
185 static char *in_start_dir
= INPUT_LAST_TEXT
;
187 static mc_search_t
*search_file_handle
= NULL
;
188 static mc_search_t
*search_content_handle
= NULL
;
191 find_load_options (void)
193 static gboolean loaded
= FALSE
;
201 /* Back compatibility: try load old parameter at first */
202 ignore_dirs
= mc_config_get_string (mc_main_config
, "Misc", "find_ignore_dirs", "");
203 if (ignore_dirs
[0] != '\0')
205 find_ignore_dirs
= g_strconcat (":", ignore_dirs
, ":", (char *) NULL
);
206 mc_config_set_string (mc_main_config
, "FindFile", "ignore_dirs", ignore_dirs
);
208 g_free (ignore_dirs
);
209 mc_config_del_param (mc_main_config
, "Misc", "find_ignore_dirs");
211 /* Then load new parameters */
212 ignore_dirs
= mc_config_get_string (mc_main_config
, "FindFile", "ignore_dirs", "");
213 if (ignore_dirs
[0] != '\0')
215 g_free (find_ignore_dirs
);
216 find_ignore_dirs
= g_strconcat (":", ignore_dirs
, ":", (char *) NULL
);
218 g_free (ignore_dirs
);
220 options
.file_case_sens
=
221 mc_config_get_bool (mc_main_config
, "FindFile", "file_case_sens", TRUE
);
222 options
.file_pattern
=
223 mc_config_get_bool (mc_main_config
, "FindFile", "file_shell_pattern", TRUE
);
224 options
.find_recurs
= mc_config_get_bool (mc_main_config
, "FindFile", "file_find_recurs", TRUE
);
225 options
.skip_hidden
=
226 mc_config_get_bool (mc_main_config
, "FindFile", "file_skip_hidden", FALSE
);
227 options
.file_all_charsets
=
228 mc_config_get_bool (mc_main_config
, "FindFile", "file_all_charsets", FALSE
);
229 options
.content_case_sens
=
230 mc_config_get_bool (mc_main_config
, "FindFile", "content_case_sens", TRUE
);
231 options
.content_regexp
=
232 mc_config_get_bool (mc_main_config
, "FindFile", "content_regexp", FALSE
);
233 options
.content_first_hit
=
234 mc_config_get_bool (mc_main_config
, "FindFile", "content_first_hit", FALSE
);
235 options
.content_whole_words
=
236 mc_config_get_bool (mc_main_config
, "FindFile", "content_whole_words", FALSE
);
237 options
.content_all_charsets
=
238 mc_config_get_bool (mc_main_config
, "FindFile", "content_all_charsets", FALSE
);
242 find_save_options (void)
244 mc_config_set_bool (mc_main_config
, "FindFile", "file_case_sens", options
.file_case_sens
);
245 mc_config_set_bool (mc_main_config
, "FindFile", "file_shell_pattern", options
.file_pattern
);
246 mc_config_set_bool (mc_main_config
, "FindFile", "file_find_recurs", options
.find_recurs
);
247 mc_config_set_bool (mc_main_config
, "FindFile", "file_skip_hidden", options
.skip_hidden
);
248 mc_config_set_bool (mc_main_config
, "FindFile", "file_all_charsets", options
.file_all_charsets
);
249 mc_config_set_bool (mc_main_config
, "FindFile", "content_case_sens", options
.content_case_sens
);
250 mc_config_set_bool (mc_main_config
, "FindFile", "content_regexp", options
.content_regexp
);
251 mc_config_set_bool (mc_main_config
, "FindFile", "content_first_hit", options
.content_first_hit
);
252 mc_config_set_bool (mc_main_config
, "FindFile", "content_whole_words",
253 options
.content_whole_words
);
254 mc_config_set_bool (mc_main_config
, "FindFile", "content_all_charsets",
255 options
.content_all_charsets
);
259 add_to_list (const char *text
, void *data
)
261 return listbox_add_item (find_list
, LISTBOX_APPEND_AT_END
, 0, text
, data
);
265 stop_idle (void *data
)
267 set_idle_proc (data
, 0);
271 status_update (const char *text
)
273 label_set_text (status_label
, text
);
277 found_num_update (void)
279 char buffer
[BUF_TINY
];
280 g_snprintf (buffer
, sizeof (buffer
), _("Found: %ld"), matches
);
281 label_set_text (found_num_label
, buffer
);
285 get_list_info (char **file
, char **dir
)
287 listbox_get_current (find_list
, file
, (void **) dir
);
290 /* check regular expression */
292 find_check_regexp (const char *r
)
295 gboolean regexp_ok
= FALSE
;
297 search
= mc_search_new (r
, -1);
301 search
->search_type
= MC_SEARCH_T_REGEX
;
302 regexp_ok
= mc_search_prepare (search
);
303 mc_search_free (search
);
310 * Callback for the parameter dialog.
311 * Validate regex, prevent closing the dialog if it's invalid.
314 find_parm_callback (Dlg_head
* h
, Widget
* sender
, dlg_msg_t msg
, int parm
, void *data
)
319 if (h
->ret_value
!= B_ENTER
)
322 /* check filename regexp */
323 if (!(file_pattern_cbox
->state
& C_BOOL
)
324 && (in_name
->buffer
[0] != '\0') && !find_check_regexp (in_name
->buffer
))
326 message (D_ERROR
, MSG_ERROR
, _(" Malformed regular expression "));
327 dlg_select_widget (in_name
);
328 h
->running
= 1; /* Don't stop the dialog */
332 /* check content regexp */
333 if ((content_regexp_cbox
->state
& C_BOOL
)
334 && (in_with
->buffer
[0] != '\0') && !find_check_regexp (in_with
->buffer
))
336 message (D_ERROR
, MSG_ERROR
, _(" Malformed regular expression "));
337 dlg_select_widget (in_with
);
338 h
->running
= 1; /* Don't stop the dialog */
345 return default_dlg_callback (h
, sender
, msg
, parm
, data
);
350 * find_parameters: gets information from the user
352 * If the return value is TRUE, then the following holds:
354 * START_DIR and PATTERN are pointers to char * and upon return they
355 * contain the information provided by the user.
357 * CONTENT holds a strdup of the contents specified by the user if he
358 * asked for them or 0 if not (note, this is different from the
359 * behavior for the other two parameters.
363 find_parameters (char **start_dir
, char **pattern
, char **content
)
365 gboolean return_value
;
368 const char *file_case_label
= N_("Cas&e sensitive");
369 const char *file_pattern_label
= N_("&Using shell patterns");
370 const char *file_recurs_label
= N_("&Find recursively");
371 const char *file_skip_hidden_label
= N_("S&kip hidden");
373 const char *file_all_charsets_label
= N_("&All charsets");
377 const char *content_case_label
= N_("Case sens&itive");
378 const char *content_regexp_label
= N_("Re&gular expression");
379 const char *content_first_hit_label
= N_("Fir&st hit");
380 const char *content_whole_words_label
= N_("&Whole words");
382 const char *content_all_charsets_label
= N_("All cha&rsets");
385 const char *buts
[] = { N_("&OK"), N_("&Cancel"), N_("&Tree") };
391 int i
= sizeof (buts
) / sizeof (buts
[0]);
393 buts
[i
] = _(buts
[i
]);
395 file_case_label
= _(file_case_label
);
396 file_pattern_label
= _(file_pattern_label
);
397 file_recurs_label
= _(file_recurs_label
);
398 file_skip_hidden_label
= _(file_skip_hidden_label
);
400 file_all_charsets_label
= _(file_all_charsets_label
);
401 content_all_charsets_label
= _(content_all_charsets_label
);
403 content_case_label
= _(content_case_label
);
404 content_regexp_label
= _(content_regexp_label
);
405 content_first_hit_label
= _(content_first_hit_label
);
406 content_whole_words_label
= _(content_whole_words_label
);
408 #endif /* ENABLE_NLS */
410 b0
= str_term_width1 (buts
[0]) + 6; /* default button */
411 b1
= str_term_width1 (buts
[1]) + 4;
412 b2
= str_term_width1 (buts
[2]) + 4;
414 find_load_options ();
416 if (in_start_dir
== NULL
)
417 in_start_dir
= g_strdup (".");
421 create_dlg (0, 0, FIND_Y
, FIND_X
, dialog_colors
,
422 find_parm_callback
, "[Find File]", _("Find File"), DLG_CENTER
| DLG_REVERSE
);
424 add_widget (find_dlg
,
425 button_new (FIND_Y
- 3, FIND_X
* 3 / 4 - b1
/ 2, B_CANCEL
, NORMAL_BUTTON
, buts
[1],
427 add_widget (find_dlg
,
428 button_new (FIND_Y
- 3, FIND_X
/ 4 - b0
/ 2, B_ENTER
, DEFPUSH_BUTTON
, buts
[0], 0));
431 content_all_charsets_cbox
= check_new (11, FIND_X
/ 2 + 1,
432 options
.content_all_charsets
,
433 content_all_charsets_label
);
434 add_widget (find_dlg
, content_all_charsets_cbox
);
437 content_whole_words_cbox
=
438 check_new (10, FIND_X
/ 2 + 1, options
.content_whole_words
, content_whole_words_label
);
439 add_widget (find_dlg
, content_whole_words_cbox
);
441 content_first_hit_cbox
=
442 check_new (9, FIND_X
/ 2 + 1, options
.content_first_hit
, content_first_hit_label
);
443 add_widget (find_dlg
, content_first_hit_cbox
);
445 content_regexp_cbox
=
446 check_new (8, FIND_X
/ 2 + 1, options
.content_regexp
, content_regexp_label
);
447 add_widget (find_dlg
, content_regexp_cbox
);
449 content_case_sens_cbox
=
450 check_new (7, FIND_X
/ 2 + 1, options
.content_case_sens
, content_case_label
);
451 add_widget (find_dlg
, content_case_sens_cbox
);
454 file_all_charsets_cbox
= check_new (11, 3, options
.file_all_charsets
, file_all_charsets_label
);
455 add_widget (find_dlg
, file_all_charsets_cbox
);
458 skip_hidden_cbox
= check_new (10, 3, options
.skip_hidden
, file_skip_hidden_label
);
459 add_widget (find_dlg
, skip_hidden_cbox
);
461 recursively_cbox
= check_new (9, 3, options
.find_recurs
, file_recurs_label
);
462 add_widget (find_dlg
, recursively_cbox
);
464 file_pattern_cbox
= check_new (8, 3, options
.file_pattern
, file_pattern_label
);
465 add_widget (find_dlg
, file_pattern_cbox
);
467 file_case_sens_cbox
= check_new (7, 3, options
.file_case_sens
, file_case_label
);
468 add_widget (find_dlg
, file_case_sens_cbox
);
470 in_with
= input_new (6, FIND_X
/ 2 + 1, INPUT_COLOR
, FIND_X
/ 2 - 4, INPUT_LAST_TEXT
,
471 MC_HISTORY_SHARED_SEARCH
, INPUT_COMPLETE_DEFAULT
);
472 add_widget (find_dlg
, in_with
);
473 add_widget (find_dlg
, label_new (5, FIND_X
/ 2 + 1, _("Content:")));
475 in_name
= input_new (6, 3, INPUT_COLOR
, FIND_X
/ 2 - 4, INPUT_LAST_TEXT
, "name",
476 INPUT_COMPLETE_DEFAULT
);
477 add_widget (find_dlg
, in_name
);
478 add_widget (find_dlg
, label_new (5, 3, _("File name:")));
480 add_widget (find_dlg
, button_new (3, FIND_X
- b2
- 2, B_TREE
, NORMAL_BUTTON
, buts
[2], 0));
482 in_start
= input_new (3, 3, INPUT_COLOR
, FIND_X
- b2
- 6, in_start_dir
, "start",
483 INPUT_COMPLETE_DEFAULT
);
484 add_widget (find_dlg
, in_start
);
485 add_widget (find_dlg
, label_new (2, 3, _("Start at:")));
487 dlg_select_widget (in_name
);
489 switch (run_dlg (find_dlg
))
492 return_value
= FALSE
;
497 char temp_dir
[MC_MAXPATHLEN
];
499 g_strlcpy (temp_dir
, in_start
->buffer
, sizeof (temp_dir
));
501 options
.file_all_charsets
= file_all_charsets_cbox
->state
& C_BOOL
;
502 options
.content_all_charsets
= content_all_charsets_cbox
->state
& C_BOOL
;
504 options
.content_case_sens
= content_case_sens_cbox
->state
& C_BOOL
;
505 options
.content_regexp
= content_regexp_cbox
->state
& C_BOOL
;
506 options
.content_first_hit
= content_first_hit_cbox
->state
& C_BOOL
;
507 options
.content_whole_words
= content_whole_words_cbox
->state
& C_BOOL
;
508 options
.file_pattern
= file_pattern_cbox
->state
& C_BOOL
;
509 options
.file_case_sens
= file_case_sens_cbox
->state
& C_BOOL
;
510 options
.find_recurs
= recursively_cbox
->state
& C_BOOL
;
511 options
.skip_hidden
= skip_hidden_cbox
->state
& C_BOOL
;
512 destroy_dlg (find_dlg
);
514 if ((temp_dir
[0] == '\0') || ((temp_dir
[0] == '.') && (temp_dir
[1] == '\0')))
515 g_strlcpy (temp_dir
, current_panel
->cwd
, sizeof (temp_dir
));
517 if (in_start_dir
!= INPUT_LAST_TEXT
)
518 g_free (in_start_dir
);
519 in_start_dir
= tree_box (temp_dir
);
520 if (in_start_dir
== NULL
)
521 in_start_dir
= g_strdup (temp_dir
);
522 /* Warning: Dreadful goto */
529 options
.file_all_charsets
= file_all_charsets_cbox
->state
& C_BOOL
;
530 options
.content_all_charsets
= content_all_charsets_cbox
->state
& C_BOOL
;
532 options
.content_case_sens
= content_case_sens_cbox
->state
& C_BOOL
;
533 options
.content_regexp
= content_regexp_cbox
->state
& C_BOOL
;
534 options
.content_first_hit
= content_first_hit_cbox
->state
& C_BOOL
;
535 options
.content_whole_words
= content_whole_words_cbox
->state
& C_BOOL
;
536 options
.find_recurs
= recursively_cbox
->state
& C_BOOL
;
537 options
.file_pattern
= file_pattern_cbox
->state
& C_BOOL
;
538 options
.file_case_sens
= file_case_sens_cbox
->state
& C_BOOL
;
539 options
.skip_hidden
= skip_hidden_cbox
->state
& C_BOOL
;
541 *content
= (in_with
->buffer
[0] != '\0') ? g_strdup (in_with
->buffer
) : NULL
;
542 *start_dir
= g_strdup ((in_start
->buffer
[0] != '\0') ? in_start
->buffer
: ".");
543 *pattern
= g_strdup (in_name
->buffer
);
544 if (in_start_dir
!= INPUT_LAST_TEXT
)
545 g_free (in_start_dir
);
546 in_start_dir
= g_strdup (*start_dir
);
548 find_save_options ();
553 destroy_dlg (find_dlg
);
558 #if GLIB_CHECK_VERSION (2, 14, 0)
561 push_directory (const char *dir
)
563 g_queue_push_head (&dir_queue
, (void *) dir
);
569 return (char *) g_queue_pop_tail (&dir_queue
);
572 /* Remove all the items in the stack */
576 g_queue_foreach (&dir_queue
, (GFunc
) g_free
, NULL
);
577 g_queue_clear (&dir_queue
);
580 #else /* GLIB_CHAECK_VERSION */
583 push_directory (const char *dir
)
587 new = g_new (dir_stack
, 1);
588 new->name
= str_unconst (dir
);
589 new->prev
= dir_stack_base
;
590 dir_stack_base
= new;
598 if (dir_stack_base
!= NULL
)
601 name
= dir_stack_base
->name
;
602 next
= dir_stack_base
->prev
;
603 g_free (dir_stack_base
);
604 dir_stack_base
= next
;
610 /* Remove all the items in the stack */
615 while ((dir
= pop_directory ()) != NULL
)
619 #endif /* GLIB_CHAECK_VERSION */
622 insert_file (const char *dir
, const char *file
)
624 char *tmp_name
= NULL
;
625 static char *dirname
= NULL
;
627 while (dir
[0] == PATH_SEP
&& dir
[1] == PATH_SEP
)
632 if (strcmp (old_dir
, dir
))
635 old_dir
= g_strdup (dir
);
636 dirname
= add_to_list (dir
, NULL
);
641 old_dir
= g_strdup (dir
);
642 dirname
= add_to_list (dir
, NULL
);
645 tmp_name
= g_strdup_printf (" %s", file
);
646 add_to_list (tmp_name
, dirname
);
651 find_add_match (const char *dir
, const char *file
)
653 insert_file (dir
, file
);
657 listbox_select_first (find_list
);
658 send_message (&find_list
->widget
, WIDGET_DRAW
, 0);
667 * Returns malloced null-terminated line from file file_fd.
668 * Input is buffered in buf_size long buffer.
669 * Current pos in buf is stored in pos.
670 * n_read - number of read chars.
671 * has_newline - is there newline ?
674 get_line_at (int file_fd
, char *buf
, int buf_size
, int *pos
, int *n_read
, gboolean
* has_newline
)
686 *n_read
= mc_read (file_fd
, buf
, buf_size
);
694 /* skip possible leading zero(s) */
701 if (i
>= buffer_size
- 1)
703 buffer
= g_realloc (buffer
, buffer_size
+= 80);
712 *has_newline
= (ch
!= '\0');
720 static FindProgressStatus
721 check_find_events (Dlg_head
* h
)
727 c
= tty_get_event (&event
, h
->mouse_status
== MOU_REPEAT
, FALSE
);
730 dlg_process_event (h
, c
, &event
);
731 if (h
->ret_value
== B_ENTER
732 || h
->ret_value
== B_CANCEL
|| h
->ret_value
== B_AGAIN
|| h
->ret_value
== B_PANELIZE
)
734 /* dialog terminated */
737 if (!(h
->flags
& DLG_WANT_IDLE
))
739 /* searching suspended */
750 * Search the content_pattern string in the DIRECTORY/FILE.
751 * It will add the found entries to the find listbox.
753 * returns FALSE if do_search should look for another file
754 * TRUE if do_search should exit and proceed to the event handler
757 search_content (Dlg_head
* h
, const char *directory
, const char *filename
)
763 gboolean ret_val
= FALSE
;
765 fname
= concat_dir_and_file (directory
, filename
);
767 if (mc_stat (fname
, &s
) != 0 || !S_ISREG (s
.st_mode
))
773 file_fd
= mc_open (fname
, O_RDONLY
);
779 g_snprintf (buffer
, sizeof (buffer
), _("Grepping in %s"), str_trunc (filename
, FIND2_X_USE
));
781 status_update (buffer
);
784 tty_enable_interrupt_key ();
785 tty_got_interrupt ();
791 gboolean has_newline
;
793 gboolean found
= FALSE
;
795 char result
[BUF_MEDIUM
];
799 /* We've been previously suspended, start from the previous position */
805 && (p
= get_line_at (file_fd
, buffer
, sizeof (buffer
),
806 &pos
, &n_read
, &has_newline
)) != NULL
)
808 if (!found
/* Search in binary line once */
809 && mc_search_run (search_content_handle
,
810 (const void *) p
, 0, strlen (p
), &found_len
))
812 g_snprintf (result
, sizeof (result
), "%d:%s", line
, filename
);
813 find_add_match (directory
, result
);
818 if (found
&& options
.content_first_hit
)
827 if ((line
& 0xff) == 0)
829 FindProgressStatus res
;
830 res
= check_find_events (h
);
850 tty_disable_interrupt_key ();
856 do_search (struct Dlg_head
*h
)
858 static struct dirent
*dp
= NULL
;
859 static DIR *dirp
= NULL
;
860 static char *directory
= NULL
;
861 struct stat tmp_stat
;
863 static int subdirs_left
= 0;
865 unsigned long count
; /* Number of files displayed */
868 { /* someone forces me to close dirp */
880 search_content_handle
= mc_search_new (content_pattern
, -1);
881 if (search_content_handle
)
883 search_content_handle
->search_type
=
884 options
.content_regexp
? MC_SEARCH_T_REGEX
: MC_SEARCH_T_NORMAL
;
885 search_content_handle
->is_case_sensitive
= options
.content_case_sens
;
886 search_content_handle
->whole_words
= options
.content_whole_words
;
887 search_content_handle
->is_all_charsets
= options
.content_all_charsets
;
889 search_file_handle
= mc_search_new (find_pattern
, -1);
890 search_file_handle
->search_type
= options
.file_pattern
? MC_SEARCH_T_GLOB
: MC_SEARCH_T_REGEX
;
891 search_file_handle
->is_case_sensitive
= options
.file_case_sens
;
892 search_file_handle
->is_all_charsets
= options
.file_all_charsets
;
893 search_file_handle
->is_entire_line
= options
.file_pattern
;
910 tty_setcolor (REVERSE_COLOR
);
913 char *temp_dir
= NULL
;
916 tmp
= pop_directory ();
920 status_update (_("Finished"));
922 mc_search_free (search_file_handle
);
923 search_file_handle
= NULL
;
924 mc_search_free (search_content_handle
);
925 search_content_handle
= NULL
;
929 if ((find_ignore_dirs
== NULL
) || (find_ignore_dirs
[0] == '\0'))
932 temp_dir
= g_strdup_printf (":%s:", tmp
);
933 found
= strstr (find_ignore_dirs
, temp_dir
) != 0;
947 char buffer
[BUF_SMALL
];
949 g_snprintf (buffer
, sizeof (buffer
), _("Searching %s"),
950 str_trunc (directory
, FIND2_X_USE
));
951 status_update (buffer
);
953 /* mc_stat should not be called after mc_opendir
954 because vfs_s_opendir modifies the st_nlink
956 if (!mc_stat (directory
, &tmp_stat
))
957 subdirs_left
= tmp_stat
.st_nlink
- 2;
961 dirp
= mc_opendir (directory
);
962 } /* while (!dirp) */
964 /* skip invalid filenames */
965 while ((dp
= mc_readdir (dirp
)) != NULL
&& !str_is_valid_string (dp
->d_name
))
969 if (strcmp (dp
->d_name
, ".") == 0 || strcmp (dp
->d_name
, "..") == 0)
971 dp
= mc_readdir (dirp
);
972 /* skip invalid filenames */
973 while (dp
!= NULL
&& !str_is_valid_string (dp
->d_name
))
974 dp
= mc_readdir (dirp
);
976 mc_search_free (search_file_handle
);
977 search_file_handle
= NULL
;
978 mc_search_free (search_content_handle
);
979 search_content_handle
= NULL
;
983 if (!(options
.skip_hidden
&& (dp
->d_name
[0] == '.')))
987 if ((subdirs_left
!= 0) && options
.find_recurs
&& (directory
!= NULL
))
988 { /* Can directory be NULL ? */
989 char *tmp_name
= concat_dir_and_file (directory
, dp
->d_name
);
990 if (!mc_lstat (tmp_name
, &tmp_stat
) && S_ISDIR (tmp_stat
.st_mode
))
992 push_directory (tmp_name
);
999 search_ok
= mc_search_run (search_file_handle
, dp
->d_name
,
1000 0, strlen (dp
->d_name
), &bytes_found
);
1004 if (content_pattern
== NULL
)
1005 find_add_match (directory
, dp
->d_name
);
1006 else if (search_content (h
, directory
, dp
->d_name
))
1008 mc_search_free (search_file_handle
);
1009 search_file_handle
= NULL
;
1010 mc_search_free (search_content_handle
);
1011 search_content_handle
= NULL
;
1017 /* skip invalid filenames */
1018 while ((dp
= mc_readdir (dirp
)) != NULL
&& !str_is_valid_string (dp
->d_name
))
1021 /* Displays the nice dot */
1025 /* For nice updating */
1026 const char rotating_dash
[] = "|/-\\";
1030 pos
= (pos
+ 1) % 4;
1031 tty_setcolor (DLG_NORMALC (h
));
1032 dlg_move (h
, FIND2_Y
- 7, FIND2_X
- 4);
1033 tty_print_char (rotating_dash
[pos
]);
1038 goto do_search_begin
;
1040 mc_search_free (search_file_handle
);
1041 search_file_handle
= NULL
;
1042 mc_search_free (search_content_handle
);
1043 search_content_handle
= NULL
;
1048 init_find_vars (void)
1054 /* Remove all the items in the stack */
1059 make_fullname (const char *dirname
, const char *filename
)
1062 if (strcmp (dirname
, ".") == 0 || strcmp (dirname
, "." PATH_SEP_STR
) == 0)
1063 return g_strdup (filename
);
1064 if (strncmp (dirname
, "." PATH_SEP_STR
, 2) == 0)
1065 return concat_dir_and_file (dirname
+ 2, filename
);
1066 return concat_dir_and_file (dirname
, filename
);
1070 find_do_view_edit (int unparsed_view
, int edit
, char *dir
, char *file
)
1072 char *fullname
= NULL
;
1073 const char *filename
= NULL
;
1076 if (content_pattern
!= NULL
)
1078 filename
= strchr (file
+ 4, ':') + 1;
1079 line
= atoi (file
+ 4);
1083 filename
= file
+ 4;
1087 fullname
= make_fullname (dir
, filename
);
1089 do_edit_at_line (fullname
, line
);
1091 view_file_at_line (fullname
, unparsed_view
, use_internal_view
, line
);
1096 view_edit_currently_selected_file (int unparsed_view
, int edit
)
1101 listbox_get_current (find_list
, &text
, (void **) &dir
);
1103 if ((text
== NULL
) || (dir
== NULL
))
1104 return MSG_NOT_HANDLED
;
1106 find_do_view_edit (unparsed_view
, edit
, dir
, text
);
1111 find_callback (struct Dlg_head
*h
, Widget
* sender
, dlg_msg_t msg
, int parm
, void *data
)
1116 if (parm
== KEY_F (3) || parm
== KEY_F (13))
1118 int unparsed_view
= (parm
== KEY_F (13));
1119 return view_edit_currently_selected_file (unparsed_view
, 0);
1121 if (parm
== KEY_F (4))
1123 return view_edit_currently_selected_file (0, 1);
1125 return MSG_NOT_HANDLED
;
1132 return default_dlg_callback (h
, sender
, msg
, parm
, data
);
1136 /* Handles the Stop/Start button in the find window */
1138 start_stop (int button
)
1143 set_idle_proc (find_dlg
, running
);
1144 is_start
= !is_start
;
1146 status_update (is_start
? _("Stopped") : _("Searching"));
1147 button_set_text (stop_button
, fbuts
[is_start
? 1 : 0].text
);
1152 /* Handle view command, when invoked as a button */
1154 find_do_view_file (int button
)
1158 view_edit_currently_selected_file (0, 0);
1162 /* Handle edit command, when invoked as a button */
1164 find_do_edit_file (int button
)
1168 view_edit_currently_selected_file (0, 1);
1176 static gboolean i18n_flag
= FALSE
;
1180 int i
= sizeof (fbuts
) / sizeof (fbuts
[0]);
1183 fbuts
[i
].text
= _(fbuts
[i
].text
);
1184 fbuts
[i
].len
= str_term_width1 (fbuts
[i
].text
) + 3;
1187 fbuts
[2].len
+= 2; /* DEFPUSH_BUTTON */
1190 #endif /* ENABLE_NLS */
1193 * Dynamically place buttons centered within current window size
1196 int l0
= max (fbuts
[0].len
, fbuts
[1].len
);
1197 int l1
= fbuts
[2].len
+ fbuts
[3].len
+ l0
+ fbuts
[4].len
;
1198 int l2
= fbuts
[5].len
+ fbuts
[6].len
+ fbuts
[7].len
;
1201 /* Check, if both button rows fit within FIND2_X */
1202 FIND2_X
= max (l1
+ 9, COLS
- 16);
1203 FIND2_X
= max (l2
+ 8, FIND2_X
);
1205 /* compute amount of space between buttons for each row */
1206 r1
= (FIND2_X
- 4 - l1
) % 5;
1207 l1
= (FIND2_X
- 4 - l1
) / 5;
1208 r2
= (FIND2_X
- 4 - l2
) % 4;
1209 l2
= (FIND2_X
- 4 - l2
) / 4;
1211 /* ...and finally, place buttons */
1212 fbuts
[2].x
= 2 + r1
/ 2 + l1
;
1213 fbuts
[3].x
= fbuts
[2].x
+ fbuts
[2].len
+ l1
;
1214 fbuts
[0].x
= fbuts
[3].x
+ fbuts
[3].len
+ l1
;
1215 fbuts
[4].x
= fbuts
[0].x
+ l0
+ l1
;
1216 fbuts
[5].x
= 2 + r2
/ 2 + l2
;
1217 fbuts
[6].x
= fbuts
[5].x
+ fbuts
[5].len
+ l2
;
1218 fbuts
[7].x
= fbuts
[6].x
+ fbuts
[6].len
+ l2
;
1222 create_dlg (0, 0, FIND2_Y
, FIND2_X
, dialog_colors
, find_callback
,
1223 "[Find File]", _("Find File"), DLG_CENTER
| DLG_REVERSE
);
1225 add_widget (find_dlg
,
1226 button_new (FIND2_Y
- 3, fbuts
[7].x
, B_VIEW
, NORMAL_BUTTON
,
1227 fbuts
[7].text
, find_do_edit_file
));
1228 add_widget (find_dlg
,
1229 button_new (FIND2_Y
- 3, fbuts
[6].x
, B_VIEW
, NORMAL_BUTTON
,
1230 fbuts
[6].text
, find_do_view_file
));
1231 add_widget (find_dlg
,
1232 button_new (FIND2_Y
- 3, fbuts
[5].x
, B_PANELIZE
, NORMAL_BUTTON
, fbuts
[5].text
, 0));
1234 add_widget (find_dlg
,
1235 button_new (FIND2_Y
- 4, fbuts
[4].x
, B_CANCEL
, NORMAL_BUTTON
, fbuts
[4].text
, 0));
1237 button_new (FIND2_Y
- 4, fbuts
[0].x
, B_STOP
, NORMAL_BUTTON
, fbuts
[0].text
, start_stop
);
1238 add_widget (find_dlg
, stop_button
);
1239 add_widget (find_dlg
,
1240 button_new (FIND2_Y
- 4, fbuts
[3].x
, B_AGAIN
, NORMAL_BUTTON
, fbuts
[3].text
, 0));
1241 add_widget (find_dlg
,
1242 button_new (FIND2_Y
- 4, fbuts
[2].x
, B_ENTER
, DEFPUSH_BUTTON
, fbuts
[2].text
, 0));
1244 status_label
= label_new (FIND2_Y
- 7, 4, _("Searching"));
1245 add_widget (find_dlg
, status_label
);
1247 found_num_label
= label_new (FIND2_Y
- 6, 4, "");
1248 add_widget (find_dlg
, found_num_label
);
1250 find_list
= listbox_new (2, 2, FIND2_Y
- 10, FIND2_X
- 4, FALSE
, NULL
);
1251 add_widget (find_dlg
, find_list
);
1258 set_idle_proc (find_dlg
, 1);
1259 return run_dlg (find_dlg
);
1265 set_idle_proc (find_dlg
, 0);
1266 destroy_dlg (find_dlg
);
1270 find_file (const char *start_dir
, const char *pattern
, const char *content
,
1271 char **dirname
, char **filename
)
1273 int return_value
= 0;
1274 char *dir_tmp
= NULL
, *file_tmp
= NULL
;
1278 /* FIXME: Need to cleanup this, this ought to be passed non-globaly */
1279 find_pattern
= str_unconst (pattern
);
1280 content_pattern
= (content
!= NULL
&& str_is_valid_string (content
))
1281 ? g_strdup (content
) : NULL
;
1284 push_directory (start_dir
);
1286 return_value
= run_process ();
1288 /* Remove all the items in the stack */
1291 get_list_info (&file_tmp
, &dir_tmp
);
1294 *dirname
= g_strdup (dir_tmp
);
1296 *filename
= g_strdup (file_tmp
);
1298 if (return_value
== B_PANELIZE
&& *filename
)
1300 int status
, link_to_dir
, stale_link
;
1305 dir_list
*list
= ¤t_panel
->dir
;
1308 for (i
= 0, entry
= find_list
->list
; entry
!= NULL
; i
++, entry
= g_list_next (entry
))
1310 const char *lc_filename
= NULL
;
1311 WLEntry
*le
= (WLEntry
*) entry
->data
;
1313 if ((le
->text
== NULL
) || (le
->data
== NULL
))
1316 if (content_pattern
!= NULL
)
1317 lc_filename
= strchr (le
->text
+ 4, ':') + 1;
1319 lc_filename
= le
->text
+ 4;
1321 name
= make_fullname (le
->data
, lc_filename
);
1322 status
= handle_path (list
, name
, &st
, next_free
, &link_to_dir
, &stale_link
);
1334 /* don't add files more than once to the panel */
1335 if (content_pattern
!= NULL
&& next_free
> 0
1336 && strcmp (list
->list
[next_free
- 1].fname
, name
) == 0)
1342 if (!next_free
) /* first turn i.e clean old list */
1343 panel_clean_dir (current_panel
);
1344 list
->list
[next_free
].fnamelen
= strlen (name
);
1345 list
->list
[next_free
].fname
= name
;
1346 list
->list
[next_free
].f
.marked
= 0;
1347 list
->list
[next_free
].f
.link_to_dir
= link_to_dir
;
1348 list
->list
[next_free
].f
.stale_link
= stale_link
;
1349 list
->list
[next_free
].f
.dir_size_computed
= 0;
1350 list
->list
[next_free
].st
= st
;
1351 list
->list
[next_free
].sort_key
= NULL
;
1352 list
->list
[next_free
].second_sort_key
= NULL
;
1354 if (!(next_free
& 15))
1360 current_panel
->count
= next_free
;
1361 current_panel
->is_panelized
= 1;
1363 if (start_dir
[0] == PATH_SEP
)
1366 strcpy (current_panel
->cwd
, PATH_SEP_STR
);
1367 ret
= chdir (PATH_SEP_STR
);
1372 g_free (content_pattern
);
1374 do_search (NULL
); /* force do_search to release resources */
1378 return return_value
;
1384 char *start_dir
= NULL
, *pattern
= NULL
, *content
= NULL
;
1385 char *filename
= NULL
, *dirname
= NULL
;
1387 gboolean dir_and_file_set
;
1389 while (find_parameters (&start_dir
, &pattern
, &content
))
1391 if (pattern
[0] == '\0')
1392 break; /* nothing search */
1394 dirname
= filename
= NULL
;
1396 v
= find_file (start_dir
, pattern
, content
, &dirname
, &filename
);
1401 if (dirname
|| filename
)
1405 do_cd (dirname
, cd_exact
);
1407 try_to_select (current_panel
, filename
+ (content
?
1408 (strchr (filename
+ 4, ':') -
1409 filename
+ 1) : 4));
1412 do_cd (filename
, cd_exact
);
1413 select_item (current_panel
);
1421 dir_and_file_set
= dirname
&& filename
;
1428 if (v
== B_PANELIZE
)
1430 if (dir_and_file_set
)
1432 try_to_select (current_panel
, NULL
);
1433 panel_re_sort (current_panel
);
1434 try_to_select (current_panel
, NULL
);