2 Find file command for the Midnight Commander
4 Copyright (C) 1995-2020
5 Free Software Foundation, Inc.
9 Slava Zanko <slavazanko@gmail.com>, 2013
10 Andrew Borodin <aborodin@vmail.ru>, 2013
12 This file is part of the Midnight Commander.
14 The Midnight Commander is free software: you can redistribute it
15 and/or modify it under the terms of the GNU General Public License as
16 published by the Free Software Foundation, either version 3 of the License,
17 or (at your option) any later version.
19 The Midnight Commander is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
24 You should have received a copy of the GNU General Public License
25 along with this program. If not, see <http://www.gnu.org/licenses/>.
29 * \brief Source: Find file command
41 #include "lib/global.h"
43 #include "lib/tty/tty.h"
44 #include "lib/tty/key.h"
46 #include "lib/search.h"
47 #include "lib/mcconfig.h"
48 #include "lib/vfs/vfs.h"
49 #include "lib/strutil.h"
50 #include "lib/widget.h"
51 #include "lib/util.h" /* canonicalize_pathname() */
53 #include "src/setup.h" /* verbose */
54 #include "src/history.h" /* MC_HISTORY_SHARED_SEARCH */
57 #include "cmd.h" /* view_file_at_line() */
58 #include "midnight.h" /* current_panel */
64 /*** global variables ****************************************************************************/
66 /*** file scope macro definitions ****************************************************************/
68 #define MAX_REFRESH_INTERVAL (G_USEC_PER_SEC / 20) /* 50 ms */
69 #define MIN_REFRESH_FILE_SIZE (256 * 1024) /* 256 KB */
71 /*** file scope type declarations ****************************************************************/
73 /* A couple of extra messages we need */
90 /* find file options */
93 /* file name options */
94 gboolean file_case_sens
;
95 gboolean file_pattern
;
98 gboolean file_all_charsets
;
100 /* file content options */
101 gboolean content_case_sens
;
102 gboolean content_regexp
;
103 gboolean content_first_hit
;
104 gboolean content_whole_words
;
105 gboolean content_all_charsets
;
107 /* whether use ignore dirs or not */
108 gboolean ignore_dirs_enable
;
109 /* list of directories to be ignored, separated by ':' */
111 } find_file_options_t
;
118 } find_match_location_t
;
120 /*** file scope variables ************************************************************************/
122 /* button callbacks */
123 static int start_stop (WButton
* button
, int action
);
124 static int find_do_view_file (WButton
* button
, int action
);
125 static int find_do_edit_file (WButton
* button
, int action
);
127 /* Parsed ignore dirs */
128 static char **find_ignore_dirs
= NULL
;
130 /* static variables to remember find parameters */
131 static WInput
*in_start
; /* Start path */
132 static WInput
*in_name
; /* Filename */
133 static WInput
*in_with
; /* Text */
134 static WInput
*in_ignore
;
135 static WLabel
*content_label
; /* 'Content:' label */
136 static WCheck
*file_case_sens_cbox
; /* "case sensitive" checkbox */
137 static WCheck
*file_pattern_cbox
; /* File name is glob or regexp */
138 static WCheck
*recursively_cbox
;
139 static WCheck
*skip_hidden_cbox
;
140 static WCheck
*content_case_sens_cbox
; /* "case sensitive" checkbox */
141 static WCheck
*content_regexp_cbox
; /* "find regular expression" checkbox */
142 static WCheck
*content_first_hit_cbox
; /* "First hit" checkbox" */
143 static WCheck
*content_whole_words_cbox
; /* "whole words" checkbox */
145 static WCheck
*file_all_charsets_cbox
;
146 static WCheck
*content_all_charsets_cbox
;
148 static WCheck
*ignore_dirs_cbox
;
150 static gboolean running
= FALSE
; /* nice flag */
151 static char *find_pattern
= NULL
; /* Pattern to search */
152 static char *content_pattern
= NULL
; /* pattern to search inside files; if
153 content_regexp_flag is true, it contains the
154 regex pattern, else the search string. */
155 static gboolean content_is_empty
= TRUE
; /* remember content field state; initially is empty */
156 static unsigned long matches
; /* Number of matches */
157 static gboolean is_start
= FALSE
; /* Status of the start/stop toggle button */
158 static char *old_dir
= NULL
;
160 static struct timeval last_refresh
;
162 /* Where did we stop */
163 static gboolean resuming
;
164 static int last_line
;
166 static off_t last_off
;
169 static size_t ignore_count
= 0;
171 static WDialog
*find_dlg
; /* The dialog */
172 static WLabel
*status_label
; /* Finished, Searching etc. */
173 static WLabel
*found_num_label
; /* Number of found items */
175 /* This keeps track of the directory stack */
176 static GQueue dir_queue
= G_QUEUE_INIT
;
182 button_flags_t flags
;
184 int len
; /* length including space and brackets */
190 { B_ENTER
, DEFPUSH_BUTTON
, N_("&Chdir"), 0, 0, NULL
, NULL
},
191 { B_AGAIN
, NORMAL_BUTTON
, N_("&Again"), 0, 0, NULL
, NULL
},
192 { B_STOP
, NORMAL_BUTTON
, N_("S&uspend"), 0, 0, NULL
, start_stop
},
193 { B_STOP
, NORMAL_BUTTON
, N_("Con&tinue"), 0, 0, NULL
, NULL
},
194 { B_CANCEL
, NORMAL_BUTTON
, N_("&Quit"), 0, 0, NULL
, NULL
},
196 { B_PANELIZE
, NORMAL_BUTTON
, N_("Pane&lize"), 0, 0, NULL
, NULL
},
197 { B_VIEW
, NORMAL_BUTTON
, N_("&View - F3"), 0, 0, NULL
, find_do_view_file
},
198 { B_VIEW
, NORMAL_BUTTON
, N_("&Edit - F4"), 0, 0, NULL
, find_do_edit_file
}
202 static const size_t fbuts_num
= G_N_ELEMENTS (fbuts
);
203 static const size_t quit_button
= 4; /* index of "Quit" button */
205 static WListbox
*find_list
; /* Listbox with the file list */
207 static find_file_options_t options
= {
208 TRUE
, TRUE
, TRUE
, FALSE
, FALSE
,
209 TRUE
, FALSE
, FALSE
, FALSE
, FALSE
,
213 static char *in_start_dir
= INPUT_LAST_TEXT
;
215 static mc_search_t
*search_file_handle
= NULL
;
216 static mc_search_t
*search_content_handle
= NULL
;
218 /* --------------------------------------------------------------------------------------------- */
219 /*** file scope functions ************************************************************************/
220 /* --------------------------------------------------------------------------------------------- */
222 /* don't use max macro to avoid double str_term_width1() call in widget length caclulation */
228 return (a
> b
? a
: b
);
231 /* --------------------------------------------------------------------------------------------- */
234 parse_ignore_dirs (const char *ignore_dirs
)
236 size_t r
= 0, w
= 0; /* read and write iterators */
238 if (!options
.ignore_dirs_enable
|| ignore_dirs
== NULL
|| ignore_dirs
[0] == '\0')
241 find_ignore_dirs
= g_strsplit (ignore_dirs
, ":", -1);
243 /* Values like '/foo::/bar: produce holes in list.
244 * Find and remove them */
245 for (; find_ignore_dirs
[r
] != NULL
; r
++)
247 if (find_ignore_dirs
[r
][0] == '\0')
249 /* empty entry -- skip it */
250 MC_PTR_FREE (find_ignore_dirs
[r
]);
256 /* copy entry to the previous free array cell */
257 find_ignore_dirs
[w
] = find_ignore_dirs
[r
];
258 find_ignore_dirs
[r
] = NULL
;
261 canonicalize_pathname (find_ignore_dirs
[w
]);
262 if (find_ignore_dirs
[w
][0] != '\0')
265 MC_PTR_FREE (find_ignore_dirs
[w
]);
268 if (find_ignore_dirs
[0] == NULL
)
270 g_strfreev (find_ignore_dirs
);
271 find_ignore_dirs
= NULL
;
275 /* --------------------------------------------------------------------------------------------- */
278 find_load_options (void)
280 static gboolean loaded
= FALSE
;
287 options
.file_case_sens
=
288 mc_config_get_bool (mc_global
.main_config
, "FindFile", "file_case_sens", TRUE
);
289 options
.file_pattern
=
290 mc_config_get_bool (mc_global
.main_config
, "FindFile", "file_shell_pattern", TRUE
);
291 options
.find_recurs
=
292 mc_config_get_bool (mc_global
.main_config
, "FindFile", "file_find_recurs", TRUE
);
293 options
.skip_hidden
=
294 mc_config_get_bool (mc_global
.main_config
, "FindFile", "file_skip_hidden", FALSE
);
295 options
.file_all_charsets
=
296 mc_config_get_bool (mc_global
.main_config
, "FindFile", "file_all_charsets", FALSE
);
297 options
.content_case_sens
=
298 mc_config_get_bool (mc_global
.main_config
, "FindFile", "content_case_sens", TRUE
);
299 options
.content_regexp
=
300 mc_config_get_bool (mc_global
.main_config
, "FindFile", "content_regexp", FALSE
);
301 options
.content_first_hit
=
302 mc_config_get_bool (mc_global
.main_config
, "FindFile", "content_first_hit", FALSE
);
303 options
.content_whole_words
=
304 mc_config_get_bool (mc_global
.main_config
, "FindFile", "content_whole_words", FALSE
);
305 options
.content_all_charsets
=
306 mc_config_get_bool (mc_global
.main_config
, "FindFile", "content_all_charsets", FALSE
);
307 options
.ignore_dirs_enable
=
308 mc_config_get_bool (mc_global
.main_config
, "FindFile", "ignore_dirs_enable", TRUE
);
309 options
.ignore_dirs
=
310 mc_config_get_string (mc_global
.main_config
, "FindFile", "ignore_dirs", "");
312 if (options
.ignore_dirs
[0] == '\0')
313 MC_PTR_FREE (options
.ignore_dirs
);
316 /* --------------------------------------------------------------------------------------------- */
319 find_save_options (void)
321 mc_config_set_bool (mc_global
.main_config
, "FindFile", "file_case_sens",
322 options
.file_case_sens
);
323 mc_config_set_bool (mc_global
.main_config
, "FindFile", "file_shell_pattern",
324 options
.file_pattern
);
325 mc_config_set_bool (mc_global
.main_config
, "FindFile", "file_find_recurs", options
.find_recurs
);
326 mc_config_set_bool (mc_global
.main_config
, "FindFile", "file_skip_hidden", options
.skip_hidden
);
327 mc_config_set_bool (mc_global
.main_config
, "FindFile", "file_all_charsets",
328 options
.file_all_charsets
);
329 mc_config_set_bool (mc_global
.main_config
, "FindFile", "content_case_sens",
330 options
.content_case_sens
);
331 mc_config_set_bool (mc_global
.main_config
, "FindFile", "content_regexp",
332 options
.content_regexp
);
333 mc_config_set_bool (mc_global
.main_config
, "FindFile", "content_first_hit",
334 options
.content_first_hit
);
335 mc_config_set_bool (mc_global
.main_config
, "FindFile", "content_whole_words",
336 options
.content_whole_words
);
337 mc_config_set_bool (mc_global
.main_config
, "FindFile", "content_all_charsets",
338 options
.content_all_charsets
);
339 mc_config_set_bool (mc_global
.main_config
, "FindFile", "ignore_dirs_enable",
340 options
.ignore_dirs_enable
);
341 mc_config_set_string (mc_global
.main_config
, "FindFile", "ignore_dirs", options
.ignore_dirs
);
344 /* --------------------------------------------------------------------------------------------- */
347 add_to_list (const char *text
, void *data
)
349 return listbox_add_item (find_list
, LISTBOX_APPEND_AT_END
, 0, text
, data
, TRUE
);
352 /* --------------------------------------------------------------------------------------------- */
355 stop_idle (void *data
)
357 widget_idle (WIDGET (data
), FALSE
);
360 /* --------------------------------------------------------------------------------------------- */
363 status_update (const char *text
)
365 label_set_text (status_label
, text
);
368 /* --------------------------------------------------------------------------------------------- */
371 found_num_update (void)
373 char buffer
[BUF_TINY
];
375 g_snprintf (buffer
, sizeof (buffer
), _("Found: %lu"), matches
);
376 label_set_text (found_num_label
, buffer
);
379 /* --------------------------------------------------------------------------------------------- */
382 get_list_info (char **file
, char **dir
, gsize
* start
, gsize
* end
)
384 find_match_location_t
*location
;
386 listbox_get_current (find_list
, file
, (void **) &location
);
387 if (location
!= NULL
)
390 *dir
= location
->dir
;
392 *start
= location
->start
;
394 *end
= location
->end
;
403 /* --------------------------------------------------------------------------------------------- */
404 /** check regular expression */
407 find_check_regexp (const char *r
)
410 gboolean regexp_ok
= FALSE
;
412 search
= mc_search_new (r
, NULL
);
416 search
->search_type
= MC_SEARCH_T_REGEX
;
417 regexp_ok
= mc_search_prepare (search
);
418 mc_search_free (search
);
424 /* --------------------------------------------------------------------------------------------- */
427 find_toggle_enable_ignore_dirs (void)
429 widget_disable (WIDGET (in_ignore
), !ignore_dirs_cbox
->state
);
432 /* --------------------------------------------------------------------------------------------- */
435 find_toggle_enable_params (void)
437 gboolean disable
= input_is_empty (in_name
);
439 widget_disable (WIDGET (file_pattern_cbox
), disable
);
440 widget_disable (WIDGET (file_case_sens_cbox
), disable
);
442 widget_disable (WIDGET (file_all_charsets_cbox
), disable
);
446 /* --------------------------------------------------------------------------------------------- */
449 find_toggle_enable_content (void)
451 widget_disable (WIDGET (content_regexp_cbox
), content_is_empty
);
452 widget_disable (WIDGET (content_case_sens_cbox
), content_is_empty
);
454 widget_disable (WIDGET (content_all_charsets_cbox
), content_is_empty
);
456 widget_disable (WIDGET (content_whole_words_cbox
), content_is_empty
);
457 widget_disable (WIDGET (content_first_hit_cbox
), content_is_empty
);
460 /* --------------------------------------------------------------------------------------------- */
462 * Callback for the parameter dialog.
463 * Validate regex, prevent closing the dialog if it's invalid.
467 find_parm_callback (Widget
* w
, Widget
* sender
, widget_msg_t msg
, int parm
, void *data
)
469 /* FIXME: HACK: use first draw of dialog to resolve widget state dependencies.
470 * Use this time moment to check input field content. We can't do that in MSG_INIT
471 * because history is not loaded yet.
472 * Probably, we want new MSG_ACTIVATE message as complement to MSG_VALIDATE one. Or
473 * we could name it MSG_POST_INIT.
475 * In one or two other places we use MSG_IDLE instead of MSG_DRAW for a similar
476 * purpose. We should remember to fix those places too when we introduce the new
479 static gboolean first_draw
= TRUE
;
481 WDialog
*h
= DIALOG (w
);
490 if (sender
== WIDGET (ignore_dirs_cbox
))
492 find_toggle_enable_ignore_dirs ();
496 return MSG_NOT_HANDLED
;
499 if (h
->ret_value
!= B_ENTER
)
502 /* check filename regexp */
503 if (!file_pattern_cbox
->state
&& !input_is_empty (in_name
)
504 && !find_check_regexp (in_name
->buffer
))
506 /* Don't stop the dialog */
507 widget_set_state (w
, WST_ACTIVE
, TRUE
);
508 message (D_ERROR
, MSG_ERROR
, _("Malformed regular expression"));
509 widget_select (WIDGET (in_name
));
513 /* check content regexp */
514 if (content_regexp_cbox
->state
&& !content_is_empty
&& !find_check_regexp (in_with
->buffer
))
516 /* Don't stop the dialog */
517 widget_set_state (w
, WST_ACTIVE
, TRUE
);
518 message (D_ERROR
, MSG_ERROR
, _("Malformed regular expression"));
519 widget_select (WIDGET (in_with
));
526 if (GROUP (h
)->current
->data
== in_name
)
527 find_toggle_enable_params ();
528 else if (GROUP (h
)->current
->data
== in_with
)
530 content_is_empty
= input_is_empty (in_with
);
531 find_toggle_enable_content ();
538 find_toggle_enable_ignore_dirs ();
539 find_toggle_enable_params ();
540 find_toggle_enable_content ();
544 MC_FALLTHROUGH
; /* to call MSG_DRAW default handler */
547 return dlg_default_callback (w
, sender
, msg
, parm
, data
);
551 /* --------------------------------------------------------------------------------------------- */
553 * find_parameters: gets information from the user
555 * If the return value is TRUE, then the following holds:
557 * start_dir, ignore_dirs, pattern and content contain the information provided by the user.
558 * They are newly allocated strings and must be freed when uneeded.
560 * start_dir_len is -1 when user entered an absolute path, otherwise it is a length
561 * of start_dir (which is absolute). It is used to get a relative pats of find results.
565 find_parameters (char **start_dir
, ssize_t
* start_dir_len
,
566 char **ignore_dirs
, char **pattern
, char **content
)
570 /* Size of the find parameters window */
572 const int lines
= 18;
574 const int lines
= 17;
578 gboolean return_value
;
581 const char *file_name_label
= N_("File name:");
582 const char *file_recurs_label
= N_("&Find recursively");
583 const char *file_pattern_label
= N_("&Using shell patterns");
585 const char *file_all_charsets_label
= N_("&All charsets");
587 const char *file_case_label
= N_("Cas&e sensitive");
588 const char *file_skip_hidden_label
= N_("S&kip hidden");
591 const char *content_content_label
= N_("Content:");
592 const char *content_use_label
= N_("Sea&rch for content");
593 const char *content_regexp_label
= N_("Re&gular expression");
594 const char *content_case_label
= N_("Case sens&itive");
596 const char *content_all_charsets_label
= N_("A&ll charsets");
598 const char *content_whole_words_label
= N_("&Whole words");
599 const char *content_first_hit_label
= N_("Fir&st hit");
601 const char *buts
[] = { N_("&Tree"), N_("&OK"), N_("&Cancel") };
613 file_name_label
= _(file_name_label
);
614 file_recurs_label
= _(file_recurs_label
);
615 file_pattern_label
= _(file_pattern_label
);
617 file_all_charsets_label
= _(file_all_charsets_label
);
619 file_case_label
= _(file_case_label
);
620 file_skip_hidden_label
= _(file_skip_hidden_label
);
623 content_content_label
= _(content_content_label
);
624 content_use_label
= _(content_use_label
);
625 content_regexp_label
= _(content_regexp_label
);
626 content_case_label
= _(content_case_label
);
628 content_all_charsets_label
= _(content_all_charsets_label
);
630 content_whole_words_label
= _(content_whole_words_label
);
631 content_first_hit_label
= _(content_first_hit_label
);
633 for (i
= 0; i
< G_N_ELEMENTS (buts
); i
++)
634 buts
[i
] = _(buts
[i
]);
636 #endif /* ENABLE_NLS */
638 /* caclulate dialog width */
641 cw
= str_term_width1 (file_name_label
);
642 cw
= max (cw
, str_term_width1 (file_recurs_label
) + 4);
643 cw
= max (cw
, str_term_width1 (file_pattern_label
) + 4);
645 cw
= max (cw
, str_term_width1 (file_all_charsets_label
) + 4);
647 cw
= max (cw
, str_term_width1 (file_case_label
) + 4);
648 cw
= max (cw
, str_term_width1 (file_skip_hidden_label
) + 4);
650 cw
= max (cw
, str_term_width1 (content_content_label
) + 4);
651 cw
= max (cw
, str_term_width1 (content_use_label
) + 4);
652 cw
= max (cw
, str_term_width1 (content_regexp_label
) + 4);
653 cw
= max (cw
, str_term_width1 (content_case_label
) + 4);
655 cw
= max (cw
, str_term_width1 (content_all_charsets_label
) + 4);
657 cw
= max (cw
, str_term_width1 (content_whole_words_label
) + 4);
658 cw
= max (cw
, str_term_width1 (content_first_hit_label
) + 4);
661 b0
= str_term_width1 (buts
[0]) + 3;
662 b1
= str_term_width1 (buts
[1]) + 5; /* default button */
663 b2
= str_term_width1 (buts
[2]) + 3;
666 cols
= max (cols
, max (b12
, cw
* 2 + 1) + 6);
668 find_load_options ();
670 if (in_start_dir
== NULL
)
671 in_start_dir
= g_strdup (".");
674 dlg_create (TRUE
, 0, 0, lines
, cols
, WPOS_CENTER
, FALSE
, dialog_colors
, find_parm_callback
,
675 NULL
, "[Find File]", _("Find File"));
676 g
= GROUP (find_dlg
);
683 group_add_widget (g
, label_new (y1
++, x1
, _("Start at:")));
685 input_new (y1
, x1
, input_colors
, cols
- b0
- 7, in_start_dir
, "start",
686 INPUT_COMPLETE_CD
| INPUT_COMPLETE_FILENAMES
);
687 group_add_widget (g
, in_start
);
689 group_add_widget (g
, button_new (y1
++, cols
- b0
- 3, B_TREE
, NORMAL_BUTTON
, buts
[0], NULL
));
692 check_new (y1
++, x1
, options
.ignore_dirs_enable
, _("Ena&ble ignore directories:"));
693 group_add_widget (g
, ignore_dirs_cbox
);
696 input_new (y1
++, x1
, input_colors
, cols
- 6,
697 options
.ignore_dirs
!= NULL
? options
.ignore_dirs
: "", "ignoredirs",
698 INPUT_COMPLETE_CD
| INPUT_COMPLETE_FILENAMES
);
699 group_add_widget (g
, in_ignore
);
701 group_add_widget (g
, hline_new (y1
++, -1, -1));
705 /* Start 1st column */
706 group_add_widget (g
, label_new (y1
++, x1
, file_name_label
));
708 input_new (y1
++, x1
, input_colors
, cw
, INPUT_LAST_TEXT
, "name",
709 INPUT_COMPLETE_FILENAMES
| INPUT_COMPLETE_CD
);
710 group_add_widget (g
, in_name
);
712 /* Start 2nd column */
713 content_label
= label_new (y2
++, x2
, content_content_label
);
714 group_add_widget (g
, content_label
);
716 input_new (y2
++, x2
, input_colors
, cw
, content_is_empty
? "" : INPUT_LAST_TEXT
,
717 MC_HISTORY_SHARED_SEARCH
, INPUT_COMPLETE_NONE
);
718 in_with
->label
= content_label
;
719 group_add_widget (g
, in_with
);
721 /* Continue 1st column */
722 recursively_cbox
= check_new (y1
++, x1
, options
.find_recurs
, file_recurs_label
);
723 group_add_widget (g
, recursively_cbox
);
725 file_pattern_cbox
= check_new (y1
++, x1
, options
.file_pattern
, file_pattern_label
);
726 group_add_widget (g
, file_pattern_cbox
);
728 file_case_sens_cbox
= check_new (y1
++, x1
, options
.file_case_sens
, file_case_label
);
729 group_add_widget (g
, file_case_sens_cbox
);
732 file_all_charsets_cbox
=
733 check_new (y1
++, x1
, options
.file_all_charsets
, file_all_charsets_label
);
734 group_add_widget (g
, file_all_charsets_cbox
);
737 skip_hidden_cbox
= check_new (y1
++, x1
, options
.skip_hidden
, file_skip_hidden_label
);
738 group_add_widget (g
, skip_hidden_cbox
);
740 /* Continue 2nd column */
741 content_whole_words_cbox
=
742 check_new (y2
++, x2
, options
.content_whole_words
, content_whole_words_label
);
743 group_add_widget (g
, content_whole_words_cbox
);
745 content_regexp_cbox
= check_new (y2
++, x2
, options
.content_regexp
, content_regexp_label
);
746 group_add_widget (g
, content_regexp_cbox
);
748 content_case_sens_cbox
= check_new (y2
++, x2
, options
.content_case_sens
, content_case_label
);
749 group_add_widget (g
, content_case_sens_cbox
);
752 content_all_charsets_cbox
=
753 check_new (y2
++, x2
, options
.content_all_charsets
, content_all_charsets_label
);
754 group_add_widget (g
, content_all_charsets_cbox
);
757 content_first_hit_cbox
=
758 check_new (y2
++, x2
, options
.content_first_hit
, content_first_hit_label
);
759 group_add_widget (g
, content_first_hit_cbox
);
763 x1
= (cols
- b12
) / 2;
764 group_add_widget (g
, hline_new (y1
++, -1, -1));
765 group_add_widget (g
, button_new (y1
, x1
, B_ENTER
, DEFPUSH_BUTTON
, buts
[1], NULL
));
766 group_add_widget (g
, button_new (y1
, x1
+ b1
+ 1, B_CANCEL
, NORMAL_BUTTON
, buts
[2], NULL
));
769 widget_select (WIDGET (in_name
));
771 switch (dlg_run (find_dlg
))
774 return_value
= FALSE
;
781 temp_dir
= in_start
->buffer
;
782 if (*temp_dir
== '\0' || DIR_IS_DOT (temp_dir
))
783 temp_dir
= g_strdup (vfs_path_as_str (current_panel
->cwd_vpath
));
785 temp_dir
= g_strdup (temp_dir
);
787 if (in_start_dir
!= INPUT_LAST_TEXT
)
788 g_free (in_start_dir
);
789 in_start_dir
= tree_box (temp_dir
);
790 if (in_start_dir
== NULL
)
791 in_start_dir
= temp_dir
;
795 input_assign_text (in_start
, in_start_dir
);
797 /* Warning: Dreadful goto */
806 options
.file_all_charsets
= file_all_charsets_cbox
->state
;
807 options
.content_all_charsets
= content_all_charsets_cbox
->state
;
809 options
.content_case_sens
= content_case_sens_cbox
->state
;
810 options
.content_regexp
= content_regexp_cbox
->state
;
811 options
.content_first_hit
= content_first_hit_cbox
->state
;
812 options
.content_whole_words
= content_whole_words_cbox
->state
;
813 options
.find_recurs
= recursively_cbox
->state
;
814 options
.file_pattern
= file_pattern_cbox
->state
;
815 options
.file_case_sens
= file_case_sens_cbox
->state
;
816 options
.skip_hidden
= skip_hidden_cbox
->state
;
817 options
.ignore_dirs_enable
= ignore_dirs_cbox
->state
;
818 g_free (options
.ignore_dirs
);
819 options
.ignore_dirs
= g_strdup (in_ignore
->buffer
);
821 *content
= !input_is_empty (in_with
) ? g_strdup (in_with
->buffer
) : NULL
;
822 if (!input_is_empty (in_name
))
823 *pattern
= g_strdup (in_name
->buffer
);
825 *pattern
= g_strdup (options
.file_pattern
? "*" : ".*");
826 *start_dir
= !input_is_empty (in_start
) ? in_start
->buffer
: (char *) ".";
827 if (in_start_dir
!= INPUT_LAST_TEXT
)
828 g_free (in_start_dir
);
829 in_start_dir
= g_strdup (*start_dir
);
831 s
= tilde_expand (*start_dir
);
832 canonicalize_pathname (s
);
836 *start_dir
= g_strdup (vfs_path_as_str (current_panel
->cwd_vpath
));
837 /* FIXME: is current_panel->cwd_vpath canonicalized? */
838 /* relative paths will be used in panelization */
839 *start_dir_len
= (ssize_t
) strlen (*start_dir
);
842 else if (g_path_is_absolute (s
))
849 /* relative paths will be used in panelization */
851 mc_build_filename (vfs_path_as_str (current_panel
->cwd_vpath
), s
,
853 *start_dir_len
= (ssize_t
) strlen (vfs_path_as_str (current_panel
->cwd_vpath
));
857 if (!options
.ignore_dirs_enable
|| input_is_empty (in_ignore
)
858 || DIR_IS_DOT (in_ignore
->buffer
))
861 *ignore_dirs
= g_strdup (in_ignore
->buffer
);
863 find_save_options ();
869 dlg_destroy (find_dlg
);
874 /* --------------------------------------------------------------------------------------------- */
877 push_directory (vfs_path_t
* dir
)
879 g_queue_push_head (&dir_queue
, (void *) dir
);
882 /* --------------------------------------------------------------------------------------------- */
884 static inline vfs_path_t
*
887 return (vfs_path_t
*) g_queue_pop_head (&dir_queue
);
890 /* --------------------------------------------------------------------------------------------- */
891 /** Remove all the items from the stack */
896 g_queue_clear_full (&dir_queue
, (GDestroyNotify
) vfs_path_free
);
899 /* --------------------------------------------------------------------------------------------- */
902 insert_file (const char *dir
, const char *file
, gsize start
, gsize end
)
904 char *tmp_name
= NULL
;
905 static char *dirname
= NULL
;
906 find_match_location_t
*location
;
908 while (IS_PATH_SEP (dir
[0]) && IS_PATH_SEP (dir
[1]))
913 if (strcmp (old_dir
, dir
) != 0)
916 old_dir
= g_strdup (dir
);
917 dirname
= add_to_list (dir
, NULL
);
922 old_dir
= g_strdup (dir
);
923 dirname
= add_to_list (dir
, NULL
);
926 tmp_name
= g_strdup_printf (" %s", file
);
927 location
= g_malloc (sizeof (*location
));
928 location
->dir
= dirname
;
929 location
->start
= start
;
931 add_to_list (tmp_name
, location
);
935 /* --------------------------------------------------------------------------------------------- */
938 find_add_match (const char *dir
, const char *file
, gsize start
, gsize end
)
940 insert_file (dir
, file
, start
, end
);
944 listbox_select_first (find_list
);
945 widget_draw (WIDGET (find_list
));
951 /* --------------------------------------------------------------------------------------------- */
953 static FindProgressStatus
954 check_find_events (WDialog
* h
)
960 c
= tty_get_event (&event
, h
->mouse_status
== MOU_REPEAT
, FALSE
);
963 dlg_process_event (h
, c
, &event
);
964 if (h
->ret_value
== B_ENTER
965 || h
->ret_value
== B_CANCEL
|| h
->ret_value
== B_AGAIN
|| h
->ret_value
== B_PANELIZE
)
967 /* dialog terminated */
970 if (!widget_get_state (WIDGET (h
), WST_IDLE
))
972 /* searching suspended */
980 /* --------------------------------------------------------------------------------------------- */
984 * Search the content_pattern string in the DIRECTORY/FILE.
985 * It will add the found entries to the find listbox.
987 * returns FALSE if do_search should look for another file
988 * TRUE if do_search should exit and proceed to the event handler
992 search_content (WDialog
* h
, const char *directory
, const char *filename
)
995 char buffer
[BUF_4K
]; /* raw input buffer */
997 gboolean ret_val
= FALSE
;
1001 suseconds_t useconds
;
1002 gboolean status_updated
= FALSE
;
1004 vpath
= vfs_path_build_filename (directory
, filename
, (char *) NULL
);
1006 if (mc_stat (vpath
, &s
) != 0 || !S_ISREG (s
.st_mode
))
1008 vfs_path_free (vpath
);
1012 file_fd
= mc_open (vpath
, O_RDONLY
);
1013 vfs_path_free (vpath
);
1018 /* get time elapsed from last refresh */
1019 if (gettimeofday (&tv
, NULL
) == -1)
1025 seconds
= tv
.tv_sec
- last_refresh
.tv_sec
;
1026 useconds
= tv
.tv_usec
- last_refresh
.tv_usec
;
1030 useconds
+= G_USEC_PER_SEC
;
1033 if (s
.st_size
>= MIN_REFRESH_FILE_SIZE
|| seconds
> 0 || useconds
> MAX_REFRESH_INTERVAL
)
1035 g_snprintf (buffer
, sizeof (buffer
), _("Grepping in %s"), filename
);
1036 status_update (str_trunc (buffer
, WIDGET (h
)->cols
- 8));
1039 status_updated
= TRUE
;
1042 tty_enable_interrupt_key ();
1043 tty_got_interrupt ();
1049 off_t off
= 0; /* file_fd's offset corresponding to strbuf[0] */
1050 gboolean found
= FALSE
;
1053 char result
[BUF_MEDIUM
];
1054 char *strbuf
= NULL
; /* buffer for fetched string */
1055 int strbuf_size
= 0;
1056 int i
= -1; /* compensate for a newline we'll add when we first enter the loop */
1060 /* We've been previously suspended, start from the previous position */
1072 off
+= i
+ 1; /* the previous line, plus a newline character */
1075 /* read to buffer and get line from there */
1081 n_read
= mc_read (file_fd
, buffer
, sizeof (buffer
));
1089 /* skip possible leading zero(s) */
1098 if (i
>= strbuf_size
- 1)
1101 strbuf
= g_realloc (strbuf
, strbuf_size
);
1116 /* if (ch == '\n'): do not search in empty strings */
1122 if (!found
/* Search in binary line once */
1123 && mc_search_run (search_content_handle
, (const void *) strbuf
, 0, i
, &found_len
))
1125 if (!status_updated
)
1127 /* if we add results for a file, we have to ensure that
1128 name of this file is shown in status bar */
1129 g_snprintf (result
, sizeof (result
), _("Grepping in %s"), filename
);
1130 status_update (str_trunc (result
, WIDGET (h
)->cols
- 8));
1133 status_updated
= TRUE
;
1136 g_snprintf (result
, sizeof (result
), "%d:%s", line
, filename
);
1137 found_start
= off
+ search_content_handle
->normal_offset
+ 1; /* off by one: ticket 3280 */
1138 find_add_match (directory
, result
, found_start
, found_start
+ found_len
);
1142 if (found
&& options
.content_first_hit
)
1152 if ((line
& 0xff) == 0)
1154 FindProgressStatus res
;
1156 res
= check_find_events (h
);
1180 tty_disable_interrupt_key ();
1185 /* --------------------------------------------------------------------------------------------- */
1188 If dir is absolute, this means we're within dir and searching file here.
1189 If dir is relative, this means we're going to add dir to the directory stack.
1192 find_ignore_dir_search (const char *dir
)
1194 if (find_ignore_dirs
!= NULL
)
1196 const size_t dlen
= strlen (dir
);
1197 const unsigned char dabs
= g_path_is_absolute (dir
) ? 1 : 0;
1201 for (ignore_dir
= find_ignore_dirs
; *ignore_dir
!= NULL
; ignore_dir
++)
1203 const size_t ilen
= strlen (*ignore_dir
);
1204 const unsigned char iabs
= g_path_is_absolute (*ignore_dir
) ? 2 : 0;
1206 /* ignore dir is too long -- skip it */
1210 /* handle absolute and relative paths */
1211 switch (iabs
| dabs
)
1213 case 0: /* both paths are relative */
1214 case 3: /* both paths are abolute */
1215 /* if ignore dir is not a path of dir -- skip it */
1216 if (strncmp (dir
, *ignore_dir
, ilen
) == 0)
1218 /* be sure that ignore dir is not a part of dir like:
1219 ignore dir is "h", dir is "home" */
1220 if (dir
[ilen
] == '\0' || IS_PATH_SEP (dir
[ilen
]))
1224 case 1: /* dir is absolute, ignore_dir is relative */
1228 d
= strstr (dir
, *ignore_dir
);
1229 if (d
!= NULL
&& IS_PATH_SEP (d
[-1])
1230 && (d
[ilen
] == '\0' || IS_PATH_SEP (d
[ilen
])))
1234 case 2: /* dir is relative, ignore_dir is absolute */
1235 /* FIXME: skip this case */
1237 default: /* this cannot occurs */
1246 /* --------------------------------------------------------------------------------------------- */
1249 find_rotate_dash (const WDialog
* h
, gboolean show
)
1251 static size_t pos
= 0;
1252 static const char rotating_dash
[4] = "|/-\\";
1253 const Widget
*w
= CONST_WIDGET (h
);
1255 tty_setcolor (h
->color
[DLG_COLOR_NORMAL
]);
1256 widget_gotoyx (h
, w
->lines
- 7, w
->cols
- 4);
1257 tty_print_char (show
? rotating_dash
[pos
] : ' ');
1258 pos
= (pos
+ 1) % sizeof (rotating_dash
);
1262 /* --------------------------------------------------------------------------------------------- */
1265 do_search (WDialog
* h
)
1267 static struct dirent
*dp
= NULL
;
1268 static DIR *dirp
= NULL
;
1269 static char *directory
= NULL
;
1270 struct stat tmp_stat
;
1272 unsigned short count
;
1275 { /* someone forces me to close dirp */
1281 MC_PTR_FREE (directory
);
1286 for (count
= 0; count
< 32; count
++)
1296 while (dirp
== NULL
)
1298 vfs_path_t
*tmp_vpath
= NULL
;
1300 tty_setcolor (REVERSE_COLOR
);
1304 tmp_vpath
= pop_directory ();
1305 if (tmp_vpath
== NULL
)
1308 if (ignore_count
== 0)
1309 status_update (_("Finished"));
1312 char msg
[BUF_SMALL
];
1314 g_snprintf (msg
, sizeof (msg
),
1315 ngettext ("Finished (ignored %zu directory)",
1316 "Finished (ignored %zu directories)",
1317 ignore_count
), ignore_count
);
1318 status_update (msg
);
1321 find_rotate_dash (h
, FALSE
);
1326 /* handle absolute ignore dirs here */
1330 ok
= find_ignore_dir_search (vfs_path_as_str (tmp_vpath
));
1335 vfs_path_free (tmp_vpath
);
1340 directory
= g_strdup (vfs_path_as_str (tmp_vpath
));
1344 char buffer
[BUF_MEDIUM
];
1346 g_snprintf (buffer
, sizeof (buffer
), _("Searching %s"), directory
);
1347 status_update (str_trunc (directory
, WIDGET (h
)->cols
- 8));
1350 dirp
= mc_opendir (tmp_vpath
);
1351 vfs_path_free (tmp_vpath
);
1352 } /* while (!dirp) */
1354 /* skip invalid filenames */
1355 while ((dp
= mc_readdir (dirp
)) != NULL
&& !str_is_valid_string (dp
->d_name
))
1359 if (DIR_IS_DOT (dp
->d_name
) || DIR_IS_DOTDOT (dp
->d_name
))
1361 /* skip invalid filenames */
1362 while ((dp
= mc_readdir (dirp
)) != NULL
&& !str_is_valid_string (dp
->d_name
))
1368 if (!(options
.skip_hidden
&& (dp
->d_name
[0] == '.')))
1372 if (options
.find_recurs
&& (directory
!= NULL
))
1373 { /* Can directory be NULL ? */
1374 /* handle relative ignore dirs here */
1375 if (options
.ignore_dirs_enable
&& find_ignore_dir_search (dp
->d_name
))
1379 vfs_path_t
*tmp_vpath
;
1381 tmp_vpath
= vfs_path_build_filename (directory
, dp
->d_name
, (char *) NULL
);
1383 if (mc_lstat (tmp_vpath
, &tmp_stat
) == 0 && S_ISDIR (tmp_stat
.st_mode
))
1384 push_directory (tmp_vpath
);
1386 vfs_path_free (tmp_vpath
);
1390 search_ok
= mc_search_run (search_file_handle
, dp
->d_name
,
1391 0, strlen (dp
->d_name
), &bytes_found
);
1395 if (content_pattern
== NULL
)
1396 find_add_match (directory
, dp
->d_name
, 0, 0);
1397 else if (search_content (h
, directory
, dp
->d_name
))
1402 /* skip invalid filenames */
1403 while ((dp
= mc_readdir (dirp
)) != NULL
&& !str_is_valid_string (dp
->d_name
))
1408 find_rotate_dash (h
, TRUE
);
1413 /* --------------------------------------------------------------------------------------------- */
1416 init_find_vars (void)
1418 MC_PTR_FREE (old_dir
);
1422 /* Remove all the items from the stack */
1425 g_strfreev (find_ignore_dirs
);
1426 find_ignore_dirs
= NULL
;
1429 /* --------------------------------------------------------------------------------------------- */
1432 find_do_view_edit (gboolean unparsed_view
, gboolean edit
, char *dir
, char *file
, off_t search_start
,
1435 const char *filename
= NULL
;
1437 vfs_path_t
*fullname_vpath
;
1439 if (content_pattern
!= NULL
)
1441 filename
= strchr (file
+ 4, ':') + 1;
1442 line
= atoi (file
+ 4);
1446 filename
= file
+ 4;
1450 fullname_vpath
= vfs_path_build_filename (dir
, filename
, (char *) NULL
);
1452 edit_file_at_line (fullname_vpath
, use_internal_edit
, line
);
1454 view_file_at_line (fullname_vpath
, unparsed_view
, use_internal_view
, line
, search_start
,
1456 vfs_path_free (fullname_vpath
);
1459 /* --------------------------------------------------------------------------------------------- */
1462 view_edit_currently_selected_file (gboolean unparsed_view
, gboolean edit
)
1465 find_match_location_t
*location
;
1467 listbox_get_current (find_list
, &text
, (void **) &location
);
1469 if ((text
== NULL
) || (location
== NULL
) || (location
->dir
== NULL
))
1470 return MSG_NOT_HANDLED
;
1472 find_do_view_edit (unparsed_view
, edit
, location
->dir
, text
, location
->start
, location
->end
);
1476 /* --------------------------------------------------------------------------------------------- */
1479 find_calc_button_locations (const WDialog
* h
, gboolean all_buttons
)
1481 const int cols
= CONST_WIDGET (h
)->cols
;
1485 l1
= fbuts
[0].len
+ fbuts
[1].len
+ fbuts
[is_start
? 3 : 2].len
+ fbuts
[4].len
+ 3;
1486 l2
= fbuts
[5].len
+ fbuts
[6].len
+ fbuts
[7].len
+ 2;
1488 fbuts
[0].x
= (cols
- l1
) / 2;
1489 fbuts
[1].x
= fbuts
[0].x
+ fbuts
[0].len
+ 1;
1490 fbuts
[2].x
= fbuts
[1].x
+ fbuts
[1].len
+ 1;
1491 fbuts
[3].x
= fbuts
[2].x
;
1492 fbuts
[4].x
= fbuts
[2].x
+ fbuts
[is_start
? 3 : 2].len
+ 1;
1496 fbuts
[5].x
= (cols
- l2
) / 2;
1497 fbuts
[6].x
= fbuts
[5].x
+ fbuts
[5].len
+ 1;
1498 fbuts
[7].x
= fbuts
[6].x
+ fbuts
[6].len
+ 1;
1502 /* --------------------------------------------------------------------------------------------- */
1505 find_adjust_header (WDialog
* h
)
1507 char title
[BUF_MEDIUM
];
1510 if (content_pattern
!= NULL
)
1511 g_snprintf (title
, sizeof (title
), _("Find File: \"%s\". Content: \"%s\""), find_pattern
,
1514 g_snprintf (title
, sizeof (title
), _("Find File: \"%s\""), find_pattern
);
1516 title_len
= str_term_width1 (title
);
1517 if (title_len
> WIDGET (h
)->cols
- 6)
1519 /* title is too wide, truncate it */
1520 title_len
= WIDGET (h
)->cols
- 6;
1521 title_len
= str_column_to_pos (title
, title_len
);
1522 title_len
-= 3; /* reserve space for three dots */
1523 title_len
= str_offset_to_pos (title
, title_len
);
1524 /* mark that title is truncated */
1525 memmove (title
+ title_len
, "...", 4);
1528 dlg_set_title (h
, title
);
1531 /* --------------------------------------------------------------------------------------------- */
1534 find_relocate_buttons (const WDialog
* h
, gboolean all_buttons
)
1538 find_calc_button_locations (h
, all_buttons
);
1540 for (i
= 0; i
< fbuts_num
; i
++)
1541 fbuts
[i
].button
->x
= CONST_WIDGET (h
)->x
+ fbuts
[i
].x
;
1544 /* --------------------------------------------------------------------------------------------- */
1547 find_callback (Widget
* w
, Widget
* sender
, widget_msg_t msg
, int parm
, void *data
)
1549 WDialog
*h
= DIALOG (w
);
1554 find_adjust_header (h
);
1558 if (parm
== KEY_F (3) || parm
== KEY_F (13))
1560 gboolean unparsed_view
= (parm
== KEY_F (13));
1562 return view_edit_currently_selected_file (unparsed_view
, FALSE
);
1564 if (parm
== KEY_F (4))
1565 return view_edit_currently_selected_file (FALSE
, TRUE
);
1566 return MSG_NOT_HANDLED
;
1569 dlg_set_size (h
, LINES
- 4, COLS
- 16);
1570 find_adjust_header (h
);
1571 find_relocate_buttons (h
, TRUE
);
1579 return dlg_default_callback (w
, sender
, msg
, parm
, data
);
1583 /* --------------------------------------------------------------------------------------------- */
1584 /** Handles the Stop/Start button in the find window */
1587 start_stop (WButton
* button
, int action
)
1589 Widget
*w
= WIDGET (button
);
1594 widget_idle (WIDGET (find_dlg
), running
);
1595 is_start
= !is_start
;
1597 status_update (is_start
? _("Stopped") : _("Searching"));
1598 button_set_text (button
, fbuts
[is_start
? 3 : 2].text
);
1600 find_relocate_buttons (DIALOG (w
->owner
), FALSE
);
1601 dlg_draw (DIALOG (w
->owner
));
1606 /* --------------------------------------------------------------------------------------------- */
1607 /** Handle view command, when invoked as a button */
1610 find_do_view_file (WButton
* button
, int action
)
1615 view_edit_currently_selected_file (FALSE
, FALSE
);
1619 /* --------------------------------------------------------------------------------------------- */
1620 /** Handle edit command, when invoked as a button */
1623 find_do_edit_file (WButton
* button
, int action
)
1628 view_edit_currently_selected_file (FALSE
, TRUE
);
1632 /* --------------------------------------------------------------------------------------------- */
1642 static gboolean i18n_flag
= FALSE
;
1646 for (i
= 0; i
< fbuts_num
; i
++)
1649 fbuts
[i
].text
= _(fbuts
[i
].text
);
1650 #endif /* ENABLE_NLS */
1651 fbuts
[i
].len
= str_term_width1 (fbuts
[i
].text
) + 3;
1652 if (fbuts
[i
].flags
== DEFPUSH_BUTTON
)
1663 dlg_create (TRUE
, 0, 0, lines
, cols
, WPOS_CENTER
, FALSE
, dialog_colors
, find_callback
, NULL
,
1664 "[Find File]", NULL
);
1665 g
= GROUP (find_dlg
);
1667 find_calc_button_locations (find_dlg
, TRUE
);
1670 find_list
= listbox_new (y
, 2, lines
- 10, cols
- 4, FALSE
, NULL
);
1671 group_add_widget_autopos (g
, find_list
, WPOS_KEEP_ALL
, NULL
);
1672 y
+= WIDGET (find_list
)->lines
;
1674 group_add_widget_autopos (g
, hline_new (y
++, -1, -1), WPOS_KEEP_BOTTOM
, NULL
);
1676 found_num_label
= label_new (y
++, 4, "");
1677 group_add_widget_autopos (g
, found_num_label
, WPOS_KEEP_BOTTOM
, NULL
);
1679 status_label
= label_new (y
++, 4, _("Searching"));
1680 group_add_widget_autopos (g
, status_label
, WPOS_KEEP_BOTTOM
, NULL
);
1682 group_add_widget_autopos (g
, hline_new (y
++, -1, -1), WPOS_KEEP_BOTTOM
, NULL
);
1684 for (i
= 0; i
< fbuts_num
; i
++)
1687 fbuts
[3].button
= fbuts
[2].button
;
1692 (y
, fbuts
[i
].x
, fbuts
[i
].ret_cmd
, fbuts
[i
].flags
, fbuts
[i
].text
,
1693 fbuts
[i
].callback
));
1694 group_add_widget_autopos (g
, fbuts
[i
].button
, WPOS_KEEP_BOTTOM
, NULL
);
1697 if (i
== quit_button
)
1701 widget_select (WIDGET (find_list
));
1704 /* --------------------------------------------------------------------------------------------- */
1711 search_content_handle
= mc_search_new (content_pattern
, NULL
);
1712 if (search_content_handle
)
1714 search_content_handle
->search_type
=
1715 options
.content_regexp
? MC_SEARCH_T_REGEX
: MC_SEARCH_T_NORMAL
;
1716 search_content_handle
->is_case_sensitive
= options
.content_case_sens
;
1717 search_content_handle
->whole_words
= options
.content_whole_words
;
1719 search_content_handle
->is_all_charsets
= options
.content_all_charsets
;
1722 search_file_handle
= mc_search_new (find_pattern
, NULL
);
1723 search_file_handle
->search_type
= options
.file_pattern
? MC_SEARCH_T_GLOB
: MC_SEARCH_T_REGEX
;
1724 search_file_handle
->is_case_sensitive
= options
.file_case_sens
;
1726 search_file_handle
->is_all_charsets
= options
.file_all_charsets
;
1728 search_file_handle
->is_entire_line
= options
.file_pattern
;
1732 widget_idle (WIDGET (find_dlg
), TRUE
);
1733 ret
= dlg_run (find_dlg
);
1735 mc_search_free (search_file_handle
);
1736 search_file_handle
= NULL
;
1737 mc_search_free (search_content_handle
);
1738 search_content_handle
= NULL
;
1743 /* --------------------------------------------------------------------------------------------- */
1748 widget_idle (WIDGET (find_dlg
), FALSE
);
1749 dlg_destroy (find_dlg
);
1752 /* --------------------------------------------------------------------------------------------- */
1755 do_find (const char *start_dir
, ssize_t start_dir_len
, const char *ignore_dirs
,
1756 char **dirname
, char **filename
)
1758 int return_value
= 0;
1759 char *dir_tmp
= NULL
, *file_tmp
= NULL
;
1764 parse_ignore_dirs (ignore_dirs
);
1765 push_directory (vfs_path_from_str (start_dir
));
1767 return_value
= run_process ();
1769 /* Clear variables */
1772 get_list_info (&file_tmp
, &dir_tmp
, NULL
, NULL
);
1775 *dirname
= g_strdup (dir_tmp
);
1777 *filename
= g_strdup (file_tmp
);
1779 if (return_value
== B_PANELIZE
&& *filename
)
1784 dir_list
*list
= ¤t_panel
->dir
;
1787 panel_clean_dir (current_panel
);
1788 dir_list_init (list
);
1790 for (i
= 0, entry
= listbox_get_first_link (find_list
); entry
!= NULL
;
1791 i
++, entry
= g_list_next (entry
))
1793 const char *lc_filename
= NULL
;
1794 WLEntry
*le
= LENTRY (entry
->data
);
1795 find_match_location_t
*location
= le
->data
;
1797 gboolean link_to_dir
, stale_link
;
1799 if ((le
->text
== NULL
) || (location
== NULL
) || (location
->dir
== NULL
))
1802 if (!content_is_empty
)
1803 lc_filename
= strchr (le
->text
+ 4, ':') + 1;
1805 lc_filename
= le
->text
+ 4;
1807 name
= mc_build_filename (location
->dir
, lc_filename
, (char *) NULL
);
1808 /* skip initial start dir */
1809 if (start_dir_len
< 0)
1813 p
= name
+ (size_t) start_dir_len
;
1814 if (IS_PATH_SEP (*p
))
1818 if (!handle_path (p
, &st
, &link_to_dir
, &stale_link
))
1823 /* Need to grow the *list? */
1824 if (list
->len
== list
->size
&& !dir_list_grow (list
, DIR_LIST_RESIZE_STEP
))
1830 /* don't add files more than once to the panel */
1831 if (!content_is_empty
&& list
->len
!= 0
1832 && strcmp (list
->list
[list
->len
- 1].fname
, p
) == 0)
1838 list
->list
[list
->len
].fnamelen
= strlen (p
);
1839 list
->list
[list
->len
].fname
= g_strndup (p
, list
->list
[list
->len
].fnamelen
);
1840 list
->list
[list
->len
].f
.marked
= 0;
1841 list
->list
[list
->len
].f
.link_to_dir
= link_to_dir
? 1 : 0;
1842 list
->list
[list
->len
].f
.stale_link
= stale_link
? 1 : 0;
1843 list
->list
[list
->len
].f
.dir_size_computed
= 0;
1844 list
->list
[list
->len
].st
= st
;
1845 list
->list
[list
->len
].sort_key
= NULL
;
1846 list
->list
[list
->len
].second_sort_key
= NULL
;
1849 if ((list
->len
& 15) == 0)
1853 current_panel
->is_panelized
= TRUE
;
1854 panelize_absolutize_if_needed (current_panel
);
1855 panelize_save_panel (current_panel
);
1859 do_search (NULL
); /* force do_search to release resources */
1860 MC_PTR_FREE (old_dir
);
1861 rotate_dash (FALSE
);
1863 return return_value
;
1866 /* --------------------------------------------------------------------------------------------- */
1867 /*** public functions ****************************************************************************/
1868 /* --------------------------------------------------------------------------------------------- */
1873 char *start_dir
= NULL
, *ignore_dirs
= NULL
;
1874 ssize_t start_dir_len
;
1876 find_pattern
= NULL
;
1877 content_pattern
= NULL
;
1879 while (find_parameters (&start_dir
, &start_dir_len
,
1880 &ignore_dirs
, &find_pattern
, &content_pattern
))
1882 char *filename
= NULL
, *dirname
= NULL
;
1885 content_is_empty
= content_pattern
== NULL
;
1887 if (find_pattern
[0] != '\0')
1889 last_refresh
.tv_sec
= 0;
1890 last_refresh
.tv_usec
= 0;
1894 if (!content_is_empty
&& !str_is_valid_string (content_pattern
))
1895 MC_PTR_FREE (content_pattern
);
1897 v
= do_find (start_dir
, start_dir_len
, ignore_dirs
, &dirname
, &filename
);
1901 g_free (ignore_dirs
);
1902 MC_PTR_FREE (find_pattern
);
1906 if (dirname
!= NULL
)
1908 vfs_path_t
*dirname_vpath
;
1910 dirname_vpath
= vfs_path_from_str (dirname
);
1911 do_cd (dirname_vpath
, cd_exact
);
1912 vfs_path_free (dirname_vpath
);
1913 if (filename
!= NULL
)
1914 try_to_select (current_panel
,
1915 filename
+ (content_pattern
!= NULL
1916 ? strchr (filename
+ 4, ':') - filename
+ 1 : 4));
1918 else if (filename
!= NULL
)
1920 vfs_path_t
*filename_vpath
;
1922 filename_vpath
= vfs_path_from_str (filename
);
1923 do_cd (filename_vpath
, cd_exact
);
1924 vfs_path_free (filename_vpath
);
1928 MC_PTR_FREE (content_pattern
);
1932 if (v
== B_ENTER
|| v
== B_CANCEL
)
1935 if (v
== B_PANELIZE
)
1937 panel_re_sort (current_panel
);
1938 try_to_select (current_panel
, NULL
);
1944 /* --------------------------------------------------------------------------------------------- */