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/strutil.h"
43 #include "lib/widget.h"
44 #include "lib/vfs/mc-vfs/vfs.h"
45 #include "lib/util.h" /* canonicalize_pathname() */
47 #include "src/setup.h" /* verbose */
48 #include "src/history.h" /* MC_HISTORY_SHARED_SEARCH */
49 #include "src/main.h" /* do_cd */
52 #include "cmd.h" /* view_file_at_line */
53 #include "midnight.h" /* current_panel */
55 #include "layout.h" /* mc_refresh() */
59 /*** global variables ****************************************************************************/
61 /*** file scope macro definitions ****************************************************************/
63 /* Size of the find window */
64 #define FIND2_Y (LINES - 4)
66 #define FIND2_X_USE (FIND2_X - 20)
68 /*** file scope type declarations ****************************************************************/
70 /* A couple of extra messages we need */
87 /* find file options */
90 /* file name options */
91 gboolean file_case_sens
;
92 gboolean file_pattern
;
95 gboolean file_all_charsets
;
97 /* file content options */
99 gboolean content_case_sens
;
100 gboolean content_regexp
;
101 gboolean content_first_hit
;
102 gboolean content_whole_words
;
103 gboolean content_all_charsets
;
104 } find_file_options_t
;
106 /*** file scope variables ************************************************************************/
108 /* Parsed ignore dirs */
109 static char **find_ignore_dirs
= NULL
;
111 /* Size of the find parameters window */
113 static int FIND_Y
= 17;
115 static int FIND_Y
= 16;
117 static int FIND_X
= 68;
119 static int FIND2_X
= 64;
121 /* static variables to remember find parameters */
122 static WInput
*in_start
; /* Start path */
123 static WInput
*in_name
; /* Filename */
124 static WInput
*in_with
; /* Text */
125 static WLabel
*content_label
; /* 'Content:' label */
126 static WCheck
*file_case_sens_cbox
; /* "case sensitive" checkbox */
127 static WCheck
*file_pattern_cbox
; /* File name is glob or regexp */
128 static WCheck
*recursively_cbox
;
129 static WCheck
*skip_hidden_cbox
;
130 static WCheck
*content_use_cbox
; /* Take into account the Content field */
131 static WCheck
*content_case_sens_cbox
; /* "case sensitive" checkbox */
132 static WCheck
*content_regexp_cbox
; /* "find regular expression" checkbox */
133 static WCheck
*content_first_hit_cbox
; /* "First hit" checkbox" */
134 static WCheck
*content_whole_words_cbox
; /* "whole words" checkbox */
136 static WCheck
*file_all_charsets_cbox
;
137 static WCheck
*content_all_charsets_cbox
;
140 static gboolean running
= FALSE
; /* nice flag */
141 static char *find_pattern
= NULL
; /* Pattern to search */
142 static char *content_pattern
= NULL
; /* pattern to search inside files; if
143 content_regexp_flag is true, it contains the
144 regex pattern, else the search string. */
145 static unsigned long matches
; /* Number of matches */
146 static gboolean is_start
= FALSE
; /* Status of the start/stop toggle button */
147 static char *old_dir
= NULL
;
149 /* Where did we stop */
151 static int last_line
;
154 static Dlg_head
*find_dlg
; /* The dialog */
155 static WButton
*stop_button
; /* pointer to the stop button */
156 static WLabel
*status_label
; /* Finished, Searching etc. */
157 static WLabel
*found_num_label
; /* Number of found items */
158 static WListbox
*find_list
; /* Listbox with the file list */
160 /* This keeps track of the directory stack */
161 #if GLIB_CHECK_VERSION (2, 14, 0)
162 static GQueue dir_queue
= G_QUEUE_INIT
;
164 typedef struct dir_stack
167 struct dir_stack
*prev
;
170 static dir_stack
*dir_stack_base
= 0;
171 #endif /* GLIB_CHECK_VERSION */
177 int len
; /* length including space and brackets */
181 {N_("&Suspend"), 11, 29},
182 {N_("Con&tinue"), 12, 29},
183 {N_("&Chdir"), 11, 3},
184 {N_("&Again"), 9, 17},
185 {N_("&Quit"), 8, 43},
186 {N_("Pane&lize"), 12, 3},
187 {N_("&View - F3"), 13, 20},
188 {N_("&Edit - F4"), 13, 38}
192 static find_file_options_t options
= {
193 TRUE
, TRUE
, TRUE
, FALSE
, FALSE
,
194 FALSE
, TRUE
, FALSE
, FALSE
, FALSE
, FALSE
197 static char *in_start_dir
= INPUT_LAST_TEXT
;
199 static mc_search_t
*search_file_handle
= NULL
;
200 static mc_search_t
*search_content_handle
= NULL
;
202 /*** file scope functions ************************************************************************/
205 find_load_options (void)
207 static gboolean loaded
= FALSE
;
215 /* Back compatibility: try load old parameter at first */
216 ignore_dirs
= mc_config_get_string (mc_main_config
, "Misc", "find_ignore_dirs", "");
217 if (ignore_dirs
[0] != '\0')
219 find_ignore_dirs
= g_strsplit (ignore_dirs
, ":", -1);
220 mc_config_set_string (mc_main_config
, "FindFile", "ignore_dirs", ignore_dirs
);
222 g_free (ignore_dirs
);
223 mc_config_del_key (mc_main_config
, "Misc", "find_ignore_dirs");
225 /* Then load new parameters */
226 ignore_dirs
= mc_config_get_string (mc_main_config
, "FindFile", "ignore_dirs", "");
227 if (ignore_dirs
[0] != '\0')
229 g_strfreev (find_ignore_dirs
);
230 find_ignore_dirs
= g_strsplit (ignore_dirs
, ":", -1);
232 g_free (ignore_dirs
);
234 if (find_ignore_dirs
!= NULL
)
236 /* Values like '/foo::/bar: produce holes in list.
237 Find and remove them */
238 size_t r
= 0, w
= 0; /* read and write iterators */
240 for (; find_ignore_dirs
[r
] != NULL
; r
++)
242 if (find_ignore_dirs
[r
][0] == '\0')
244 /* empty entry -- skip it */
245 g_free (find_ignore_dirs
[r
]);
246 find_ignore_dirs
[r
] = NULL
;
252 /* copy entry to the previous free array cell */
253 find_ignore_dirs
[w
] = find_ignore_dirs
[r
];
254 find_ignore_dirs
[r
] = NULL
;
257 canonicalize_pathname (find_ignore_dirs
[w
]);
258 if (find_ignore_dirs
[w
][0] != '\0')
262 g_free (find_ignore_dirs
[w
]);
263 find_ignore_dirs
[w
] = NULL
;
267 if (find_ignore_dirs
[0] == NULL
)
269 g_strfreev (find_ignore_dirs
);
270 find_ignore_dirs
= NULL
;
274 options
.file_case_sens
=
275 mc_config_get_bool (mc_main_config
, "FindFile", "file_case_sens", TRUE
);
276 options
.file_pattern
=
277 mc_config_get_bool (mc_main_config
, "FindFile", "file_shell_pattern", TRUE
);
278 options
.find_recurs
= mc_config_get_bool (mc_main_config
, "FindFile", "file_find_recurs", TRUE
);
279 options
.skip_hidden
=
280 mc_config_get_bool (mc_main_config
, "FindFile", "file_skip_hidden", FALSE
);
281 options
.file_all_charsets
=
282 mc_config_get_bool (mc_main_config
, "FindFile", "file_all_charsets", FALSE
);
283 options
.content_use
= mc_config_get_bool (mc_main_config
, "FindFile", "content_use", FALSE
);
284 options
.content_case_sens
=
285 mc_config_get_bool (mc_main_config
, "FindFile", "content_case_sens", TRUE
);
286 options
.content_regexp
=
287 mc_config_get_bool (mc_main_config
, "FindFile", "content_regexp", FALSE
);
288 options
.content_first_hit
=
289 mc_config_get_bool (mc_main_config
, "FindFile", "content_first_hit", FALSE
);
290 options
.content_whole_words
=
291 mc_config_get_bool (mc_main_config
, "FindFile", "content_whole_words", FALSE
);
292 options
.content_all_charsets
=
293 mc_config_get_bool (mc_main_config
, "FindFile", "content_all_charsets", FALSE
);
296 /* --------------------------------------------------------------------------------------------- */
299 find_save_options (void)
301 mc_config_set_bool (mc_main_config
, "FindFile", "file_case_sens", options
.file_case_sens
);
302 mc_config_set_bool (mc_main_config
, "FindFile", "file_shell_pattern", options
.file_pattern
);
303 mc_config_set_bool (mc_main_config
, "FindFile", "file_find_recurs", options
.find_recurs
);
304 mc_config_set_bool (mc_main_config
, "FindFile", "file_skip_hidden", options
.skip_hidden
);
305 mc_config_set_bool (mc_main_config
, "FindFile", "file_all_charsets", options
.file_all_charsets
);
306 mc_config_set_bool (mc_main_config
, "FindFile", "content_use", options
.content_use
);
307 mc_config_set_bool (mc_main_config
, "FindFile", "content_case_sens", options
.content_case_sens
);
308 mc_config_set_bool (mc_main_config
, "FindFile", "content_regexp", options
.content_regexp
);
309 mc_config_set_bool (mc_main_config
, "FindFile", "content_first_hit", options
.content_first_hit
);
310 mc_config_set_bool (mc_main_config
, "FindFile", "content_whole_words",
311 options
.content_whole_words
);
312 mc_config_set_bool (mc_main_config
, "FindFile", "content_all_charsets",
313 options
.content_all_charsets
);
316 /* --------------------------------------------------------------------------------------------- */
319 add_to_list (const char *text
, void *data
)
321 return listbox_add_item (find_list
, LISTBOX_APPEND_AT_END
, 0, text
, data
);
324 /* --------------------------------------------------------------------------------------------- */
327 stop_idle (void *data
)
329 set_idle_proc (data
, 0);
332 /* --------------------------------------------------------------------------------------------- */
335 status_update (const char *text
)
337 label_set_text (status_label
, text
);
340 /* --------------------------------------------------------------------------------------------- */
343 found_num_update (void)
345 char buffer
[BUF_TINY
];
346 g_snprintf (buffer
, sizeof (buffer
), _("Found: %ld"), matches
);
347 label_set_text (found_num_label
, buffer
);
350 /* --------------------------------------------------------------------------------------------- */
353 get_list_info (char **file
, char **dir
)
355 listbox_get_current (find_list
, file
, (void **) dir
);
358 /* --------------------------------------------------------------------------------------------- */
359 /** check regular expression */
362 find_check_regexp (const char *r
)
365 gboolean regexp_ok
= FALSE
;
367 search
= mc_search_new (r
, -1);
371 search
->search_type
= MC_SEARCH_T_REGEX
;
372 regexp_ok
= mc_search_prepare (search
);
373 mc_search_free (search
);
379 /* --------------------------------------------------------------------------------------------- */
381 * Callback for the parameter dialog.
382 * Validate regex, prevent closing the dialog if it's invalid.
386 find_parm_callback (Dlg_head
* h
, Widget
* sender
, dlg_msg_t msg
, int parm
, void *data
)
391 if (sender
== (Widget
*) content_use_cbox
)
393 gboolean disable
= !(content_use_cbox
->state
& C_BOOL
);
395 widget_disable (content_label
->widget
, disable
);
396 send_message ((Widget
*) content_label
, WIDGET_DRAW
, 0);
397 widget_disable (in_with
->widget
, disable
);
398 send_message ((Widget
*) in_with
, WIDGET_DRAW
, 0);
399 widget_disable (content_first_hit_cbox
->widget
, disable
);
400 send_message ((Widget
*) content_first_hit_cbox
, WIDGET_DRAW
, 0);
401 widget_disable (content_regexp_cbox
->widget
, disable
);
402 send_message ((Widget
*) content_regexp_cbox
, WIDGET_DRAW
, 0);
403 widget_disable (content_case_sens_cbox
->widget
, disable
);
404 send_message ((Widget
*) content_case_sens_cbox
, WIDGET_DRAW
, 0);
406 widget_disable (content_all_charsets_cbox
->widget
, disable
);
407 send_message ((Widget
*) content_all_charsets_cbox
, WIDGET_DRAW
, 0);
409 widget_disable (content_whole_words_cbox
->widget
, disable
);
410 send_message ((Widget
*) content_whole_words_cbox
, WIDGET_DRAW
, 0);
415 return MSG_NOT_HANDLED
;
419 if (h
->ret_value
!= B_ENTER
)
422 /* check filename regexp */
423 if (!(file_pattern_cbox
->state
& C_BOOL
)
424 && (in_name
->buffer
[0] != '\0') && !find_check_regexp (in_name
->buffer
))
426 h
->state
= DLG_ACTIVE
; /* Don't stop the dialog */
427 message (D_ERROR
, MSG_ERROR
, _("Malformed regular expression"));
428 dlg_select_widget (in_name
);
432 /* check content regexp */
433 if ((content_use_cbox
->state
& C_BOOL
) && (content_regexp_cbox
->state
& C_BOOL
)
434 && (in_with
->buffer
[0] != '\0') && !find_check_regexp (in_with
->buffer
))
436 h
->state
= DLG_ACTIVE
; /* Don't stop the dialog */
437 message (D_ERROR
, MSG_ERROR
, _("Malformed regular expression"));
438 dlg_select_widget (in_with
);
445 return default_dlg_callback (h
, sender
, msg
, parm
, data
);
449 /* --------------------------------------------------------------------------------------------- */
451 * find_parameters: gets information from the user
453 * If the return value is TRUE, then the following holds:
455 * START_DIR and PATTERN are pointers to char * and upon return they
456 * contain the information provided by the user.
458 * CONTENT holds a strdup of the contents specified by the user if he
459 * asked for them or 0 if not (note, this is different from the
460 * behavior for the other two parameters.
465 find_parameters (char **start_dir
, char **pattern
, char **content
)
467 gboolean return_value
;
470 const char *file_case_label
= N_("Cas&e sensitive");
471 const char *file_pattern_label
= N_("&Using shell patterns");
472 const char *file_recurs_label
= N_("&Find recursively");
473 const char *file_skip_hidden_label
= N_("S&kip hidden");
475 const char *file_all_charsets_label
= N_("&All charsets");
479 const char *content_use_label
= N_("Sea&rch for content");
480 const char *content_case_label
= N_("Case sens&itive");
481 const char *content_regexp_label
= N_("Re&gular expression");
482 const char *content_first_hit_label
= N_("Fir&st hit");
483 const char *content_whole_words_label
= N_("&Whole words");
485 const char *content_all_charsets_label
= N_("A&ll charsets");
488 const char *buts
[] = { N_("&OK"), N_("&Cancel"), N_("&Tree") };
497 int i
= sizeof (buts
) / sizeof (buts
[0]);
499 buts
[i
] = _(buts
[i
]);
501 file_case_label
= _(file_case_label
);
502 file_pattern_label
= _(file_pattern_label
);
503 file_recurs_label
= _(file_recurs_label
);
504 file_skip_hidden_label
= _(file_skip_hidden_label
);
506 file_all_charsets_label
= _(file_all_charsets_label
);
507 content_all_charsets_label
= _(content_all_charsets_label
);
509 content_use_label
= _(content_use_label
);
510 content_case_label
= _(content_case_label
);
511 content_regexp_label
= _(content_regexp_label
);
512 content_first_hit_label
= _(content_first_hit_label
);
513 content_whole_words_label
= _(content_whole_words_label
);
515 #endif /* ENABLE_NLS */
517 b0
= str_term_width1 (buts
[0]) + 6; /* default button */
518 b1
= str_term_width1 (buts
[1]) + 4;
519 b2
= str_term_width1 (buts
[2]) + 4;
521 find_load_options ();
523 if (in_start_dir
== NULL
)
524 in_start_dir
= g_strdup (".");
526 disable
= !options
.content_use
;
529 create_dlg (TRUE
, 0, 0, FIND_Y
, FIND_X
, dialog_colors
,
530 find_parm_callback
, "[Find File]", _("Find File"), DLG_CENTER
| DLG_REVERSE
);
532 add_widget (find_dlg
,
533 button_new (FIND_Y
- 3, FIND_X
* 3 / 4 - b1
/ 2, B_CANCEL
, NORMAL_BUTTON
, buts
[1],
535 add_widget (find_dlg
,
536 button_new (FIND_Y
- 3, FIND_X
/ 4 - b0
/ 2, B_ENTER
, DEFPUSH_BUTTON
, buts
[0], 0));
538 cbox_position
= FIND_Y
- 5;
540 content_first_hit_cbox
=
541 check_new (cbox_position
--, FIND_X
/ 2 + 1, options
.content_first_hit
,
542 content_first_hit_label
);
543 widget_disable (content_first_hit_cbox
->widget
, disable
);
544 add_widget (find_dlg
, content_first_hit_cbox
);
546 content_whole_words_cbox
=
547 check_new (cbox_position
--, FIND_X
/ 2 + 1, options
.content_whole_words
,
548 content_whole_words_label
);
549 widget_disable (content_whole_words_cbox
->widget
, disable
);
550 add_widget (find_dlg
, content_whole_words_cbox
);
553 content_all_charsets_cbox
= check_new (cbox_position
--, FIND_X
/ 2 + 1,
554 options
.content_all_charsets
,
555 content_all_charsets_label
);
556 widget_disable (content_all_charsets_cbox
->widget
, disable
);
557 add_widget (find_dlg
, content_all_charsets_cbox
);
560 content_case_sens_cbox
=
561 check_new (cbox_position
--, FIND_X
/ 2 + 1, options
.content_case_sens
, content_case_label
);
562 widget_disable (content_case_sens_cbox
->widget
, disable
);
563 add_widget (find_dlg
, content_case_sens_cbox
);
565 content_regexp_cbox
=
566 check_new (cbox_position
--, FIND_X
/ 2 + 1, options
.content_regexp
, content_regexp_label
);
567 widget_disable (content_regexp_cbox
->widget
, disable
);
568 add_widget (find_dlg
, content_regexp_cbox
);
570 cbox_position
= FIND_Y
- 6;
572 skip_hidden_cbox
= check_new (cbox_position
--, 3, options
.skip_hidden
, file_skip_hidden_label
);
573 add_widget (find_dlg
, skip_hidden_cbox
);
576 file_all_charsets_cbox
=
577 check_new (cbox_position
--, 3, options
.file_all_charsets
, file_all_charsets_label
);
578 add_widget (find_dlg
, file_all_charsets_cbox
);
581 file_case_sens_cbox
= check_new (cbox_position
--, 3, options
.file_case_sens
, file_case_label
);
582 add_widget (find_dlg
, file_case_sens_cbox
);
584 file_pattern_cbox
= check_new (cbox_position
--, 3, options
.file_pattern
, file_pattern_label
);
585 add_widget (find_dlg
, file_pattern_cbox
);
587 recursively_cbox
= check_new (cbox_position
, 3, options
.find_recurs
, file_recurs_label
);
588 add_widget (find_dlg
, recursively_cbox
);
590 /* This checkbox is located in the second column */
592 check_new (cbox_position
, FIND_X
/ 2 + 1, options
.content_use
, content_use_label
);
593 add_widget (find_dlg
, content_use_cbox
);
596 input_new (6, FIND_X
/ 2 + 1, input_get_default_colors (), FIND_X
/ 2 - 4, INPUT_LAST_TEXT
,
597 MC_HISTORY_SHARED_SEARCH
, INPUT_COMPLETE_DEFAULT
);
598 widget_disable (in_with
->widget
, disable
);
599 add_widget (find_dlg
, in_with
);
601 content_label
= label_new (5, FIND_X
/ 2 + 1, _("Content:"));
602 widget_disable (content_label
->widget
, disable
);
603 add_widget (find_dlg
, content_label
);
605 in_name
= input_new (6, 3, input_get_default_colors (),
606 FIND_X
/ 2 - 4, INPUT_LAST_TEXT
, "name", INPUT_COMPLETE_DEFAULT
);
607 add_widget (find_dlg
, in_name
);
608 add_widget (find_dlg
, label_new (5, 3, _("File name:")));
610 add_widget (find_dlg
, button_new (3, FIND_X
- b2
- 2, B_TREE
, NORMAL_BUTTON
, buts
[2], 0));
612 in_start
= input_new (3, 3, input_get_default_colors (),
613 FIND_X
- b2
- 6, in_start_dir
, "start", INPUT_COMPLETE_DEFAULT
);
614 add_widget (find_dlg
, in_start
);
615 add_widget (find_dlg
, label_new (2, 3, _("Start at:")));
618 dlg_select_widget (in_name
);
620 switch (run_dlg (find_dlg
))
623 return_value
= FALSE
;
628 const char *temp_dir
= in_start
->buffer
;
630 if ((temp_dir
[0] == '\0') || ((temp_dir
[0] == '.') && (temp_dir
[1] == '\0')))
631 temp_dir
= current_panel
->cwd
;
633 if (in_start_dir
!= INPUT_LAST_TEXT
)
634 g_free (in_start_dir
);
635 in_start_dir
= tree_box (temp_dir
);
636 if (in_start_dir
== NULL
)
637 in_start_dir
= g_strdup (temp_dir
);
639 input_assign_text (in_start
, in_start_dir
);
641 /* Warning: Dreadful goto */
647 options
.file_all_charsets
= file_all_charsets_cbox
->state
& C_BOOL
;
648 options
.content_all_charsets
= content_all_charsets_cbox
->state
& C_BOOL
;
650 options
.content_use
= content_use_cbox
->state
& C_BOOL
;
651 options
.content_case_sens
= content_case_sens_cbox
->state
& C_BOOL
;
652 options
.content_regexp
= content_regexp_cbox
->state
& C_BOOL
;
653 options
.content_first_hit
= content_first_hit_cbox
->state
& C_BOOL
;
654 options
.content_whole_words
= content_whole_words_cbox
->state
& C_BOOL
;
655 options
.find_recurs
= recursively_cbox
->state
& C_BOOL
;
656 options
.file_pattern
= file_pattern_cbox
->state
& C_BOOL
;
657 options
.file_case_sens
= file_case_sens_cbox
->state
& C_BOOL
;
658 options
.skip_hidden
= skip_hidden_cbox
->state
& C_BOOL
;
660 *content
= (options
.content_use
&& in_with
->buffer
[0] != '\0')
661 ? g_strdup (in_with
->buffer
) : NULL
;
662 *start_dir
= in_start
->buffer
[0] != '\0' ? in_start
->buffer
: (char *) ".";
663 *pattern
= g_strdup (in_name
->buffer
);
664 if (in_start_dir
!= INPUT_LAST_TEXT
)
665 g_free (in_start_dir
);
666 in_start_dir
= g_strdup (*start_dir
);
667 if ((*start_dir
)[0] == '.' && (*start_dir
)[1] == '\0')
668 *start_dir
= g_strdup (current_panel
->cwd
);
669 else if (g_path_is_absolute (*start_dir
))
670 *start_dir
= g_strdup (*start_dir
);
672 *start_dir
= g_build_filename (current_panel
->cwd
, *start_dir
, (char *) NULL
);
674 canonicalize_pathname (*start_dir
);
676 find_save_options ();
681 destroy_dlg (find_dlg
);
686 /* --------------------------------------------------------------------------------------------- */
688 #if GLIB_CHECK_VERSION (2, 14, 0)
690 push_directory (const char *dir
)
692 g_queue_push_head (&dir_queue
, (void *) dir
);
695 /* --------------------------------------------------------------------------------------------- */
700 return (char *) g_queue_pop_tail (&dir_queue
);
703 /* --------------------------------------------------------------------------------------------- */
704 /** Remove all the items from the stack */
709 g_queue_foreach (&dir_queue
, (GFunc
) g_free
, NULL
);
710 g_queue_clear (&dir_queue
);
713 /* --------------------------------------------------------------------------------------------- */
715 #else /* GLIB_CHECK_VERSION */
717 push_directory (const char *dir
)
721 new = g_new (dir_stack
, 1);
722 new->name
= (char *) dir
;
723 new->prev
= dir_stack_base
;
724 dir_stack_base
= new;
727 /* --------------------------------------------------------------------------------------------- */
734 if (dir_stack_base
!= NULL
)
737 name
= dir_stack_base
->name
;
738 next
= dir_stack_base
->prev
;
739 g_free (dir_stack_base
);
740 dir_stack_base
= next
;
746 /* --------------------------------------------------------------------------------------------- */
747 /** Remove all the items from the stack */
753 while ((dir
= pop_directory ()) != NULL
)
756 #endif /* GLIB_CHECK_VERSION */
758 /* --------------------------------------------------------------------------------------------- */
761 insert_file (const char *dir
, const char *file
)
763 char *tmp_name
= NULL
;
764 static char *dirname
= NULL
;
766 while (dir
[0] == PATH_SEP
&& dir
[1] == PATH_SEP
)
771 if (strcmp (old_dir
, dir
))
774 old_dir
= g_strdup (dir
);
775 dirname
= add_to_list (dir
, NULL
);
780 old_dir
= g_strdup (dir
);
781 dirname
= add_to_list (dir
, NULL
);
784 tmp_name
= g_strdup_printf (" %s", file
);
785 add_to_list (tmp_name
, dirname
);
789 /* --------------------------------------------------------------------------------------------- */
792 find_add_match (const char *dir
, const char *file
)
794 insert_file (dir
, file
);
798 listbox_select_first (find_list
);
799 send_message (&find_list
->widget
, WIDGET_DRAW
, 0);
805 /* --------------------------------------------------------------------------------------------- */
809 * Returns malloced null-terminated line from file file_fd.
810 * Input is buffered in buf_size long buffer.
811 * Current pos in buf is stored in pos.
812 * n_read - number of read chars.
813 * has_newline - is there newline ?
817 get_line_at (int file_fd
, char *buf
, int buf_size
, int *pos
, int *n_read
, gboolean
* has_newline
)
829 *n_read
= mc_read (file_fd
, buf
, buf_size
);
837 /* skip possible leading zero(s) */
843 if (i
>= buffer_size
- 1)
844 buffer
= g_realloc (buffer
, buffer_size
+= 80);
853 *has_newline
= (ch
!= '\0');
861 /* --------------------------------------------------------------------------------------------- */
863 static FindProgressStatus
864 check_find_events (Dlg_head
* h
)
870 c
= tty_get_event (&event
, h
->mouse_status
== MOU_REPEAT
, FALSE
);
873 dlg_process_event (h
, c
, &event
);
874 if (h
->ret_value
== B_ENTER
875 || h
->ret_value
== B_CANCEL
|| h
->ret_value
== B_AGAIN
|| h
->ret_value
== B_PANELIZE
)
877 /* dialog terminated */
880 if (!(h
->flags
& DLG_WANT_IDLE
))
882 /* searching suspended */
890 /* --------------------------------------------------------------------------------------------- */
894 * Search the content_pattern string in the DIRECTORY/FILE.
895 * It will add the found entries to the find listbox.
897 * returns FALSE if do_search should look for another file
898 * TRUE if do_search should exit and proceed to the event handler
902 search_content (Dlg_head
* h
, const char *directory
, const char *filename
)
908 gboolean ret_val
= FALSE
;
910 fname
= concat_dir_and_file (directory
, filename
);
912 if (mc_stat (fname
, &s
) != 0 || !S_ISREG (s
.st_mode
))
918 file_fd
= mc_open (fname
, O_RDONLY
);
924 g_snprintf (buffer
, sizeof (buffer
), _("Grepping in %s"), str_trunc (filename
, FIND2_X_USE
));
926 status_update (buffer
);
929 tty_enable_interrupt_key ();
930 tty_got_interrupt ();
936 gboolean has_newline
;
938 gboolean found
= FALSE
;
940 char result
[BUF_MEDIUM
];
944 /* We've been previously suspended, start from the previous position */
950 && (p
= get_line_at (file_fd
, buffer
, sizeof (buffer
),
951 &pos
, &n_read
, &has_newline
)) != NULL
)
953 if (!found
/* Search in binary line once */
954 && mc_search_run (search_content_handle
,
955 (const void *) p
, 0, strlen (p
), &found_len
))
957 g_snprintf (result
, sizeof (result
), "%d:%s", line
, filename
);
958 find_add_match (directory
, result
);
963 if (found
&& options
.content_first_hit
)
972 if ((line
& 0xff) == 0)
974 FindProgressStatus res
;
975 res
= check_find_events (h
);
994 tty_disable_interrupt_key ();
999 /* --------------------------------------------------------------------------------------------- */
1002 If dir is absolute, this means we're within dir and searching file here.
1003 If dir is relative, this means we're going to add dir to the directory stack.
1006 find_ignore_dir_search (const char *dir
)
1008 if (find_ignore_dirs
!= NULL
)
1010 const size_t dlen
= strlen (dir
);
1011 const unsigned char dabs
= g_path_is_absolute (dir
) ? 1 : 0;
1015 for (ignore_dir
= find_ignore_dirs
; *ignore_dir
!= NULL
; ignore_dir
++)
1017 const size_t ilen
= strlen (*ignore_dir
);
1018 const unsigned char iabs
= g_path_is_absolute (*ignore_dir
) ? 2 : 0;
1020 /* ignore dir is too long -- skip it */
1024 /* handle absolute and relative paths */
1025 switch (iabs
| dabs
)
1027 case 0: /* both paths are relative */
1028 case 3: /* both paths are abolute */
1029 /* if ignore dir is not a path of dir -- skip it */
1030 if (strncmp (dir
, *ignore_dir
, ilen
) == 0)
1032 /* be sure that ignore dir is not a part of dir like:
1033 ignore dir is "h", dir is "home" */
1034 if (dir
[ilen
] == '\0' || dir
[ilen
] == PATH_SEP
)
1038 case 1: /* dir is absolute, ignore_dir is relative */
1042 d
= strstr (dir
, *ignore_dir
);
1043 if (d
!= NULL
&& d
[-1] == PATH_SEP
&& (d
[ilen
] == '\0' || d
[ilen
] == PATH_SEP
))
1047 case 2: /* dir is relative, ignore_dir is absolute */
1048 /* FIXME: skip this case */
1050 default: /* this cannot occurs */
1059 /* --------------------------------------------------------------------------------------------- */
1062 find_rotate_dash (const Dlg_head
* h
, gboolean finish
)
1064 static const char rotating_dash
[] = "|/-\\";
1065 static unsigned int pos
= 0;
1069 pos
= (pos
+ 1) % 4;
1070 tty_setcolor (h
->color
[DLG_COLOR_NORMAL
]);
1071 dlg_move (h
, FIND2_Y
- 7, FIND2_X
- 4);
1072 tty_print_char (finish
? ' ' : rotating_dash
[pos
]);
1077 /* --------------------------------------------------------------------------------------------- */
1080 do_search (Dlg_head
* h
)
1082 static struct dirent
*dp
= NULL
;
1083 static DIR *dirp
= NULL
;
1084 static char *directory
= NULL
;
1085 struct stat tmp_stat
;
1086 static int subdirs_left
= 0;
1088 unsigned short count
;
1091 { /* someone forces me to close dirp */
1103 for (count
= 0; count
< 32; count
++)
1113 while (dirp
== NULL
)
1117 tty_setcolor (REVERSE_COLOR
);
1121 tmp
= pop_directory ();
1125 status_update (_("Finished"));
1126 find_rotate_dash (h
, TRUE
);
1131 /* handle absolute ignore dirs here */
1132 if (!find_ignore_dir_search (tmp
))
1143 char buffer
[BUF_SMALL
];
1145 g_snprintf (buffer
, sizeof (buffer
), _("Searching %s"),
1146 str_trunc (directory
, FIND2_X_USE
));
1147 status_update (buffer
);
1149 /* mc_stat should not be called after mc_opendir
1150 because vfs_s_opendir modifies the st_nlink
1152 if (mc_stat (directory
, &tmp_stat
) == 0)
1153 subdirs_left
= tmp_stat
.st_nlink
- 2;
1157 dirp
= mc_opendir (directory
);
1158 } /* while (!dirp) */
1160 /* skip invalid filenames */
1161 while ((dp
= mc_readdir (dirp
)) != NULL
&& !str_is_valid_string (dp
->d_name
))
1165 if (strcmp (dp
->d_name
, ".") == 0 || strcmp (dp
->d_name
, "..") == 0)
1167 /* skip invalid filenames */
1168 while ((dp
= mc_readdir (dirp
)) != NULL
&& !str_is_valid_string (dp
->d_name
))
1174 if (!(options
.skip_hidden
&& (dp
->d_name
[0] == '.')))
1178 if ((subdirs_left
!= 0) && options
.find_recurs
&& (directory
!= NULL
))
1179 { /* Can directory be NULL ? */
1180 /* handle relative ignore dirs here */
1181 if (!find_ignore_dir_search (dp
->d_name
))
1185 tmp_name
= g_build_filename (directory
, dp
->d_name
, (char *) NULL
);
1187 if (mc_lstat (tmp_name
, &tmp_stat
) == 0 && S_ISDIR (tmp_stat
.st_mode
))
1189 push_directory (tmp_name
);
1197 search_ok
= mc_search_run (search_file_handle
, dp
->d_name
,
1198 0, strlen (dp
->d_name
), &bytes_found
);
1202 if (content_pattern
== NULL
)
1203 find_add_match (directory
, dp
->d_name
);
1204 else if (search_content (h
, directory
, dp
->d_name
))
1209 /* skip invalid filenames */
1210 while ((dp
= mc_readdir (dirp
)) != NULL
&& !str_is_valid_string (dp
->d_name
))
1214 find_rotate_dash (h
, FALSE
);
1219 /* --------------------------------------------------------------------------------------------- */
1222 init_find_vars (void)
1228 /* Remove all the items from the stack */
1232 /* --------------------------------------------------------------------------------------------- */
1235 make_fullname (const char *dirname
, const char *filename
)
1238 if (strcmp (dirname
, ".") == 0 || strcmp (dirname
, "." PATH_SEP_STR
) == 0)
1239 return g_strdup (filename
);
1240 if (strncmp (dirname
, "." PATH_SEP_STR
, 2) == 0)
1241 return concat_dir_and_file (dirname
+ 2, filename
);
1242 return concat_dir_and_file (dirname
, filename
);
1245 /* --------------------------------------------------------------------------------------------- */
1248 find_do_view_edit (int unparsed_view
, int edit
, char *dir
, char *file
)
1250 char *fullname
= NULL
;
1251 const char *filename
= NULL
;
1254 if (content_pattern
!= NULL
)
1256 filename
= strchr (file
+ 4, ':') + 1;
1257 line
= atoi (file
+ 4);
1261 filename
= file
+ 4;
1265 fullname
= make_fullname (dir
, filename
);
1267 do_edit_at_line (fullname
, use_internal_edit
, line
);
1269 view_file_at_line (fullname
, unparsed_view
, use_internal_view
, line
);
1273 /* --------------------------------------------------------------------------------------------- */
1276 view_edit_currently_selected_file (int unparsed_view
, int edit
)
1281 listbox_get_current (find_list
, &text
, (void **) &dir
);
1283 if ((text
== NULL
) || (dir
== NULL
))
1284 return MSG_NOT_HANDLED
;
1286 find_do_view_edit (unparsed_view
, edit
, dir
, text
);
1290 /* --------------------------------------------------------------------------------------------- */
1293 find_callback (Dlg_head
* h
, Widget
* sender
, dlg_msg_t msg
, int parm
, void *data
)
1298 if (parm
== KEY_F (3) || parm
== KEY_F (13))
1300 int unparsed_view
= (parm
== KEY_F (13));
1301 return view_edit_currently_selected_file (unparsed_view
, 0);
1303 if (parm
== KEY_F (4))
1305 return view_edit_currently_selected_file (0, 1);
1307 return MSG_NOT_HANDLED
;
1314 return default_dlg_callback (h
, sender
, msg
, parm
, data
);
1318 /* --------------------------------------------------------------------------------------------- */
1319 /** Handles the Stop/Start button in the find window */
1322 start_stop (WButton
* button
, int action
)
1328 set_idle_proc (find_dlg
, running
);
1329 is_start
= !is_start
;
1331 status_update (is_start
? _("Stopped") : _("Searching"));
1332 button_set_text (stop_button
, fbuts
[is_start
? 1 : 0].text
);
1337 /* --------------------------------------------------------------------------------------------- */
1338 /** Handle view command, when invoked as a button */
1341 find_do_view_file (WButton
* button
, int action
)
1346 view_edit_currently_selected_file (0, 0);
1350 /* --------------------------------------------------------------------------------------------- */
1351 /** Handle edit command, when invoked as a button */
1354 find_do_edit_file (WButton
* button
, int action
)
1359 view_edit_currently_selected_file (0, 1);
1363 /* --------------------------------------------------------------------------------------------- */
1369 static gboolean i18n_flag
= FALSE
;
1373 int i
= sizeof (fbuts
) / sizeof (fbuts
[0]);
1376 fbuts
[i
].text
= _(fbuts
[i
].text
);
1377 fbuts
[i
].len
= str_term_width1 (fbuts
[i
].text
) + 3;
1380 fbuts
[2].len
+= 2; /* DEFPUSH_BUTTON */
1383 #endif /* ENABLE_NLS */
1386 * Dynamically place buttons centered within current window size
1389 int l0
= max (fbuts
[0].len
, fbuts
[1].len
);
1390 int l1
= fbuts
[2].len
+ fbuts
[3].len
+ l0
+ fbuts
[4].len
;
1391 int l2
= fbuts
[5].len
+ fbuts
[6].len
+ fbuts
[7].len
;
1394 /* Check, if both button rows fit within FIND2_X */
1395 FIND2_X
= max (l1
+ 9, COLS
- 16);
1396 FIND2_X
= max (l2
+ 8, FIND2_X
);
1398 /* compute amount of space between buttons for each row */
1399 r1
= (FIND2_X
- 4 - l1
) % 5;
1400 l1
= (FIND2_X
- 4 - l1
) / 5;
1401 r2
= (FIND2_X
- 4 - l2
) % 4;
1402 l2
= (FIND2_X
- 4 - l2
) / 4;
1404 /* ...and finally, place buttons */
1405 fbuts
[2].x
= 2 + r1
/ 2 + l1
;
1406 fbuts
[3].x
= fbuts
[2].x
+ fbuts
[2].len
+ l1
;
1407 fbuts
[0].x
= fbuts
[3].x
+ fbuts
[3].len
+ l1
;
1408 fbuts
[4].x
= fbuts
[0].x
+ l0
+ l1
;
1409 fbuts
[5].x
= 2 + r2
/ 2 + l2
;
1410 fbuts
[6].x
= fbuts
[5].x
+ fbuts
[5].len
+ l2
;
1411 fbuts
[7].x
= fbuts
[6].x
+ fbuts
[6].len
+ l2
;
1415 create_dlg (TRUE
, 0, 0, FIND2_Y
, FIND2_X
, dialog_colors
, find_callback
,
1416 "[Find File]", _("Find File"), DLG_CENTER
| DLG_REVERSE
);
1418 add_widget (find_dlg
,
1419 button_new (FIND2_Y
- 3, fbuts
[7].x
, B_VIEW
, NORMAL_BUTTON
,
1420 fbuts
[7].text
, find_do_edit_file
));
1421 add_widget (find_dlg
,
1422 button_new (FIND2_Y
- 3, fbuts
[6].x
, B_VIEW
, NORMAL_BUTTON
,
1423 fbuts
[6].text
, find_do_view_file
));
1424 add_widget (find_dlg
,
1425 button_new (FIND2_Y
- 3, fbuts
[5].x
, B_PANELIZE
, NORMAL_BUTTON
, fbuts
[5].text
,
1428 add_widget (find_dlg
,
1429 button_new (FIND2_Y
- 4, fbuts
[4].x
, B_CANCEL
, NORMAL_BUTTON
, fbuts
[4].text
, NULL
));
1431 button_new (FIND2_Y
- 4, fbuts
[0].x
, B_STOP
, NORMAL_BUTTON
, fbuts
[0].text
, start_stop
);
1432 add_widget (find_dlg
, stop_button
);
1433 add_widget (find_dlg
,
1434 button_new (FIND2_Y
- 4, fbuts
[3].x
, B_AGAIN
, NORMAL_BUTTON
, fbuts
[3].text
, NULL
));
1435 add_widget (find_dlg
,
1436 button_new (FIND2_Y
- 4, fbuts
[2].x
, B_ENTER
, DEFPUSH_BUTTON
, fbuts
[2].text
, NULL
));
1438 status_label
= label_new (FIND2_Y
- 7, 4, _("Searching"));
1439 add_widget (find_dlg
, status_label
);
1441 found_num_label
= label_new (FIND2_Y
- 6, 4, "");
1442 add_widget (find_dlg
, found_num_label
);
1444 find_list
= listbox_new (2, 2, FIND2_Y
- 10, FIND2_X
- 4, FALSE
, NULL
);
1445 add_widget (find_dlg
, find_list
);
1448 /* --------------------------------------------------------------------------------------------- */
1455 search_content_handle
= mc_search_new (content_pattern
, -1);
1456 if (search_content_handle
)
1458 search_content_handle
->search_type
=
1459 options
.content_regexp
? MC_SEARCH_T_REGEX
: MC_SEARCH_T_NORMAL
;
1460 search_content_handle
->is_case_sensitive
= options
.content_case_sens
;
1461 search_content_handle
->whole_words
= options
.content_whole_words
;
1462 search_content_handle
->is_all_charsets
= options
.content_all_charsets
;
1464 search_file_handle
= mc_search_new (find_pattern
, -1);
1465 search_file_handle
->search_type
= options
.file_pattern
? MC_SEARCH_T_GLOB
: MC_SEARCH_T_REGEX
;
1466 search_file_handle
->is_case_sensitive
= options
.file_case_sens
;
1467 search_file_handle
->is_all_charsets
= options
.file_all_charsets
;
1468 search_file_handle
->is_entire_line
= options
.file_pattern
;
1471 set_idle_proc (find_dlg
, 1);
1472 ret
= run_dlg (find_dlg
);
1474 mc_search_free (search_file_handle
);
1475 search_file_handle
= NULL
;
1476 mc_search_free (search_content_handle
);
1477 search_content_handle
= NULL
;
1482 /* --------------------------------------------------------------------------------------------- */
1487 set_idle_proc (find_dlg
, 0);
1488 destroy_dlg (find_dlg
);
1491 /* --------------------------------------------------------------------------------------------- */
1494 find_file (const char *start_dir
, const char *pattern
, const char *content
,
1495 char **dirname
, char **filename
)
1497 int return_value
= 0;
1498 char *dir_tmp
= NULL
, *file_tmp
= NULL
;
1502 /* FIXME: Need to cleanup this, this ought to be passed non-globaly */
1503 find_pattern
= (char *) pattern
;
1505 content_pattern
= NULL
;
1506 if (options
.content_use
&& content
!= NULL
&& str_is_valid_string (content
))
1507 content_pattern
= g_strdup (content
);
1510 push_directory (start_dir
);
1512 return_value
= run_process ();
1514 /* Remove all the items from the stack */
1517 get_list_info (&file_tmp
, &dir_tmp
);
1520 *dirname
= g_strdup (dir_tmp
);
1522 *filename
= g_strdup (file_tmp
);
1524 if (return_value
== B_PANELIZE
&& *filename
)
1526 int status
, link_to_dir
, stale_link
;
1531 dir_list
*list
= ¤t_panel
->dir
;
1534 for (i
= 0, entry
= find_list
->list
; entry
!= NULL
; i
++, entry
= g_list_next (entry
))
1536 const char *lc_filename
= NULL
;
1537 WLEntry
*le
= (WLEntry
*) entry
->data
;
1539 if ((le
->text
== NULL
) || (le
->data
== NULL
))
1542 if (content_pattern
!= NULL
)
1543 lc_filename
= strchr (le
->text
+ 4, ':') + 1;
1545 lc_filename
= le
->text
+ 4;
1547 name
= make_fullname (le
->data
, lc_filename
);
1548 status
= handle_path (list
, name
, &st
, next_free
, &link_to_dir
, &stale_link
);
1560 /* don't add files more than once to the panel */
1561 if (content_pattern
!= NULL
&& next_free
> 0
1562 && strcmp (list
->list
[next_free
- 1].fname
, name
) == 0)
1568 if (!next_free
) /* first turn i.e clean old list */
1569 panel_clean_dir (current_panel
);
1570 list
->list
[next_free
].fnamelen
= strlen (name
);
1571 list
->list
[next_free
].fname
= name
;
1572 list
->list
[next_free
].f
.marked
= 0;
1573 list
->list
[next_free
].f
.link_to_dir
= link_to_dir
;
1574 list
->list
[next_free
].f
.stale_link
= stale_link
;
1575 list
->list
[next_free
].f
.dir_size_computed
= 0;
1576 list
->list
[next_free
].st
= st
;
1577 list
->list
[next_free
].sort_key
= NULL
;
1578 list
->list
[next_free
].second_sort_key
= NULL
;
1580 if (!(next_free
& 15))
1586 current_panel
->count
= next_free
;
1587 current_panel
->is_panelized
= 1;
1589 if (start_dir
[0] == PATH_SEP
)
1592 strcpy (current_panel
->cwd
, PATH_SEP_STR
);
1593 ret
= chdir (PATH_SEP_STR
);
1598 g_free (content_pattern
);
1600 do_search (NULL
); /* force do_search to release resources */
1604 return return_value
;
1607 /* --------------------------------------------------------------------------------------------- */
1608 /*** public functions ****************************************************************************/
1609 /* --------------------------------------------------------------------------------------------- */
1614 char *start_dir
= NULL
, *pattern
= NULL
, *content
= NULL
;
1615 char *filename
= NULL
, *dirname
= NULL
;
1617 gboolean dir_and_file_set
;
1619 while (find_parameters (&start_dir
, &pattern
, &content
))
1621 if (pattern
[0] == '\0')
1622 break; /* nothing search */
1624 dirname
= filename
= NULL
;
1626 v
= find_file (start_dir
, pattern
, content
, &dirname
, &filename
);
1631 if (dirname
!= NULL
)
1633 do_cd (dirname
, cd_exact
);
1634 if (filename
!= NULL
)
1635 try_to_select (current_panel
,
1636 filename
+ (content
!= NULL
1637 ? strchr (filename
+ 4, ':') - filename
+ 1 : 4));
1639 else if (filename
!= NULL
)
1640 do_cd (filename
, cd_exact
);
1648 dir_and_file_set
= (dirname
!= NULL
) && (filename
!= NULL
);
1655 if (v
== B_PANELIZE
)
1657 if (dir_and_file_set
)
1659 try_to_select (current_panel
, NULL
);
1660 panel_re_sort (current_panel
);
1661 try_to_select (current_panel
, NULL
);
1668 /* --------------------------------------------------------------------------------------------- */