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/util.h" /* canonicalize_pathname() */
38 #include "lib/tty/tty.h"
39 #include "lib/tty/key.h"
41 #include "lib/search.h"
42 #include "lib/mcconfig.h"
43 #include "lib/vfs/mc-vfs/vfs.h"
44 #include "lib/strutil.h"
46 #include "lib/widget.h"
48 #include "src/setup.h" /* verbose */
49 #include "src/history.h" /* MC_HISTORY_SHARED_SEARCH */
50 #include "src/main.h" /* do_cd */
53 #include "cmd.h" /* view_file_at_line */
54 #include "midnight.h" /* current_panel */
56 #include "layout.h" /* mc_refresh() */
60 /*** global variables ****************************************************************************/
62 /* List of directories to be ignored, separated by ':' */
63 char **find_ignore_dirs
= NULL
;
65 /*** file scope macro definitions ****************************************************************/
67 /* Size of the find window */
68 #define FIND2_Y (LINES - 4)
70 #define FIND2_X_USE (FIND2_X - 20)
72 /*** file scope type declarations ****************************************************************/
74 /* A couple of extra messages we need */
91 /* find file options */
94 /* file name options */
95 gboolean file_case_sens
;
96 gboolean file_pattern
;
99 gboolean file_all_charsets
;
101 /* file content options */
102 gboolean content_use
;
103 gboolean content_case_sens
;
104 gboolean content_regexp
;
105 gboolean content_first_hit
;
106 gboolean content_whole_words
;
107 gboolean content_all_charsets
;
108 } find_file_options_t
;
110 /*** file scope variables ************************************************************************/
112 /* Size of the find parameters window */
114 static int FIND_Y
= 17;
116 static int FIND_Y
= 16;
118 static int FIND_X
= 68;
120 static int FIND2_X
= 64;
122 /* static variables to remember find parameters */
123 static WInput
*in_start
; /* Start path */
124 static WInput
*in_name
; /* Filename */
125 static WInput
*in_with
; /* Text */
126 static WLabel
*content_label
; /* 'Content:' label */
127 static WCheck
*file_case_sens_cbox
; /* "case sensitive" checkbox */
128 static WCheck
*file_pattern_cbox
; /* File name is glob or regexp */
129 static WCheck
*recursively_cbox
;
130 static WCheck
*skip_hidden_cbox
;
131 static WCheck
*content_use_cbox
; /* Take into account the Content field */
132 static WCheck
*content_case_sens_cbox
; /* "case sensitive" checkbox */
133 static WCheck
*content_regexp_cbox
; /* "find regular expression" checkbox */
134 static WCheck
*content_first_hit_cbox
; /* "First hit" checkbox" */
135 static WCheck
*content_whole_words_cbox
; /* "whole words" checkbox */
137 static WCheck
*file_all_charsets_cbox
;
138 static WCheck
*content_all_charsets_cbox
;
141 static gboolean running
= FALSE
; /* nice flag */
142 static char *find_pattern
= NULL
; /* Pattern to search */
143 static char *content_pattern
= NULL
; /* pattern to search inside files; if
144 content_regexp_flag is true, it contains the
145 regex pattern, else the search string. */
146 static unsigned long matches
; /* Number of matches */
147 static gboolean is_start
= FALSE
; /* Status of the start/stop toggle button */
148 static char *old_dir
= NULL
;
150 /* Where did we stop */
152 static int last_line
;
155 static Dlg_head
*find_dlg
; /* The dialog */
156 static WButton
*stop_button
; /* pointer to the stop button */
157 static WLabel
*status_label
; /* Finished, Searching etc. */
158 static WLabel
*found_num_label
; /* Number of found items */
159 static WListbox
*find_list
; /* Listbox with the file list */
161 /* This keeps track of the directory stack */
162 #if GLIB_CHECK_VERSION (2, 14, 0)
163 static GQueue dir_queue
= G_QUEUE_INIT
;
165 typedef struct dir_stack
168 struct dir_stack
*prev
;
171 static dir_stack
*dir_stack_base
= 0;
172 #endif /* GLIB_CHECK_VERSION */
178 int len
; /* length including space and brackets */
182 {N_("&Suspend"), 11, 29},
183 {N_("Con&tinue"), 12, 29},
184 {N_("&Chdir"), 11, 3},
185 {N_("&Again"), 9, 17},
186 {N_("&Quit"), 8, 43},
187 {N_("Pane&lize"), 12, 3},
188 {N_("&View - F3"), 13, 20},
189 {N_("&Edit - F4"), 13, 38}
193 static find_file_options_t options
= {
194 TRUE
, TRUE
, TRUE
, FALSE
, FALSE
,
195 FALSE
, TRUE
, FALSE
, FALSE
, FALSE
, FALSE
198 static char *in_start_dir
= INPUT_LAST_TEXT
;
200 static mc_search_t
*search_file_handle
= NULL
;
201 static mc_search_t
*search_content_handle
= NULL
;
203 /*** file scope functions ************************************************************************/
206 find_ignore_dirs_cmp (const void *d1
, const void *d2
)
208 return strcmp (*(const char **) d1
, *(const char **) d2
);
211 /* --------------------------------------------------------------------------------------------- */
214 find_load_options (void)
216 static gboolean loaded
= FALSE
;
224 /* Back compatibility: try load old parameter at first */
225 ignore_dirs
= mc_config_get_string (mc_main_config
, "Misc", "find_ignore_dirs", "");
226 if (ignore_dirs
[0] != '\0')
228 find_ignore_dirs
= g_strsplit (ignore_dirs
, ":", -1);
229 mc_config_set_string (mc_main_config
, "FindFile", "ignore_dirs", ignore_dirs
);
231 g_free (ignore_dirs
);
232 mc_config_del_key (mc_main_config
, "Misc", "find_ignore_dirs");
234 /* Then load new parameters */
235 ignore_dirs
= mc_config_get_string (mc_main_config
, "FindFile", "ignore_dirs", "");
236 if (ignore_dirs
[0] != '\0')
238 g_strfreev (find_ignore_dirs
);
239 find_ignore_dirs
= g_strsplit (ignore_dirs
, ":", -1);
241 g_free (ignore_dirs
);
243 if (find_ignore_dirs
!= NULL
)
245 /* Values like '/foo::/bar: produce holes in list.
246 Find and remove them */
247 size_t r
= 0, w
= 0; /* read and write iterators */
249 for (; find_ignore_dirs
[r
] != NULL
; r
++)
251 if (find_ignore_dirs
[r
][0] == '\0')
253 /* empty entry -- skip it */
254 g_free (find_ignore_dirs
[r
]);
255 find_ignore_dirs
[r
] = NULL
;
261 /* copy entry to the previous free array cell */
262 find_ignore_dirs
[w
] = find_ignore_dirs
[r
];
263 find_ignore_dirs
[r
] = NULL
;
266 canonicalize_pathname (find_ignore_dirs
[w
]);
271 if (find_ignore_dirs
[0] != NULL
)
272 qsort (find_ignore_dirs
, g_strv_length (find_ignore_dirs
),
273 sizeof (find_ignore_dirs
[0]), &find_ignore_dirs_cmp
);
276 g_strfreev (find_ignore_dirs
);
277 find_ignore_dirs
= NULL
;
281 options
.file_case_sens
=
282 mc_config_get_bool (mc_main_config
, "FindFile", "file_case_sens", TRUE
);
283 options
.file_pattern
=
284 mc_config_get_bool (mc_main_config
, "FindFile", "file_shell_pattern", TRUE
);
285 options
.find_recurs
= mc_config_get_bool (mc_main_config
, "FindFile", "file_find_recurs", TRUE
);
286 options
.skip_hidden
=
287 mc_config_get_bool (mc_main_config
, "FindFile", "file_skip_hidden", FALSE
);
288 options
.file_all_charsets
=
289 mc_config_get_bool (mc_main_config
, "FindFile", "file_all_charsets", FALSE
);
290 options
.content_use
= mc_config_get_bool (mc_main_config
, "FindFile", "content_use", FALSE
);
291 options
.content_case_sens
=
292 mc_config_get_bool (mc_main_config
, "FindFile", "content_case_sens", TRUE
);
293 options
.content_regexp
=
294 mc_config_get_bool (mc_main_config
, "FindFile", "content_regexp", FALSE
);
295 options
.content_first_hit
=
296 mc_config_get_bool (mc_main_config
, "FindFile", "content_first_hit", FALSE
);
297 options
.content_whole_words
=
298 mc_config_get_bool (mc_main_config
, "FindFile", "content_whole_words", FALSE
);
299 options
.content_all_charsets
=
300 mc_config_get_bool (mc_main_config
, "FindFile", "content_all_charsets", FALSE
);
303 /* --------------------------------------------------------------------------------------------- */
306 find_save_options (void)
308 mc_config_set_bool (mc_main_config
, "FindFile", "file_case_sens", options
.file_case_sens
);
309 mc_config_set_bool (mc_main_config
, "FindFile", "file_shell_pattern", options
.file_pattern
);
310 mc_config_set_bool (mc_main_config
, "FindFile", "file_find_recurs", options
.find_recurs
);
311 mc_config_set_bool (mc_main_config
, "FindFile", "file_skip_hidden", options
.skip_hidden
);
312 mc_config_set_bool (mc_main_config
, "FindFile", "file_all_charsets", options
.file_all_charsets
);
313 mc_config_set_bool (mc_main_config
, "FindFile", "content_use", options
.content_use
);
314 mc_config_set_bool (mc_main_config
, "FindFile", "content_case_sens", options
.content_case_sens
);
315 mc_config_set_bool (mc_main_config
, "FindFile", "content_regexp", options
.content_regexp
);
316 mc_config_set_bool (mc_main_config
, "FindFile", "content_first_hit", options
.content_first_hit
);
317 mc_config_set_bool (mc_main_config
, "FindFile", "content_whole_words",
318 options
.content_whole_words
);
319 mc_config_set_bool (mc_main_config
, "FindFile", "content_all_charsets",
320 options
.content_all_charsets
);
323 /* --------------------------------------------------------------------------------------------- */
326 add_to_list (const char *text
, void *data
)
328 return listbox_add_item (find_list
, LISTBOX_APPEND_AT_END
, 0, text
, data
);
331 /* --------------------------------------------------------------------------------------------- */
334 stop_idle (void *data
)
336 set_idle_proc (data
, 0);
339 /* --------------------------------------------------------------------------------------------- */
342 status_update (const char *text
)
344 label_set_text (status_label
, text
);
347 /* --------------------------------------------------------------------------------------------- */
350 found_num_update (void)
352 char buffer
[BUF_TINY
];
353 g_snprintf (buffer
, sizeof (buffer
), _("Found: %ld"), matches
);
354 label_set_text (found_num_label
, buffer
);
357 /* --------------------------------------------------------------------------------------------- */
360 get_list_info (char **file
, char **dir
)
362 listbox_get_current (find_list
, file
, (void **) dir
);
365 /* --------------------------------------------------------------------------------------------- */
366 /** check regular expression */
369 find_check_regexp (const char *r
)
372 gboolean regexp_ok
= FALSE
;
374 search
= mc_search_new (r
, -1);
378 search
->search_type
= MC_SEARCH_T_REGEX
;
379 regexp_ok
= mc_search_prepare (search
);
380 mc_search_free (search
);
386 /* --------------------------------------------------------------------------------------------- */
388 * Callback for the parameter dialog.
389 * Validate regex, prevent closing the dialog if it's invalid.
393 find_parm_callback (Dlg_head
* h
, Widget
* sender
, dlg_msg_t msg
, int parm
, void *data
)
398 if (sender
== (Widget
*) content_use_cbox
)
400 gboolean disable
= !(content_use_cbox
->state
& C_BOOL
);
402 widget_disable (content_label
->widget
, disable
);
403 send_message ((Widget
*) content_label
, WIDGET_DRAW
, 0);
404 widget_disable (in_with
->widget
, disable
);
405 send_message ((Widget
*) in_with
, WIDGET_DRAW
, 0);
406 widget_disable (content_first_hit_cbox
->widget
, disable
);
407 send_message ((Widget
*) content_first_hit_cbox
, WIDGET_DRAW
, 0);
408 widget_disable (content_regexp_cbox
->widget
, disable
);
409 send_message ((Widget
*) content_regexp_cbox
, WIDGET_DRAW
, 0);
410 widget_disable (content_case_sens_cbox
->widget
, disable
);
411 send_message ((Widget
*) content_case_sens_cbox
, WIDGET_DRAW
, 0);
413 widget_disable (content_all_charsets_cbox
->widget
, disable
);
414 send_message ((Widget
*) content_all_charsets_cbox
, WIDGET_DRAW
, 0);
416 widget_disable (content_whole_words_cbox
->widget
, disable
);
417 send_message ((Widget
*) content_whole_words_cbox
, WIDGET_DRAW
, 0);
422 return MSG_NOT_HANDLED
;
426 if (h
->ret_value
!= B_ENTER
)
429 /* check filename regexp */
430 if (!(file_pattern_cbox
->state
& C_BOOL
)
431 && (in_name
->buffer
[0] != '\0') && !find_check_regexp (in_name
->buffer
))
433 h
->state
= DLG_ACTIVE
; /* Don't stop the dialog */
434 message (D_ERROR
, MSG_ERROR
, _("Malformed regular expression"));
435 dlg_select_widget (in_name
);
439 /* check content regexp */
440 if ((content_use_cbox
->state
& C_BOOL
) && (content_regexp_cbox
->state
& C_BOOL
)
441 && (in_with
->buffer
[0] != '\0') && !find_check_regexp (in_with
->buffer
))
443 h
->state
= DLG_ACTIVE
; /* Don't stop the dialog */
444 message (D_ERROR
, MSG_ERROR
, _("Malformed regular expression"));
445 dlg_select_widget (in_with
);
452 return default_dlg_callback (h
, sender
, msg
, parm
, data
);
456 /* --------------------------------------------------------------------------------------------- */
458 * find_parameters: gets information from the user
460 * If the return value is TRUE, then the following holds:
462 * START_DIR and PATTERN are pointers to char * and upon return they
463 * contain the information provided by the user.
465 * CONTENT holds a strdup of the contents specified by the user if he
466 * asked for them or 0 if not (note, this is different from the
467 * behavior for the other two parameters.
472 find_parameters (char **start_dir
, char **pattern
, char **content
)
474 gboolean return_value
;
477 const char *file_case_label
= N_("Cas&e sensitive");
478 const char *file_pattern_label
= N_("&Using shell patterns");
479 const char *file_recurs_label
= N_("&Find recursively");
480 const char *file_skip_hidden_label
= N_("S&kip hidden");
482 const char *file_all_charsets_label
= N_("&All charsets");
486 const char *content_use_label
= N_("Sea&rch for content");
487 const char *content_case_label
= N_("Case sens&itive");
488 const char *content_regexp_label
= N_("Re&gular expression");
489 const char *content_first_hit_label
= N_("Fir&st hit");
490 const char *content_whole_words_label
= N_("&Whole words");
492 const char *content_all_charsets_label
= N_("A&ll charsets");
495 const char *buts
[] = { N_("&OK"), N_("&Cancel"), N_("&Tree") };
504 int i
= sizeof (buts
) / sizeof (buts
[0]);
506 buts
[i
] = _(buts
[i
]);
508 file_case_label
= _(file_case_label
);
509 file_pattern_label
= _(file_pattern_label
);
510 file_recurs_label
= _(file_recurs_label
);
511 file_skip_hidden_label
= _(file_skip_hidden_label
);
513 file_all_charsets_label
= _(file_all_charsets_label
);
514 content_all_charsets_label
= _(content_all_charsets_label
);
516 content_use_label
= _(content_use_label
);
517 content_case_label
= _(content_case_label
);
518 content_regexp_label
= _(content_regexp_label
);
519 content_first_hit_label
= _(content_first_hit_label
);
520 content_whole_words_label
= _(content_whole_words_label
);
522 #endif /* ENABLE_NLS */
524 b0
= str_term_width1 (buts
[0]) + 6; /* default button */
525 b1
= str_term_width1 (buts
[1]) + 4;
526 b2
= str_term_width1 (buts
[2]) + 4;
528 find_load_options ();
530 if (in_start_dir
== NULL
)
531 in_start_dir
= g_strdup (".");
533 disable
= !options
.content_use
;
536 create_dlg (TRUE
, 0, 0, FIND_Y
, FIND_X
, dialog_colors
,
537 find_parm_callback
, "[Find File]", _("Find File"), DLG_CENTER
| DLG_REVERSE
);
539 add_widget (find_dlg
,
540 button_new (FIND_Y
- 3, FIND_X
* 3 / 4 - b1
/ 2, B_CANCEL
, NORMAL_BUTTON
, buts
[1],
542 add_widget (find_dlg
,
543 button_new (FIND_Y
- 3, FIND_X
/ 4 - b0
/ 2, B_ENTER
, DEFPUSH_BUTTON
, buts
[0], 0));
545 cbox_position
= FIND_Y
- 5;
547 content_first_hit_cbox
=
548 check_new (cbox_position
--, FIND_X
/ 2 + 1, options
.content_first_hit
,
549 content_first_hit_label
);
550 widget_disable (content_first_hit_cbox
->widget
, disable
);
551 add_widget (find_dlg
, content_first_hit_cbox
);
553 content_whole_words_cbox
=
554 check_new (cbox_position
--, FIND_X
/ 2 + 1, options
.content_whole_words
,
555 content_whole_words_label
);
556 widget_disable (content_whole_words_cbox
->widget
, disable
);
557 add_widget (find_dlg
, content_whole_words_cbox
);
560 content_all_charsets_cbox
= check_new (cbox_position
--, FIND_X
/ 2 + 1,
561 options
.content_all_charsets
,
562 content_all_charsets_label
);
563 widget_disable (content_all_charsets_cbox
->widget
, disable
);
564 add_widget (find_dlg
, content_all_charsets_cbox
);
567 content_case_sens_cbox
=
568 check_new (cbox_position
--, FIND_X
/ 2 + 1, options
.content_case_sens
, content_case_label
);
569 widget_disable (content_case_sens_cbox
->widget
, disable
);
570 add_widget (find_dlg
, content_case_sens_cbox
);
572 content_regexp_cbox
=
573 check_new (cbox_position
--, FIND_X
/ 2 + 1, options
.content_regexp
, content_regexp_label
);
574 widget_disable (content_regexp_cbox
->widget
, disable
);
575 add_widget (find_dlg
, content_regexp_cbox
);
577 cbox_position
= FIND_Y
- 6;
579 skip_hidden_cbox
= check_new (cbox_position
--, 3, options
.skip_hidden
, file_skip_hidden_label
);
580 add_widget (find_dlg
, skip_hidden_cbox
);
583 file_all_charsets_cbox
=
584 check_new (cbox_position
--, 3, options
.file_all_charsets
, file_all_charsets_label
);
585 add_widget (find_dlg
, file_all_charsets_cbox
);
588 file_case_sens_cbox
= check_new (cbox_position
--, 3, options
.file_case_sens
, file_case_label
);
589 add_widget (find_dlg
, file_case_sens_cbox
);
591 file_pattern_cbox
= check_new (cbox_position
--, 3, options
.file_pattern
, file_pattern_label
);
592 add_widget (find_dlg
, file_pattern_cbox
);
594 recursively_cbox
= check_new (cbox_position
, 3, options
.find_recurs
, file_recurs_label
);
595 add_widget (find_dlg
, recursively_cbox
);
597 /* This checkbox is located in the second column */
599 check_new (cbox_position
, FIND_X
/ 2 + 1, options
.content_use
, content_use_label
);
600 add_widget (find_dlg
, content_use_cbox
);
603 input_new (6, FIND_X
/ 2 + 1, input_get_default_colors (), FIND_X
/ 2 - 4, INPUT_LAST_TEXT
,
604 MC_HISTORY_SHARED_SEARCH
, INPUT_COMPLETE_DEFAULT
);
605 widget_disable (in_with
->widget
, disable
);
606 add_widget (find_dlg
, in_with
);
608 content_label
= label_new (5, FIND_X
/ 2 + 1, _("Content:"));
609 widget_disable (content_label
->widget
, disable
);
610 add_widget (find_dlg
, content_label
);
612 in_name
= input_new (6, 3, input_get_default_colors (),
613 FIND_X
/ 2 - 4, INPUT_LAST_TEXT
, "name", INPUT_COMPLETE_DEFAULT
);
614 add_widget (find_dlg
, in_name
);
615 add_widget (find_dlg
, label_new (5, 3, _("File name:")));
617 add_widget (find_dlg
, button_new (3, FIND_X
- b2
- 2, B_TREE
, NORMAL_BUTTON
, buts
[2], 0));
619 in_start
= input_new (3, 3, input_get_default_colors (),
620 FIND_X
- b2
- 6, in_start_dir
, "start", INPUT_COMPLETE_DEFAULT
);
621 add_widget (find_dlg
, in_start
);
622 add_widget (find_dlg
, label_new (2, 3, _("Start at:")));
625 dlg_select_widget (in_name
);
627 switch (run_dlg (find_dlg
))
630 return_value
= FALSE
;
635 const char *temp_dir
= in_start
->buffer
;
637 if ((temp_dir
[0] == '\0') || ((temp_dir
[0] == '.') && (temp_dir
[1] == '\0')))
638 temp_dir
= current_panel
->cwd
;
640 if (in_start_dir
!= INPUT_LAST_TEXT
)
641 g_free (in_start_dir
);
642 in_start_dir
= tree_box (temp_dir
);
643 if (in_start_dir
== NULL
)
644 in_start_dir
= g_strdup (temp_dir
);
646 input_assign_text (in_start
, in_start_dir
);
648 /* Warning: Dreadful goto */
654 options
.file_all_charsets
= file_all_charsets_cbox
->state
& C_BOOL
;
655 options
.content_all_charsets
= content_all_charsets_cbox
->state
& C_BOOL
;
657 options
.content_use
= content_use_cbox
->state
& C_BOOL
;
658 options
.content_case_sens
= content_case_sens_cbox
->state
& C_BOOL
;
659 options
.content_regexp
= content_regexp_cbox
->state
& C_BOOL
;
660 options
.content_first_hit
= content_first_hit_cbox
->state
& C_BOOL
;
661 options
.content_whole_words
= content_whole_words_cbox
->state
& C_BOOL
;
662 options
.find_recurs
= recursively_cbox
->state
& C_BOOL
;
663 options
.file_pattern
= file_pattern_cbox
->state
& C_BOOL
;
664 options
.file_case_sens
= file_case_sens_cbox
->state
& C_BOOL
;
665 options
.skip_hidden
= skip_hidden_cbox
->state
& C_BOOL
;
667 *content
= (options
.content_use
&& in_with
->buffer
[0] != '\0')
668 ? g_strdup (in_with
->buffer
) : NULL
;
669 *start_dir
= in_start
->buffer
[0] != '\0' ? in_start
->buffer
: (char *) ".";
670 *pattern
= g_strdup (in_name
->buffer
);
671 if (in_start_dir
!= INPUT_LAST_TEXT
)
672 g_free (in_start_dir
);
673 in_start_dir
= g_strdup (*start_dir
);
674 if ((*start_dir
)[0] == '.' && (*start_dir
)[1] == '\0')
675 *start_dir
= g_strdup (current_panel
->cwd
);
676 else if (g_path_is_absolute (*start_dir
))
677 *start_dir
= g_strdup (*start_dir
);
679 *start_dir
= g_build_filename (current_panel
->cwd
, *start_dir
, (char *) NULL
);
681 canonicalize_pathname (*start_dir
);
683 find_save_options ();
688 destroy_dlg (find_dlg
);
693 /* --------------------------------------------------------------------------------------------- */
695 #if GLIB_CHECK_VERSION (2, 14, 0)
697 push_directory (const char *dir
)
699 g_queue_push_head (&dir_queue
, (void *) dir
);
702 /* --------------------------------------------------------------------------------------------- */
707 return (char *) g_queue_pop_tail (&dir_queue
);
710 /* --------------------------------------------------------------------------------------------- */
711 /** Remove all the items from the stack */
716 g_queue_foreach (&dir_queue
, (GFunc
) g_free
, NULL
);
717 g_queue_clear (&dir_queue
);
720 /* --------------------------------------------------------------------------------------------- */
722 #else /* GLIB_CHECK_VERSION */
724 push_directory (const char *dir
)
728 new = g_new (dir_stack
, 1);
729 new->name
= (char *) dir
;
730 new->prev
= dir_stack_base
;
731 dir_stack_base
= new;
734 /* --------------------------------------------------------------------------------------------- */
741 if (dir_stack_base
!= NULL
)
744 name
= dir_stack_base
->name
;
745 next
= dir_stack_base
->prev
;
746 g_free (dir_stack_base
);
747 dir_stack_base
= next
;
753 /* --------------------------------------------------------------------------------------------- */
754 /** Remove all the items from the stack */
760 while ((dir
= pop_directory ()) != NULL
)
763 #endif /* GLIB_CHECK_VERSION */
765 /* --------------------------------------------------------------------------------------------- */
768 insert_file (const char *dir
, const char *file
)
770 char *tmp_name
= NULL
;
771 static char *dirname
= NULL
;
773 while (dir
[0] == PATH_SEP
&& dir
[1] == PATH_SEP
)
778 if (strcmp (old_dir
, dir
))
781 old_dir
= g_strdup (dir
);
782 dirname
= add_to_list (dir
, NULL
);
787 old_dir
= g_strdup (dir
);
788 dirname
= add_to_list (dir
, NULL
);
791 tmp_name
= g_strdup_printf (" %s", file
);
792 add_to_list (tmp_name
, dirname
);
796 /* --------------------------------------------------------------------------------------------- */
799 find_add_match (const char *dir
, const char *file
)
801 insert_file (dir
, file
);
805 listbox_select_first (find_list
);
806 send_message (&find_list
->widget
, WIDGET_DRAW
, 0);
812 /* --------------------------------------------------------------------------------------------- */
816 * Returns malloced null-terminated line from file file_fd.
817 * Input is buffered in buf_size long buffer.
818 * Current pos in buf is stored in pos.
819 * n_read - number of read chars.
820 * has_newline - is there newline ?
824 get_line_at (int file_fd
, char *buf
, int buf_size
, int *pos
, int *n_read
, gboolean
* has_newline
)
836 *n_read
= mc_read (file_fd
, buf
, buf_size
);
844 /* skip possible leading zero(s) */
850 if (i
>= buffer_size
- 1)
851 buffer
= g_realloc (buffer
, buffer_size
+= 80);
860 *has_newline
= (ch
!= '\0');
868 /* --------------------------------------------------------------------------------------------- */
870 static FindProgressStatus
871 check_find_events (Dlg_head
* h
)
877 c
= tty_get_event (&event
, h
->mouse_status
== MOU_REPEAT
, FALSE
);
880 dlg_process_event (h
, c
, &event
);
881 if (h
->ret_value
== B_ENTER
882 || h
->ret_value
== B_CANCEL
|| h
->ret_value
== B_AGAIN
|| h
->ret_value
== B_PANELIZE
)
884 /* dialog terminated */
887 if (!(h
->flags
& DLG_WANT_IDLE
))
889 /* searching suspended */
897 /* --------------------------------------------------------------------------------------------- */
901 * Search the content_pattern string in the DIRECTORY/FILE.
902 * It will add the found entries to the find listbox.
904 * returns FALSE if do_search should look for another file
905 * TRUE if do_search should exit and proceed to the event handler
909 search_content (Dlg_head
* h
, const char *directory
, const char *filename
)
915 gboolean ret_val
= FALSE
;
917 fname
= concat_dir_and_file (directory
, filename
);
919 if (mc_stat (fname
, &s
) != 0 || !S_ISREG (s
.st_mode
))
925 file_fd
= mc_open (fname
, O_RDONLY
);
931 g_snprintf (buffer
, sizeof (buffer
), _("Grepping in %s"), str_trunc (filename
, FIND2_X_USE
));
933 status_update (buffer
);
936 tty_enable_interrupt_key ();
937 tty_got_interrupt ();
943 gboolean has_newline
;
945 gboolean found
= FALSE
;
947 char result
[BUF_MEDIUM
];
951 /* We've been previously suspended, start from the previous position */
957 && (p
= get_line_at (file_fd
, buffer
, sizeof (buffer
),
958 &pos
, &n_read
, &has_newline
)) != NULL
)
960 if (!found
/* Search in binary line once */
961 && mc_search_run (search_content_handle
,
962 (const void *) p
, 0, strlen (p
), &found_len
))
964 g_snprintf (result
, sizeof (result
), "%d:%s", line
, filename
);
965 find_add_match (directory
, result
);
970 if (found
&& options
.content_first_hit
)
979 if ((line
& 0xff) == 0)
981 FindProgressStatus res
;
982 res
= check_find_events (h
);
1001 tty_disable_interrupt_key ();
1006 /* --------------------------------------------------------------------------------------------- */
1008 static inline gboolean
1009 find_ignore_dir_search (const char *dir
)
1011 if (find_ignore_dirs
!= NULL
)
1013 const size_t dlen
= strlen (dir
);
1016 for (ignore_dir
= find_ignore_dirs
; *ignore_dir
!= NULL
; ignore_dir
++)
1018 const size_t ilen
= strlen (*ignore_dir
);
1021 continue; /* ignore dir is too long -- skip it */
1023 if (strncmp (dir
, *ignore_dir
, ilen
) != 0)
1024 continue; /* strings are different -- skip ignore_dir */
1026 /* be sure than ignore_dir is not a part of dir like:
1027 ignore_dir is "/h", dir is "/home" */
1028 if (dir
[ilen
] == PATH_SEP
|| dir
[ilen
] == '\0')
1036 /* --------------------------------------------------------------------------------------------- */
1039 find_rotate_dash (const Dlg_head
* h
, gboolean finish
)
1041 static const char rotating_dash
[] = "|/-\\";
1042 static unsigned int pos
= 0;
1046 pos
= (pos
+ 1) % 4;
1047 tty_setcolor (h
->color
[DLG_COLOR_NORMAL
]);
1048 dlg_move (h
, FIND2_Y
- 7, FIND2_X
- 4);
1049 tty_print_char (finish
? ' ' : rotating_dash
[pos
]);
1054 /* --------------------------------------------------------------------------------------------- */
1057 do_search (Dlg_head
* h
)
1059 static struct dirent
*dp
= NULL
;
1060 static DIR *dirp
= NULL
;
1061 static char *directory
= NULL
;
1062 struct stat tmp_stat
;
1063 static int subdirs_left
= 0;
1065 unsigned short count
;
1068 { /* someone forces me to close dirp */
1080 for (count
= 0; count
< 32; count
++)
1090 while (dirp
== NULL
)
1094 tty_setcolor (REVERSE_COLOR
);
1098 tmp
= pop_directory ();
1102 status_update (_("Finished"));
1103 find_rotate_dash (h
, TRUE
);
1108 if (!find_ignore_dir_search (tmp
))
1119 char buffer
[BUF_SMALL
];
1121 g_snprintf (buffer
, sizeof (buffer
), _("Searching %s"),
1122 str_trunc (directory
, FIND2_X_USE
));
1123 status_update (buffer
);
1125 /* mc_stat should not be called after mc_opendir
1126 because vfs_s_opendir modifies the st_nlink
1128 if (mc_stat (directory
, &tmp_stat
) == 0)
1129 subdirs_left
= tmp_stat
.st_nlink
- 2;
1133 dirp
= mc_opendir (directory
);
1134 } /* while (!dirp) */
1136 /* skip invalid filenames */
1137 while ((dp
= mc_readdir (dirp
)) != NULL
&& !str_is_valid_string (dp
->d_name
))
1141 if (strcmp (dp
->d_name
, ".") == 0 || strcmp (dp
->d_name
, "..") == 0)
1143 /* skip invalid filenames */
1144 while ((dp
= mc_readdir (dirp
)) != NULL
&& !str_is_valid_string (dp
->d_name
))
1150 if (!(options
.skip_hidden
&& (dp
->d_name
[0] == '.')))
1154 if ((subdirs_left
!= 0) && options
.find_recurs
&& (directory
!= NULL
))
1155 { /* Can directory be NULL ? */
1156 char *tmp_name
= concat_dir_and_file (directory
, dp
->d_name
);
1157 if (mc_lstat (tmp_name
, &tmp_stat
) == 0 && S_ISDIR (tmp_stat
.st_mode
))
1159 push_directory (tmp_name
);
1166 search_ok
= mc_search_run (search_file_handle
, dp
->d_name
,
1167 0, strlen (dp
->d_name
), &bytes_found
);
1171 if (content_pattern
== NULL
)
1172 find_add_match (directory
, dp
->d_name
);
1173 else if (search_content (h
, directory
, dp
->d_name
))
1178 /* skip invalid filenames */
1179 while ((dp
= mc_readdir (dirp
)) != NULL
&& !str_is_valid_string (dp
->d_name
))
1183 find_rotate_dash (h
, FALSE
);
1188 /* --------------------------------------------------------------------------------------------- */
1191 init_find_vars (void)
1197 /* Remove all the items from the stack */
1201 /* --------------------------------------------------------------------------------------------- */
1204 make_fullname (const char *dirname
, const char *filename
)
1207 if (strcmp (dirname
, ".") == 0 || strcmp (dirname
, "." PATH_SEP_STR
) == 0)
1208 return g_strdup (filename
);
1209 if (strncmp (dirname
, "." PATH_SEP_STR
, 2) == 0)
1210 return concat_dir_and_file (dirname
+ 2, filename
);
1211 return concat_dir_and_file (dirname
, filename
);
1214 /* --------------------------------------------------------------------------------------------- */
1217 find_do_view_edit (int unparsed_view
, int edit
, char *dir
, char *file
)
1219 char *fullname
= NULL
;
1220 const char *filename
= NULL
;
1223 if (content_pattern
!= NULL
)
1225 filename
= strchr (file
+ 4, ':') + 1;
1226 line
= atoi (file
+ 4);
1230 filename
= file
+ 4;
1234 fullname
= make_fullname (dir
, filename
);
1236 do_edit_at_line (fullname
, use_internal_edit
, line
);
1238 view_file_at_line (fullname
, unparsed_view
, use_internal_view
, line
);
1242 /* --------------------------------------------------------------------------------------------- */
1245 view_edit_currently_selected_file (int unparsed_view
, int edit
)
1250 listbox_get_current (find_list
, &text
, (void **) &dir
);
1252 if ((text
== NULL
) || (dir
== NULL
))
1253 return MSG_NOT_HANDLED
;
1255 find_do_view_edit (unparsed_view
, edit
, dir
, text
);
1259 /* --------------------------------------------------------------------------------------------- */
1262 find_callback (Dlg_head
* h
, Widget
* sender
, dlg_msg_t msg
, int parm
, void *data
)
1267 if (parm
== KEY_F (3) || parm
== KEY_F (13))
1269 int unparsed_view
= (parm
== KEY_F (13));
1270 return view_edit_currently_selected_file (unparsed_view
, 0);
1272 if (parm
== KEY_F (4))
1274 return view_edit_currently_selected_file (0, 1);
1276 return MSG_NOT_HANDLED
;
1283 return default_dlg_callback (h
, sender
, msg
, parm
, data
);
1287 /* --------------------------------------------------------------------------------------------- */
1288 /** Handles the Stop/Start button in the find window */
1291 start_stop (WButton
* button
, int action
)
1297 set_idle_proc (find_dlg
, running
);
1298 is_start
= !is_start
;
1300 status_update (is_start
? _("Stopped") : _("Searching"));
1301 button_set_text (stop_button
, fbuts
[is_start
? 1 : 0].text
);
1306 /* --------------------------------------------------------------------------------------------- */
1307 /** Handle view command, when invoked as a button */
1310 find_do_view_file (WButton
* button
, int action
)
1315 view_edit_currently_selected_file (0, 0);
1319 /* --------------------------------------------------------------------------------------------- */
1320 /** Handle edit command, when invoked as a button */
1323 find_do_edit_file (WButton
* button
, int action
)
1328 view_edit_currently_selected_file (0, 1);
1332 /* --------------------------------------------------------------------------------------------- */
1338 static gboolean i18n_flag
= FALSE
;
1342 int i
= sizeof (fbuts
) / sizeof (fbuts
[0]);
1345 fbuts
[i
].text
= _(fbuts
[i
].text
);
1346 fbuts
[i
].len
= str_term_width1 (fbuts
[i
].text
) + 3;
1349 fbuts
[2].len
+= 2; /* DEFPUSH_BUTTON */
1352 #endif /* ENABLE_NLS */
1355 * Dynamically place buttons centered within current window size
1358 int l0
= max (fbuts
[0].len
, fbuts
[1].len
);
1359 int l1
= fbuts
[2].len
+ fbuts
[3].len
+ l0
+ fbuts
[4].len
;
1360 int l2
= fbuts
[5].len
+ fbuts
[6].len
+ fbuts
[7].len
;
1363 /* Check, if both button rows fit within FIND2_X */
1364 FIND2_X
= max (l1
+ 9, COLS
- 16);
1365 FIND2_X
= max (l2
+ 8, FIND2_X
);
1367 /* compute amount of space between buttons for each row */
1368 r1
= (FIND2_X
- 4 - l1
) % 5;
1369 l1
= (FIND2_X
- 4 - l1
) / 5;
1370 r2
= (FIND2_X
- 4 - l2
) % 4;
1371 l2
= (FIND2_X
- 4 - l2
) / 4;
1373 /* ...and finally, place buttons */
1374 fbuts
[2].x
= 2 + r1
/ 2 + l1
;
1375 fbuts
[3].x
= fbuts
[2].x
+ fbuts
[2].len
+ l1
;
1376 fbuts
[0].x
= fbuts
[3].x
+ fbuts
[3].len
+ l1
;
1377 fbuts
[4].x
= fbuts
[0].x
+ l0
+ l1
;
1378 fbuts
[5].x
= 2 + r2
/ 2 + l2
;
1379 fbuts
[6].x
= fbuts
[5].x
+ fbuts
[5].len
+ l2
;
1380 fbuts
[7].x
= fbuts
[6].x
+ fbuts
[6].len
+ l2
;
1384 create_dlg (TRUE
, 0, 0, FIND2_Y
, FIND2_X
, dialog_colors
, find_callback
,
1385 "[Find File]", _("Find File"), DLG_CENTER
| DLG_REVERSE
);
1387 add_widget (find_dlg
,
1388 button_new (FIND2_Y
- 3, fbuts
[7].x
, B_VIEW
, NORMAL_BUTTON
,
1389 fbuts
[7].text
, find_do_edit_file
));
1390 add_widget (find_dlg
,
1391 button_new (FIND2_Y
- 3, fbuts
[6].x
, B_VIEW
, NORMAL_BUTTON
,
1392 fbuts
[6].text
, find_do_view_file
));
1393 add_widget (find_dlg
,
1394 button_new (FIND2_Y
- 3, fbuts
[5].x
, B_PANELIZE
, NORMAL_BUTTON
, fbuts
[5].text
,
1397 add_widget (find_dlg
,
1398 button_new (FIND2_Y
- 4, fbuts
[4].x
, B_CANCEL
, NORMAL_BUTTON
, fbuts
[4].text
, NULL
));
1400 button_new (FIND2_Y
- 4, fbuts
[0].x
, B_STOP
, NORMAL_BUTTON
, fbuts
[0].text
, start_stop
);
1401 add_widget (find_dlg
, stop_button
);
1402 add_widget (find_dlg
,
1403 button_new (FIND2_Y
- 4, fbuts
[3].x
, B_AGAIN
, NORMAL_BUTTON
, fbuts
[3].text
, NULL
));
1404 add_widget (find_dlg
,
1405 button_new (FIND2_Y
- 4, fbuts
[2].x
, B_ENTER
, DEFPUSH_BUTTON
, fbuts
[2].text
, NULL
));
1407 status_label
= label_new (FIND2_Y
- 7, 4, _("Searching"));
1408 add_widget (find_dlg
, status_label
);
1410 found_num_label
= label_new (FIND2_Y
- 6, 4, "");
1411 add_widget (find_dlg
, found_num_label
);
1413 find_list
= listbox_new (2, 2, FIND2_Y
- 10, FIND2_X
- 4, FALSE
, NULL
);
1414 add_widget (find_dlg
, find_list
);
1417 /* --------------------------------------------------------------------------------------------- */
1424 search_content_handle
= mc_search_new (content_pattern
, -1);
1425 if (search_content_handle
)
1427 search_content_handle
->search_type
=
1428 options
.content_regexp
? MC_SEARCH_T_REGEX
: MC_SEARCH_T_NORMAL
;
1429 search_content_handle
->is_case_sensitive
= options
.content_case_sens
;
1430 search_content_handle
->whole_words
= options
.content_whole_words
;
1431 search_content_handle
->is_all_charsets
= options
.content_all_charsets
;
1433 search_file_handle
= mc_search_new (find_pattern
, -1);
1434 search_file_handle
->search_type
= options
.file_pattern
? MC_SEARCH_T_GLOB
: MC_SEARCH_T_REGEX
;
1435 search_file_handle
->is_case_sensitive
= options
.file_case_sens
;
1436 search_file_handle
->is_all_charsets
= options
.file_all_charsets
;
1437 search_file_handle
->is_entire_line
= options
.file_pattern
;
1440 set_idle_proc (find_dlg
, 1);
1441 ret
= run_dlg (find_dlg
);
1443 mc_search_free (search_file_handle
);
1444 search_file_handle
= NULL
;
1445 mc_search_free (search_content_handle
);
1446 search_content_handle
= NULL
;
1451 /* --------------------------------------------------------------------------------------------- */
1456 set_idle_proc (find_dlg
, 0);
1457 destroy_dlg (find_dlg
);
1460 /* --------------------------------------------------------------------------------------------- */
1463 find_file (const char *start_dir
, const char *pattern
, const char *content
,
1464 char **dirname
, char **filename
)
1466 int return_value
= 0;
1467 char *dir_tmp
= NULL
, *file_tmp
= NULL
;
1471 /* FIXME: Need to cleanup this, this ought to be passed non-globaly */
1472 find_pattern
= (char *) pattern
;
1474 content_pattern
= NULL
;
1475 if (options
.content_use
&& content
!= NULL
&& str_is_valid_string (content
))
1476 content_pattern
= g_strdup (content
);
1479 push_directory (start_dir
);
1481 return_value
= run_process ();
1483 /* Remove all the items from the stack */
1486 get_list_info (&file_tmp
, &dir_tmp
);
1489 *dirname
= g_strdup (dir_tmp
);
1491 *filename
= g_strdup (file_tmp
);
1493 if (return_value
== B_PANELIZE
&& *filename
)
1495 int status
, link_to_dir
, stale_link
;
1500 dir_list
*list
= ¤t_panel
->dir
;
1503 for (i
= 0, entry
= find_list
->list
; entry
!= NULL
; i
++, entry
= g_list_next (entry
))
1505 const char *lc_filename
= NULL
;
1506 WLEntry
*le
= (WLEntry
*) entry
->data
;
1508 if ((le
->text
== NULL
) || (le
->data
== NULL
))
1511 if (content_pattern
!= NULL
)
1512 lc_filename
= strchr (le
->text
+ 4, ':') + 1;
1514 lc_filename
= le
->text
+ 4;
1516 name
= make_fullname (le
->data
, lc_filename
);
1517 status
= handle_path (list
, name
, &st
, next_free
, &link_to_dir
, &stale_link
);
1529 /* don't add files more than once to the panel */
1530 if (content_pattern
!= NULL
&& next_free
> 0
1531 && strcmp (list
->list
[next_free
- 1].fname
, name
) == 0)
1537 if (!next_free
) /* first turn i.e clean old list */
1538 panel_clean_dir (current_panel
);
1539 list
->list
[next_free
].fnamelen
= strlen (name
);
1540 list
->list
[next_free
].fname
= name
;
1541 list
->list
[next_free
].f
.marked
= 0;
1542 list
->list
[next_free
].f
.link_to_dir
= link_to_dir
;
1543 list
->list
[next_free
].f
.stale_link
= stale_link
;
1544 list
->list
[next_free
].f
.dir_size_computed
= 0;
1545 list
->list
[next_free
].st
= st
;
1546 list
->list
[next_free
].sort_key
= NULL
;
1547 list
->list
[next_free
].second_sort_key
= NULL
;
1549 if (!(next_free
& 15))
1555 current_panel
->count
= next_free
;
1556 current_panel
->is_panelized
= 1;
1558 if (start_dir
[0] == PATH_SEP
)
1561 strcpy (current_panel
->cwd
, PATH_SEP_STR
);
1562 ret
= chdir (PATH_SEP_STR
);
1567 g_free (content_pattern
);
1569 do_search (NULL
); /* force do_search to release resources */
1573 return return_value
;
1576 /* --------------------------------------------------------------------------------------------- */
1577 /*** public functions ****************************************************************************/
1578 /* --------------------------------------------------------------------------------------------- */
1583 char *start_dir
= NULL
, *pattern
= NULL
, *content
= NULL
;
1584 char *filename
= NULL
, *dirname
= NULL
;
1586 gboolean dir_and_file_set
;
1588 while (find_parameters (&start_dir
, &pattern
, &content
))
1590 if (pattern
[0] == '\0')
1591 break; /* nothing search */
1593 dirname
= filename
= NULL
;
1595 v
= find_file (start_dir
, pattern
, content
, &dirname
, &filename
);
1600 if (dirname
!= NULL
)
1602 do_cd (dirname
, cd_exact
);
1603 if (filename
!= NULL
)
1604 try_to_select (current_panel
,
1605 filename
+ (content
!= NULL
1606 ? strchr (filename
+ 4, ':') - filename
+ 1 : 4));
1608 else if (filename
!= NULL
)
1609 do_cd (filename
, cd_exact
);
1617 dir_and_file_set
= (dirname
!= NULL
) && (filename
!= NULL
);
1624 if (v
== B_PANELIZE
)
1626 if (dir_and_file_set
)
1628 try_to_select (current_panel
, NULL
);
1629 panel_re_sort (current_panel
);
1630 try_to_select (current_panel
, NULL
);
1637 /* --------------------------------------------------------------------------------------------- */