2 Find file command for the Midnight Commander
4 Copyright (C) 1995-2024
5 Free Software Foundation, Inc.
9 Slava Zanko <slavazanko@gmail.com>, 2013
10 Andrew Borodin <aborodin@vmail.ru>, 2013-2022
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
40 #include "lib/global.h"
42 #include "lib/tty/tty.h"
43 #include "lib/tty/key.h"
45 #include "lib/search.h"
46 #include "lib/mcconfig.h"
47 #include "lib/vfs/vfs.h"
48 #include "lib/strutil.h"
49 #include "lib/widget.h"
50 #include "lib/util.h" /* canonicalize_pathname() */
52 #include "src/setup.h" /* verbose */
53 #include "src/history.h" /* MC_HISTORY_SHARED_SEARCH */
56 #include "cmd.h" /* find_cmd(), view_file_at_line() */
60 /*** global variables ****************************************************************************/
62 /*** file scope macro definitions ****************************************************************/
64 #define MAX_REFRESH_INTERVAL (G_USEC_PER_SEC / 20) /* 50 ms */
65 #define MIN_REFRESH_FILE_SIZE (256 * 1024) /* 256 KB */
67 /*** file scope type declarations ****************************************************************/
69 /* A couple of extra messages we need */
86 /* find file options */
89 /* file name options */
90 gboolean file_case_sens
;
91 gboolean file_pattern
;
93 gboolean follow_symlinks
;
95 gboolean file_all_charsets
;
97 /* file content options */
98 gboolean content_case_sens
;
99 gboolean content_regexp
;
100 gboolean content_first_hit
;
101 gboolean content_whole_words
;
102 gboolean content_all_charsets
;
104 /* whether use ignore dirs or not */
105 gboolean ignore_dirs_enable
;
106 /* list of directories to be ignored, separated by ':' */
108 } find_file_options_t
;
115 } find_match_location_t
;
117 /*** forward declarations (file scope functions) *************************************************/
119 /* button callbacks */
120 static int start_stop (WButton
* button
, int action
);
121 static int find_do_view_file (WButton
* button
, int action
);
122 static int find_do_edit_file (WButton
* button
, int action
);
124 /*** file scope variables ************************************************************************/
126 /* Parsed ignore dirs */
127 static char **find_ignore_dirs
= NULL
;
129 /* static variables to remember find parameters */
130 static WInput
*in_start
; /* Start path */
131 static WInput
*in_name
; /* Filename */
132 static WInput
*in_with
; /* Text */
133 static WInput
*in_ignore
;
134 static WLabel
*content_label
; /* 'Content:' label */
135 static WCheck
*file_case_sens_cbox
; /* "case sensitive" checkbox */
136 static WCheck
*file_pattern_cbox
; /* File name is glob or regexp */
137 static WCheck
*recursively_cbox
;
138 static WCheck
*follow_sym_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 gint64 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
, 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 calculation */
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
.follow_symlinks
=
294 mc_config_get_bool (mc_global
.main_config
, "FindFile", "follow_symlinks", FALSE
);
295 options
.skip_hidden
=
296 mc_config_get_bool (mc_global
.main_config
, "FindFile", "file_skip_hidden", FALSE
);
297 options
.file_all_charsets
=
298 mc_config_get_bool (mc_global
.main_config
, "FindFile", "file_all_charsets", FALSE
);
299 options
.content_case_sens
=
300 mc_config_get_bool (mc_global
.main_config
, "FindFile", "content_case_sens", TRUE
);
301 options
.content_regexp
=
302 mc_config_get_bool (mc_global
.main_config
, "FindFile", "content_regexp", FALSE
);
303 options
.content_first_hit
=
304 mc_config_get_bool (mc_global
.main_config
, "FindFile", "content_first_hit", FALSE
);
305 options
.content_whole_words
=
306 mc_config_get_bool (mc_global
.main_config
, "FindFile", "content_whole_words", FALSE
);
307 options
.content_all_charsets
=
308 mc_config_get_bool (mc_global
.main_config
, "FindFile", "content_all_charsets", FALSE
);
309 options
.ignore_dirs_enable
=
310 mc_config_get_bool (mc_global
.main_config
, "FindFile", "ignore_dirs_enable", TRUE
);
311 options
.ignore_dirs
=
312 mc_config_get_string (mc_global
.main_config
, "FindFile", "ignore_dirs", "");
314 if (options
.ignore_dirs
[0] == '\0')
315 MC_PTR_FREE (options
.ignore_dirs
);
318 /* --------------------------------------------------------------------------------------------- */
321 find_save_options (void)
323 mc_config_set_bool (mc_global
.main_config
, "FindFile", "file_case_sens",
324 options
.file_case_sens
);
325 mc_config_set_bool (mc_global
.main_config
, "FindFile", "file_shell_pattern",
326 options
.file_pattern
);
327 mc_config_set_bool (mc_global
.main_config
, "FindFile", "file_find_recurs", options
.find_recurs
);
328 mc_config_set_bool (mc_global
.main_config
, "FindFile", "follow_symlinks",
329 options
.follow_symlinks
);
330 mc_config_set_bool (mc_global
.main_config
, "FindFile", "file_skip_hidden", options
.skip_hidden
);
331 mc_config_set_bool (mc_global
.main_config
, "FindFile", "file_all_charsets",
332 options
.file_all_charsets
);
333 mc_config_set_bool (mc_global
.main_config
, "FindFile", "content_case_sens",
334 options
.content_case_sens
);
335 mc_config_set_bool (mc_global
.main_config
, "FindFile", "content_regexp",
336 options
.content_regexp
);
337 mc_config_set_bool (mc_global
.main_config
, "FindFile", "content_first_hit",
338 options
.content_first_hit
);
339 mc_config_set_bool (mc_global
.main_config
, "FindFile", "content_whole_words",
340 options
.content_whole_words
);
341 mc_config_set_bool (mc_global
.main_config
, "FindFile", "content_all_charsets",
342 options
.content_all_charsets
);
343 mc_config_set_bool (mc_global
.main_config
, "FindFile", "ignore_dirs_enable",
344 options
.ignore_dirs_enable
);
345 mc_config_set_string (mc_global
.main_config
, "FindFile", "ignore_dirs", options
.ignore_dirs
);
348 /* --------------------------------------------------------------------------------------------- */
351 add_to_list (const char *text
, void *data
)
353 return listbox_add_item (find_list
, LISTBOX_APPEND_AT_END
, 0, text
, data
, TRUE
);
356 /* --------------------------------------------------------------------------------------------- */
359 add_to_list_take (char *text
, void *data
)
361 return listbox_add_item_take (find_list
, LISTBOX_APPEND_AT_END
, 0, text
, data
, TRUE
);
364 /* --------------------------------------------------------------------------------------------- */
367 stop_idle (void *data
)
369 widget_idle (WIDGET (data
), FALSE
);
372 /* --------------------------------------------------------------------------------------------- */
375 status_update (const char *text
)
377 label_set_text (status_label
, text
);
380 /* --------------------------------------------------------------------------------------------- */
383 found_num_update (void)
385 label_set_textv (found_num_label
, _("Found: %lu"), matches
);
388 /* --------------------------------------------------------------------------------------------- */
391 get_list_info (char **file
, char **dir
, gsize
*start
, gsize
*end
)
393 find_match_location_t
*location
;
395 listbox_get_current (find_list
, file
, (void **) &location
);
396 if (location
!= NULL
)
399 *dir
= location
->dir
;
401 *start
= location
->start
;
403 *end
= location
->end
;
412 /* --------------------------------------------------------------------------------------------- */
413 /** check regular expression */
416 find_check_regexp (const char *r
)
419 gboolean regexp_ok
= FALSE
;
421 search
= mc_search_new (r
, NULL
);
425 search
->search_type
= MC_SEARCH_T_REGEX
;
426 regexp_ok
= mc_search_prepare (search
);
427 mc_search_free (search
);
433 /* --------------------------------------------------------------------------------------------- */
436 find_toggle_enable_ignore_dirs (void)
438 widget_disable (WIDGET (in_ignore
), !ignore_dirs_cbox
->state
);
441 /* --------------------------------------------------------------------------------------------- */
444 find_toggle_enable_params (void)
446 gboolean disable
= input_is_empty (in_name
);
448 widget_disable (WIDGET (file_pattern_cbox
), disable
);
449 widget_disable (WIDGET (file_case_sens_cbox
), disable
);
451 widget_disable (WIDGET (file_all_charsets_cbox
), disable
);
455 /* --------------------------------------------------------------------------------------------- */
458 find_toggle_enable_content (void)
460 widget_disable (WIDGET (content_regexp_cbox
), content_is_empty
);
461 widget_disable (WIDGET (content_case_sens_cbox
), content_is_empty
);
463 widget_disable (WIDGET (content_all_charsets_cbox
), content_is_empty
);
465 widget_disable (WIDGET (content_whole_words_cbox
), content_is_empty
);
466 widget_disable (WIDGET (content_first_hit_cbox
), content_is_empty
);
469 /* --------------------------------------------------------------------------------------------- */
471 * Callback for the parameter dialog.
472 * Validate regex, prevent closing the dialog if it's invalid.
476 find_parm_callback (Widget
*w
, Widget
*sender
, widget_msg_t msg
, int parm
, void *data
)
478 /* FIXME: HACK: use first draw of dialog to resolve widget state dependencies.
479 * Use this time moment to check input field content. We can't do that in MSG_INIT
480 * because history is not loaded yet.
481 * Probably, we want new MSG_ACTIVATE message as complement to MSG_VALIDATE one. Or
482 * we could name it MSG_POST_INIT.
484 * In one or two other places we use MSG_IDLE instead of MSG_DRAW for a similar
485 * purpose. We should remember to fix those places too when we introduce the new
488 static gboolean first_draw
= TRUE
;
490 WDialog
*h
= DIALOG (w
);
495 group_default_callback (w
, NULL
, MSG_INIT
, 0, NULL
);
500 if (sender
== WIDGET (ignore_dirs_cbox
))
502 find_toggle_enable_ignore_dirs ();
506 return MSG_NOT_HANDLED
;
509 if (h
->ret_value
!= B_ENTER
)
512 /* check filename regexp */
513 if (!file_pattern_cbox
->state
&& !input_is_empty (in_name
)
514 && !find_check_regexp (input_get_ctext (in_name
)))
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_name
));
523 /* check content regexp */
524 if (content_regexp_cbox
->state
&& !content_is_empty
525 && !find_check_regexp (input_get_ctext (in_with
)))
527 /* Don't stop the dialog */
528 widget_set_state (w
, WST_ACTIVE
, TRUE
);
529 message (D_ERROR
, MSG_ERROR
, _("Malformed regular expression"));
530 widget_select (WIDGET (in_with
));
537 if (GROUP (h
)->current
->data
== in_name
)
538 find_toggle_enable_params ();
539 else if (GROUP (h
)->current
->data
== in_with
)
541 content_is_empty
= input_is_empty (in_with
);
542 find_toggle_enable_content ();
549 find_toggle_enable_ignore_dirs ();
550 find_toggle_enable_params ();
551 find_toggle_enable_content ();
555 MC_FALLTHROUGH
; /* to call MSG_DRAW default handler */
558 return dlg_default_callback (w
, sender
, msg
, parm
, data
);
562 /* --------------------------------------------------------------------------------------------- */
564 * find_parameters: gets information from the user
566 * If the return value is TRUE, then the following holds:
568 * start_dir, ignore_dirs, pattern and content contain the information provided by the user.
569 * They are newly allocated strings and must be freed when unneeded.
571 * start_dir_len is -1 when user entered an absolute path, otherwise it is a length
572 * of start_dir (which is absolute). It is used to get a relative pats of find results.
576 find_parameters (WPanel
*panel
, char **start_dir
, ssize_t
*start_dir_len
,
577 char **ignore_dirs
, char **pattern
, char **content
)
581 /* Size of the find parameters window */
583 const int lines
= 19;
585 const int lines
= 18;
589 gboolean return_value
;
592 const char *file_name_label
= N_("File name:");
593 const char *file_recurs_label
= N_("&Find recursively");
594 const char *file_follow_symlinks
= N_("Follow s&ymlinks");
595 const char *file_pattern_label
= N_("&Using shell patterns");
597 const char *file_all_charsets_label
= N_("&All charsets");
599 const char *file_case_label
= N_("Cas&e sensitive");
600 const char *file_skip_hidden_label
= N_("S&kip hidden");
603 const char *content_content_label
= N_("Content:");
604 const char *content_use_label
= N_("Sea&rch for content");
605 const char *content_regexp_label
= N_("Re&gular expression");
606 const char *content_case_label
= N_("Case sens&itive");
608 const char *content_all_charsets_label
= N_("A&ll charsets");
610 const char *content_whole_words_label
= N_("&Whole words");
611 const char *content_first_hit_label
= N_("Fir&st hit");
613 const char *buts
[] = { N_("&Tree"), N_("&OK"), N_("&Cancel") };
625 file_name_label
= _(file_name_label
);
626 file_recurs_label
= _(file_recurs_label
);
627 file_follow_symlinks
= _(file_follow_symlinks
);
628 file_pattern_label
= _(file_pattern_label
);
630 file_all_charsets_label
= _(file_all_charsets_label
);
632 file_case_label
= _(file_case_label
);
633 file_skip_hidden_label
= _(file_skip_hidden_label
);
636 content_content_label
= _(content_content_label
);
637 content_use_label
= _(content_use_label
);
638 content_regexp_label
= _(content_regexp_label
);
639 content_case_label
= _(content_case_label
);
641 content_all_charsets_label
= _(content_all_charsets_label
);
643 content_whole_words_label
= _(content_whole_words_label
);
644 content_first_hit_label
= _(content_first_hit_label
);
646 for (i
= 0; i
< G_N_ELEMENTS (buts
); i
++)
647 buts
[i
] = _(buts
[i
]);
649 #endif /* ENABLE_NLS */
651 /* calculate dialog width */
654 cw
= str_term_width1 (file_name_label
);
655 cw
= max (cw
, str_term_width1 (file_recurs_label
) + 4);
656 cw
= max (cw
, str_term_width1 (file_follow_symlinks
) + 4);
657 cw
= max (cw
, str_term_width1 (file_pattern_label
) + 4);
659 cw
= max (cw
, str_term_width1 (file_all_charsets_label
) + 4);
661 cw
= max (cw
, str_term_width1 (file_case_label
) + 4);
662 cw
= max (cw
, str_term_width1 (file_skip_hidden_label
) + 4);
664 cw
= max (cw
, str_term_width1 (content_content_label
) + 4);
665 cw
= max (cw
, str_term_width1 (content_use_label
) + 4);
666 cw
= max (cw
, str_term_width1 (content_regexp_label
) + 4);
667 cw
= max (cw
, str_term_width1 (content_case_label
) + 4);
669 cw
= max (cw
, str_term_width1 (content_all_charsets_label
) + 4);
671 cw
= max (cw
, str_term_width1 (content_whole_words_label
) + 4);
672 cw
= max (cw
, str_term_width1 (content_first_hit_label
) + 4);
675 b0
= str_term_width1 (buts
[0]) + 3;
676 b1
= str_term_width1 (buts
[1]) + 5; /* default button */
677 b2
= str_term_width1 (buts
[2]) + 3;
680 cols
= max (cols
, max (b12
, cw
* 2 + 1) + 6);
682 find_load_options ();
684 if (in_start_dir
== NULL
)
685 in_start_dir
= g_strdup (".");
688 dlg_create (TRUE
, 0, 0, lines
, cols
, WPOS_CENTER
, FALSE
, dialog_colors
, find_parm_callback
,
689 NULL
, "[Find File]", _("Find File"));
690 g
= GROUP (find_dlg
);
697 group_add_widget (g
, label_new (y1
++, x1
, _("Start at:")));
699 input_new (y1
, x1
, input_colors
, cols
- b0
- 7, in_start_dir
, "start",
700 INPUT_COMPLETE_CD
| INPUT_COMPLETE_FILENAMES
);
701 group_add_widget (g
, in_start
);
703 group_add_widget (g
, button_new (y1
++, cols
- b0
- 3, B_TREE
, NORMAL_BUTTON
, buts
[0], NULL
));
706 check_new (y1
++, x1
, options
.ignore_dirs_enable
, _("Ena&ble ignore directories:"));
707 group_add_widget (g
, ignore_dirs_cbox
);
710 input_new (y1
++, x1
, input_colors
, cols
- 6,
711 options
.ignore_dirs
!= NULL
? options
.ignore_dirs
: "", "ignoredirs",
712 INPUT_COMPLETE_CD
| INPUT_COMPLETE_FILENAMES
);
713 group_add_widget (g
, in_ignore
);
715 group_add_widget (g
, hline_new (y1
++, -1, -1));
719 /* Start 1st column */
720 group_add_widget (g
, label_new (y1
++, x1
, file_name_label
));
722 input_new (y1
++, x1
, input_colors
, cw
, INPUT_LAST_TEXT
, "name",
723 INPUT_COMPLETE_FILENAMES
| INPUT_COMPLETE_CD
);
724 group_add_widget (g
, in_name
);
726 /* Start 2nd column */
727 content_label
= label_new (y2
++, x2
, content_content_label
);
728 group_add_widget (g
, content_label
);
730 input_new (y2
++, x2
, input_colors
, cw
, content_is_empty
? "" : INPUT_LAST_TEXT
,
731 MC_HISTORY_SHARED_SEARCH
, INPUT_COMPLETE_NONE
);
732 in_with
->label
= content_label
;
733 group_add_widget (g
, in_with
);
735 /* Continue 1st column */
736 recursively_cbox
= check_new (y1
++, x1
, options
.find_recurs
, file_recurs_label
);
737 group_add_widget (g
, recursively_cbox
);
739 follow_sym_cbox
= check_new (y1
++, x1
, options
.follow_symlinks
, file_follow_symlinks
);
740 group_add_widget (g
, follow_sym_cbox
);
742 file_pattern_cbox
= check_new (y1
++, x1
, options
.file_pattern
, file_pattern_label
);
743 group_add_widget (g
, file_pattern_cbox
);
745 file_case_sens_cbox
= check_new (y1
++, x1
, options
.file_case_sens
, file_case_label
);
746 group_add_widget (g
, file_case_sens_cbox
);
749 file_all_charsets_cbox
=
750 check_new (y1
++, x1
, options
.file_all_charsets
, file_all_charsets_label
);
751 group_add_widget (g
, file_all_charsets_cbox
);
754 skip_hidden_cbox
= check_new (y1
++, x1
, options
.skip_hidden
, file_skip_hidden_label
);
755 group_add_widget (g
, skip_hidden_cbox
);
757 /* Continue 2nd column */
758 content_whole_words_cbox
=
759 check_new (y2
++, x2
, options
.content_whole_words
, content_whole_words_label
);
760 group_add_widget (g
, content_whole_words_cbox
);
762 content_regexp_cbox
= check_new (y2
++, x2
, options
.content_regexp
, content_regexp_label
);
763 group_add_widget (g
, content_regexp_cbox
);
765 content_case_sens_cbox
= check_new (y2
++, x2
, options
.content_case_sens
, content_case_label
);
766 group_add_widget (g
, content_case_sens_cbox
);
769 content_all_charsets_cbox
=
770 check_new (y2
++, x2
, options
.content_all_charsets
, content_all_charsets_label
);
771 group_add_widget (g
, content_all_charsets_cbox
);
774 content_first_hit_cbox
=
775 check_new (y2
++, x2
, options
.content_first_hit
, content_first_hit_label
);
776 group_add_widget (g
, content_first_hit_cbox
);
780 x1
= (cols
- b12
) / 2;
781 group_add_widget (g
, hline_new (y1
++, -1, -1));
782 group_add_widget (g
, button_new (y1
, x1
, B_ENTER
, DEFPUSH_BUTTON
, buts
[1], NULL
));
783 group_add_widget (g
, button_new (y1
, x1
+ b1
+ 1, B_CANCEL
, NORMAL_BUTTON
, buts
[2], NULL
));
786 widget_select (WIDGET (in_name
));
788 switch (dlg_run (find_dlg
))
791 return_value
= FALSE
;
796 const char *start_cstr
;
797 const char *temp_dir
;
799 start_cstr
= input_get_ctext (in_start
);
801 if (input_is_empty (in_start
) || DIR_IS_DOT (start_cstr
))
802 temp_dir
= vfs_path_as_str (panel
->cwd_vpath
);
804 temp_dir
= start_cstr
;
806 if (in_start_dir
!= INPUT_LAST_TEXT
)
807 g_free (in_start_dir
);
808 in_start_dir
= tree_box (temp_dir
);
809 if (in_start_dir
== NULL
)
810 in_start_dir
= g_strdup (temp_dir
);
812 input_assign_text (in_start
, in_start_dir
);
814 /* Warning: Dreadful goto */
823 options
.file_all_charsets
= file_all_charsets_cbox
->state
;
824 options
.content_all_charsets
= content_all_charsets_cbox
->state
;
826 options
.content_case_sens
= content_case_sens_cbox
->state
;
827 options
.content_regexp
= content_regexp_cbox
->state
;
828 options
.content_first_hit
= content_first_hit_cbox
->state
;
829 options
.content_whole_words
= content_whole_words_cbox
->state
;
830 options
.find_recurs
= recursively_cbox
->state
;
831 options
.follow_symlinks
= follow_sym_cbox
->state
;
832 options
.file_pattern
= file_pattern_cbox
->state
;
833 options
.file_case_sens
= file_case_sens_cbox
->state
;
834 options
.skip_hidden
= skip_hidden_cbox
->state
;
835 options
.ignore_dirs_enable
= ignore_dirs_cbox
->state
;
836 g_free (options
.ignore_dirs
);
837 options
.ignore_dirs
= input_get_text (in_ignore
);
839 *content
= !input_is_empty (in_with
) ? input_get_text (in_with
) : NULL
;
840 if (input_is_empty (in_name
))
841 *pattern
= g_strdup (options
.file_pattern
? "*" : ".*");
843 *pattern
= input_get_text (in_name
);
844 *start_dir
= (char *) (!input_is_empty (in_start
) ? input_get_ctext (in_start
) : ".");
845 if (in_start_dir
!= INPUT_LAST_TEXT
)
846 g_free (in_start_dir
);
847 in_start_dir
= g_strdup (*start_dir
);
849 s
= tilde_expand (*start_dir
);
850 canonicalize_pathname (s
);
854 *start_dir
= g_strdup (vfs_path_as_str (panel
->cwd_vpath
));
855 /* FIXME: is panel->cwd_vpath canonicalized? */
856 /* relative paths will be used in panelization */
857 *start_dir_len
= (ssize_t
) strlen (*start_dir
);
860 else if (g_path_is_absolute (s
))
867 /* relative paths will be used in panelization */
869 mc_build_filename (vfs_path_as_str (panel
->cwd_vpath
), s
, (char *) NULL
);
870 *start_dir_len
= (ssize_t
) vfs_path_len (panel
->cwd_vpath
);
874 if (!options
.ignore_dirs_enable
|| input_is_empty (in_ignore
)
875 || DIR_IS_DOT (input_get_ctext (in_ignore
)))
878 *ignore_dirs
= input_get_text (in_ignore
);
880 find_save_options ();
886 widget_destroy (WIDGET (find_dlg
));
891 /* --------------------------------------------------------------------------------------------- */
894 push_directory (vfs_path_t
*dir
)
896 g_queue_push_head (&dir_queue
, (void *) dir
);
899 /* --------------------------------------------------------------------------------------------- */
901 static inline vfs_path_t
*
904 return (vfs_path_t
*) g_queue_pop_head (&dir_queue
);
907 /* --------------------------------------------------------------------------------------------- */
910 queue_dir_free (gpointer data
)
912 vfs_path_free ((vfs_path_t
*) data
, TRUE
);
915 /* --------------------------------------------------------------------------------------------- */
916 /** Remove all the items from the stack */
921 g_queue_clear_full (&dir_queue
, queue_dir_free
);
924 /* --------------------------------------------------------------------------------------------- */
927 insert_file (const char *dir
, const char *file
, gsize start
, gsize end
)
930 static char *dirname
= NULL
;
931 find_match_location_t
*location
;
933 while (IS_PATH_SEP (dir
[0]) && IS_PATH_SEP (dir
[1]))
938 if (strcmp (old_dir
, dir
) != 0)
941 old_dir
= g_strdup (dir
);
942 dirname
= add_to_list (dir
, NULL
);
947 old_dir
= g_strdup (dir
);
948 dirname
= add_to_list (dir
, NULL
);
951 tmp_name
= g_strdup_printf (" %s", file
);
952 location
= g_malloc (sizeof (*location
));
953 location
->dir
= dirname
;
954 location
->start
= start
;
956 add_to_list_take (tmp_name
, location
);
959 /* --------------------------------------------------------------------------------------------- */
962 find_add_match (const char *dir
, const char *file
, gsize start
, gsize end
)
964 insert_file (dir
, file
, start
, end
);
968 listbox_select_first (find_list
);
969 widget_draw (WIDGET (find_list
));
975 /* --------------------------------------------------------------------------------------------- */
977 static FindProgressStatus
978 check_find_events (WDialog
*h
)
984 c
= tty_get_event (&event
, GROUP (h
)->mouse_status
== MOU_REPEAT
, FALSE
);
987 dlg_process_event (h
, c
, &event
);
988 if (h
->ret_value
== B_ENTER
989 || h
->ret_value
== B_CANCEL
|| h
->ret_value
== B_AGAIN
|| h
->ret_value
== B_PANELIZE
)
991 /* dialog terminated */
994 if (!widget_get_state (WIDGET (h
), WST_IDLE
))
996 /* searching suspended */
1004 /* --------------------------------------------------------------------------------------------- */
1008 * Search the content_pattern string in the DIRECTORY/FILE.
1009 * It will add the found entries to the find listbox.
1011 * returns FALSE if do_search should look for another file
1012 * TRUE if do_search should exit and proceed to the event handler
1016 search_content (WDialog
*h
, const char *directory
, const char *filename
)
1019 char buffer
[BUF_4K
] = ""; /* raw input buffer */
1021 gboolean ret_val
= FALSE
;
1024 gboolean status_updated
= FALSE
;
1026 vpath
= vfs_path_build_filename (directory
, filename
, (char *) NULL
);
1028 if (mc_stat (vpath
, &s
) == 0 && S_ISREG (s
.st_mode
))
1029 file_fd
= mc_open (vpath
, O_RDONLY
);
1031 vfs_path_free (vpath
, TRUE
);
1036 /* get time elapsed from last refresh */
1037 tv
= g_get_monotonic_time ();
1039 if (s
.st_size
>= MIN_REFRESH_FILE_SIZE
|| (tv
- last_refresh
) > MAX_REFRESH_INTERVAL
)
1041 g_snprintf (buffer
, sizeof (buffer
), _("Grepping in %s"), filename
);
1042 status_update (str_trunc (buffer
, WIDGET (h
)->rect
.cols
- 8));
1045 status_updated
= TRUE
;
1048 tty_enable_interrupt_key ();
1049 tty_got_interrupt ();
1055 off_t off
= 0; /* file_fd's offset corresponding to strbuf[0] */
1056 gboolean found
= FALSE
;
1057 char *strbuf
= NULL
; /* buffer for fetched string */
1058 int strbuf_size
= 0;
1059 int i
= -1; /* compensate for a newline we'll add when we first enter the loop */
1063 /* We've been previously suspended, start from the previous position */
1076 off
+= i
+ 1; /* the previous line, plus a newline character */
1079 /* read to buffer and get line from there */
1085 n_read
= mc_read (file_fd
, buffer
, sizeof (buffer
));
1093 /* skip possible leading zero(s) */
1102 if (i
>= strbuf_size
- 1)
1105 strbuf
= g_realloc (strbuf
, strbuf_size
);
1120 /* if (ch == '\n'): do not search in empty strings */
1126 if (!found
/* Search in binary line once */
1127 && mc_search_run (search_content_handle
, (const void *) strbuf
, 0, i
, &found_len
))
1130 char result
[BUF_MEDIUM
];
1132 if (!status_updated
)
1134 /* if we add results for a file, we have to ensure that
1135 name of this file is shown in status bar */
1136 g_snprintf (result
, sizeof (result
), _("Grepping in %s"), filename
);
1137 status_update (str_trunc (result
, WIDGET (h
)->rect
.cols
- 8));
1140 status_updated
= TRUE
;
1143 g_snprintf (result
, sizeof (result
), "%d:%s", line
, filename
);
1144 found_start
= off
+ search_content_handle
->normal_offset
+ 1; /* off by one: ticket 3280 */
1145 find_add_match (directory
, result
, found_start
, found_start
+ found_len
);
1149 if (found
&& options
.content_first_hit
)
1159 if ((line
& 0xff) == 0)
1161 FindProgressStatus res
;
1163 res
= check_find_events (h
);
1187 tty_disable_interrupt_key ();
1192 /* --------------------------------------------------------------------------------------------- */
1195 If dir is absolute, this means we're within dir and searching file here.
1196 If dir is relative, this means we're going to add dir to the directory stack.
1199 find_ignore_dir_search (const char *dir
, size_t len
)
1201 if (find_ignore_dirs
!= NULL
)
1203 const size_t dlen
= len
== (size_t) (-1) ? strlen (dir
) : len
;
1204 const unsigned char dabs
= g_path_is_absolute (dir
) ? 1 : 0;
1208 for (ignore_dir
= find_ignore_dirs
; *ignore_dir
!= NULL
; ignore_dir
++)
1210 const size_t ilen
= strlen (*ignore_dir
);
1211 const unsigned char iabs
= g_path_is_absolute (*ignore_dir
) ? 2 : 0;
1213 /* ignore dir is too long -- skip it */
1217 /* handle absolute and relative paths */
1218 switch (iabs
| dabs
)
1220 case 0: /* both paths are relative */
1221 case 3: /* both paths are absolute */
1222 /* if ignore dir is not a path of dir -- skip it */
1223 if (strncmp (dir
, *ignore_dir
, ilen
) == 0)
1225 /* be sure that ignore dir is not a part of dir like:
1226 ignore dir is "h", dir is "home" */
1227 if (dir
[ilen
] == '\0' || IS_PATH_SEP (dir
[ilen
]))
1231 case 1: /* dir is absolute, ignore_dir is relative */
1235 d
= strstr (dir
, *ignore_dir
);
1236 if (d
!= NULL
&& IS_PATH_SEP (d
[-1])
1237 && (d
[ilen
] == '\0' || IS_PATH_SEP (d
[ilen
])))
1241 case 2: /* dir is relative, ignore_dir is absolute */
1242 /* FIXME: skip this case */
1244 default: /* this cannot occurs */
1253 /* --------------------------------------------------------------------------------------------- */
1256 find_rotate_dash (const WDialog
*h
, gboolean show
)
1258 static size_t pos
= 0;
1259 static const char rotating_dash
[4] = "|/-\\";
1260 const Widget
*w
= CONST_WIDGET (h
);
1263 colors
= widget_get_colors (w
);
1264 tty_setcolor (colors
[DLG_COLOR_NORMAL
]);
1265 widget_gotoyx (h
, w
->rect
.lines
- 7, w
->rect
.cols
- 4);
1266 tty_print_char (show
? rotating_dash
[pos
] : ' ');
1267 pos
= (pos
+ 1) % sizeof (rotating_dash
);
1271 /* --------------------------------------------------------------------------------------------- */
1274 do_search (WDialog
*h
)
1276 static struct vfs_dirent
*dp
= NULL
;
1277 static DIR *dirp
= NULL
;
1278 static char *directory
= NULL
;
1279 static gboolean pop_start_dir
= TRUE
;
1280 struct stat tmp_stat
;
1282 unsigned short count
;
1285 { /* someone forces me to close dirp */
1291 MC_PTR_FREE (directory
);
1293 pop_start_dir
= TRUE
;
1297 for (count
= 0; count
< 32; count
++)
1307 while (dirp
== NULL
)
1309 vfs_path_t
*tmp_vpath
= NULL
;
1311 tty_setcolor (REVERSE_COLOR
);
1315 tmp_vpath
= pop_directory ();
1316 if (tmp_vpath
== NULL
)
1319 if (ignore_count
== 0)
1320 status_update (_("Finished"));
1323 char msg
[BUF_SMALL
];
1325 g_snprintf (msg
, sizeof (msg
),
1326 ngettext ("Finished (ignored %zu directory)",
1327 "Finished (ignored %zu directories)",
1328 ignore_count
), ignore_count
);
1329 status_update (msg
);
1332 find_rotate_dash (h
, FALSE
);
1337 /* The start directory is the first one in the stack (see do_find() below).
1338 Do not apply ignore_dir to it. */
1341 pop_start_dir
= FALSE
;
1345 pop_start_dir
= FALSE
;
1347 /* handle absolute ignore dirs here */
1348 if (!find_ignore_dir_search (vfs_path_as_str (tmp_vpath
), -1))
1351 vfs_path_free (tmp_vpath
, TRUE
);
1359 char buffer
[BUF_MEDIUM
];
1361 directory
= (char *) vfs_path_as_str (tmp_vpath
);
1362 g_snprintf (buffer
, sizeof (buffer
), _("Searching %s"), directory
);
1363 status_update (str_trunc (directory
, WIDGET (h
)->rect
.cols
- 8));
1366 dirp
= mc_opendir (tmp_vpath
);
1367 directory
= vfs_path_free (tmp_vpath
, FALSE
);
1368 } /* while (!dirp) */
1370 /* skip invalid filenames */
1371 while ((dp
= mc_readdir (dirp
)) != NULL
&& !str_is_valid_string (dp
->d_name
))
1375 if (DIR_IS_DOT (dp
->d_name
) || DIR_IS_DOTDOT (dp
->d_name
))
1377 /* skip invalid filenames */
1378 while ((dp
= mc_readdir (dirp
)) != NULL
&& !str_is_valid_string (dp
->d_name
))
1384 if (!(options
.skip_hidden
&& (dp
->d_name
[0] == '.')))
1388 if (options
.find_recurs
&& (directory
!= NULL
))
1389 { /* Can directory be NULL ? */
1390 /* handle relative ignore dirs here */
1391 if (options
.ignore_dirs_enable
&& find_ignore_dir_search (dp
->d_name
, dp
->d_len
))
1395 vfs_path_t
*tmp_vpath
;
1398 tmp_vpath
= vfs_path_build_filename (directory
, dp
->d_name
, (char *) NULL
);
1400 if (options
.follow_symlinks
)
1401 stat_res
= mc_stat (tmp_vpath
, &tmp_stat
);
1403 stat_res
= mc_lstat (tmp_vpath
, &tmp_stat
);
1405 if (stat_res
== 0 && S_ISDIR (tmp_stat
.st_mode
))
1406 push_directory (tmp_vpath
);
1408 vfs_path_free (tmp_vpath
, TRUE
);
1412 search_ok
= mc_search_run (search_file_handle
, dp
->d_name
, 0, dp
->d_len
, &bytes_found
);
1416 if (content_pattern
== NULL
)
1417 find_add_match (directory
, dp
->d_name
, 0, 0);
1418 else if (search_content (h
, directory
, dp
->d_name
))
1423 /* skip invalid filenames */
1424 while ((dp
= mc_readdir (dirp
)) != NULL
&& !str_is_valid_string (dp
->d_name
))
1429 find_rotate_dash (h
, TRUE
);
1434 /* --------------------------------------------------------------------------------------------- */
1437 init_find_vars (void)
1439 MC_PTR_FREE (old_dir
);
1443 /* Remove all the items from the stack */
1446 g_strfreev (find_ignore_dirs
);
1447 find_ignore_dirs
= NULL
;
1450 /* --------------------------------------------------------------------------------------------- */
1453 find_do_view_edit (gboolean unparsed_view
, gboolean edit
, char *dir
, char *file
, off_t search_start
,
1456 const char *filename
= NULL
;
1458 vfs_path_t
*fullname_vpath
;
1460 if (content_pattern
!= NULL
)
1462 filename
= strchr (file
+ 4, ':') + 1;
1463 line
= atoi (file
+ 4);
1467 filename
= file
+ 4;
1471 fullname_vpath
= vfs_path_build_filename (dir
, filename
, (char *) NULL
);
1473 edit_file_at_line (fullname_vpath
, use_internal_edit
, line
);
1475 view_file_at_line (fullname_vpath
, unparsed_view
, use_internal_view
, line
, search_start
,
1477 vfs_path_free (fullname_vpath
, TRUE
);
1480 /* --------------------------------------------------------------------------------------------- */
1483 view_edit_currently_selected_file (gboolean unparsed_view
, gboolean edit
)
1486 find_match_location_t
*location
;
1488 listbox_get_current (find_list
, &text
, (void **) &location
);
1490 if ((text
== NULL
) || (location
== NULL
) || (location
->dir
== NULL
))
1491 return MSG_NOT_HANDLED
;
1493 find_do_view_edit (unparsed_view
, edit
, location
->dir
, text
, location
->start
, location
->end
);
1497 /* --------------------------------------------------------------------------------------------- */
1500 find_calc_button_locations (const WDialog
*h
, gboolean all_buttons
)
1502 const int cols
= CONST_WIDGET (h
)->rect
.cols
;
1506 l1
= fbuts
[0].len
+ fbuts
[1].len
+ fbuts
[is_start
? 3 : 2].len
+ fbuts
[4].len
+ 3;
1507 l2
= fbuts
[5].len
+ fbuts
[6].len
+ fbuts
[7].len
+ 2;
1509 fbuts
[0].x
= (cols
- l1
) / 2;
1510 fbuts
[1].x
= fbuts
[0].x
+ fbuts
[0].len
+ 1;
1511 fbuts
[2].x
= fbuts
[1].x
+ fbuts
[1].len
+ 1;
1512 fbuts
[3].x
= fbuts
[2].x
;
1513 fbuts
[4].x
= fbuts
[2].x
+ fbuts
[is_start
? 3 : 2].len
+ 1;
1517 fbuts
[5].x
= (cols
- l2
) / 2;
1518 fbuts
[6].x
= fbuts
[5].x
+ fbuts
[5].len
+ 1;
1519 fbuts
[7].x
= fbuts
[6].x
+ fbuts
[6].len
+ 1;
1523 /* --------------------------------------------------------------------------------------------- */
1526 find_adjust_header (WDialog
*h
)
1528 char title
[BUF_MEDIUM
];
1531 if (content_pattern
!= NULL
)
1532 g_snprintf (title
, sizeof (title
), _("Find File: \"%s\". Content: \"%s\""), find_pattern
,
1535 g_snprintf (title
, sizeof (title
), _("Find File: \"%s\""), find_pattern
);
1537 title_len
= str_term_width1 (title
);
1538 if (title_len
> WIDGET (h
)->rect
.cols
- 6)
1540 /* title is too wide, truncate it */
1541 title_len
= WIDGET (h
)->rect
.cols
- 6;
1542 title_len
= str_column_to_pos (title
, title_len
);
1543 title_len
-= 3; /* reserve space for three dots */
1544 title_len
= str_offset_to_pos (title
, title_len
);
1545 /* mark that title is truncated */
1546 memmove (title
+ title_len
, "...", 4);
1549 frame_set_title (FRAME (h
->bg
), title
);
1552 /* --------------------------------------------------------------------------------------------- */
1555 find_relocate_buttons (const WDialog
*h
, gboolean all_buttons
)
1559 find_calc_button_locations (h
, all_buttons
);
1561 for (i
= 0; i
< fbuts_num
; i
++)
1562 fbuts
[i
].button
->rect
.x
= CONST_WIDGET (h
)->rect
.x
+ fbuts
[i
].x
;
1565 /* --------------------------------------------------------------------------------------------- */
1568 find_resize (WDialog
*h
)
1570 Widget
*w
= WIDGET (h
);
1573 r
.lines
= LINES
- 4;
1575 dlg_default_callback (w
, NULL
, MSG_RESIZE
, 0, &r
);
1576 find_adjust_header (h
);
1577 find_relocate_buttons (h
, TRUE
);
1582 /* --------------------------------------------------------------------------------------------- */
1585 find_callback (Widget
*w
, Widget
*sender
, widget_msg_t msg
, int parm
, void *data
)
1587 WDialog
*h
= DIALOG (w
);
1592 group_default_callback (w
, NULL
, MSG_INIT
, 0, NULL
);
1593 find_adjust_header (h
);
1597 if (parm
== KEY_F (3) || parm
== KEY_F (13))
1599 gboolean unparsed_view
= (parm
== KEY_F (13));
1601 return view_edit_currently_selected_file (unparsed_view
, FALSE
);
1603 if (parm
== KEY_F (4))
1604 return view_edit_currently_selected_file (FALSE
, TRUE
);
1605 return MSG_NOT_HANDLED
;
1608 return find_resize (h
);
1615 return dlg_default_callback (w
, sender
, msg
, parm
, data
);
1619 /* --------------------------------------------------------------------------------------------- */
1620 /** Handles the Stop/Start button in the find window */
1623 start_stop (WButton
*button
, int action
)
1625 Widget
*w
= WIDGET (button
);
1630 widget_idle (WIDGET (find_dlg
), running
);
1631 is_start
= !is_start
;
1633 status_update (is_start
? _("Stopped") : _("Searching"));
1634 button_set_text (button
, fbuts
[is_start
? 3 : 2].text
);
1636 find_relocate_buttons (DIALOG (w
->owner
), FALSE
);
1637 widget_draw (WIDGET (w
->owner
));
1642 /* --------------------------------------------------------------------------------------------- */
1643 /** Handle view command, when invoked as a button */
1646 find_do_view_file (WButton
*button
, int action
)
1651 view_edit_currently_selected_file (FALSE
, FALSE
);
1655 /* --------------------------------------------------------------------------------------------- */
1656 /** Handle edit command, when invoked as a button */
1659 find_do_edit_file (WButton
*button
, int action
)
1664 view_edit_currently_selected_file (FALSE
, TRUE
);
1668 /* --------------------------------------------------------------------------------------------- */
1678 static gboolean i18n_flag
= FALSE
;
1682 for (i
= 0; i
< fbuts_num
; i
++)
1685 fbuts
[i
].text
= _(fbuts
[i
].text
);
1686 #endif /* ENABLE_NLS */
1687 fbuts
[i
].len
= str_term_width1 (fbuts
[i
].text
) + 3;
1688 if (fbuts
[i
].flags
== DEFPUSH_BUTTON
)
1699 dlg_create (TRUE
, 0, 0, lines
, cols
, WPOS_CENTER
, FALSE
, dialog_colors
, find_callback
, NULL
,
1700 "[Find File]", NULL
);
1701 g
= GROUP (find_dlg
);
1703 find_calc_button_locations (find_dlg
, TRUE
);
1706 find_list
= listbox_new (y
, 2, lines
- 10, cols
- 4, FALSE
, NULL
);
1707 group_add_widget_autopos (g
, find_list
, WPOS_KEEP_ALL
, NULL
);
1708 y
+= WIDGET (find_list
)->rect
.lines
;
1710 group_add_widget_autopos (g
, hline_new (y
++, -1, -1), WPOS_KEEP_BOTTOM
, NULL
);
1712 found_num_label
= label_new (y
++, 4, NULL
);
1713 group_add_widget_autopos (g
, found_num_label
, WPOS_KEEP_BOTTOM
, NULL
);
1715 status_label
= label_new (y
++, 4, _("Searching"));
1716 group_add_widget_autopos (g
, status_label
, WPOS_KEEP_BOTTOM
, NULL
);
1718 group_add_widget_autopos (g
, hline_new (y
++, -1, -1), WPOS_KEEP_BOTTOM
, NULL
);
1720 for (i
= 0; i
< fbuts_num
; i
++)
1723 fbuts
[3].button
= fbuts
[2].button
;
1728 (y
, fbuts
[i
].x
, fbuts
[i
].ret_cmd
, fbuts
[i
].flags
, fbuts
[i
].text
,
1729 fbuts
[i
].callback
));
1730 group_add_widget_autopos (g
, fbuts
[i
].button
, WPOS_KEEP_BOTTOM
, NULL
);
1733 if (i
== quit_button
)
1737 widget_select (WIDGET (find_list
));
1740 /* --------------------------------------------------------------------------------------------- */
1747 search_content_handle
= mc_search_new (content_pattern
, NULL
);
1748 if (search_content_handle
)
1750 search_content_handle
->search_type
=
1751 options
.content_regexp
? MC_SEARCH_T_REGEX
: MC_SEARCH_T_NORMAL
;
1752 search_content_handle
->is_case_sensitive
= options
.content_case_sens
;
1753 search_content_handle
->whole_words
= options
.content_whole_words
;
1755 search_content_handle
->is_all_charsets
= options
.content_all_charsets
;
1758 search_file_handle
= mc_search_new (find_pattern
, NULL
);
1759 search_file_handle
->search_type
= options
.file_pattern
? MC_SEARCH_T_GLOB
: MC_SEARCH_T_REGEX
;
1760 search_file_handle
->is_case_sensitive
= options
.file_case_sens
;
1762 search_file_handle
->is_all_charsets
= options
.file_all_charsets
;
1764 search_file_handle
->is_entire_line
= options
.file_pattern
;
1768 widget_idle (WIDGET (find_dlg
), TRUE
);
1769 ret
= dlg_run (find_dlg
);
1771 mc_search_free (search_file_handle
);
1772 search_file_handle
= NULL
;
1773 mc_search_free (search_content_handle
);
1774 search_content_handle
= NULL
;
1779 /* --------------------------------------------------------------------------------------------- */
1784 Widget
*w
= WIDGET (find_dlg
);
1786 widget_idle (w
, FALSE
);
1790 /* --------------------------------------------------------------------------------------------- */
1793 do_find (WPanel
*panel
, const char *start_dir
, ssize_t start_dir_len
, const char *ignore_dirs
,
1794 char **dirname
, char **filename
)
1797 char *dir_tmp
= NULL
, *file_tmp
= NULL
;
1802 parse_ignore_dirs (ignore_dirs
);
1803 push_directory (vfs_path_from_str (start_dir
));
1805 return_value
= run_process ();
1807 /* Clear variables */
1810 get_list_info (&file_tmp
, &dir_tmp
, NULL
, NULL
);
1812 if (dir_tmp
!= NULL
)
1813 *dirname
= g_strdup (dir_tmp
);
1814 if (file_tmp
!= NULL
)
1815 *filename
= g_strdup (file_tmp
);
1817 if (return_value
== B_PANELIZE
&& *filename
!= NULL
)
1821 dir_list
*list
= &panel
->dir
;
1825 panel_clean_dir (panel
);
1826 dir_list_init (list
);
1828 for (entry
= listbox_get_first_link (find_list
); entry
!= NULL
&& ok
;
1829 entry
= g_list_next (entry
))
1831 const char *lc_filename
;
1832 WLEntry
*le
= LENTRY (entry
->data
);
1833 find_match_location_t
*location
= le
->data
;
1835 gboolean link_to_dir
, stale_link
;
1837 if ((le
->text
== NULL
) || (location
== NULL
) || (location
->dir
== NULL
))
1840 if (!content_is_empty
)
1841 lc_filename
= strchr (le
->text
+ 4, ':') + 1;
1843 lc_filename
= le
->text
+ 4;
1845 name
= mc_build_filename (location
->dir
, lc_filename
, (char *) NULL
);
1846 /* skip initial start dir */
1848 if (start_dir_len
> 0)
1849 p
+= (size_t) start_dir_len
;
1850 if (IS_PATH_SEP (*p
))
1853 if (!handle_path (p
, &st
, &link_to_dir
, &stale_link
))
1859 /* don't add files more than once to the panel */
1860 if (!content_is_empty
&& list
->len
!= 0
1861 && strcmp (list
->list
[list
->len
- 1].fname
->str
, p
) == 0)
1867 ok
= dir_list_append (list
, p
, &st
, link_to_dir
, stale_link
);
1871 if ((list
->len
& 15) == 0)
1875 panel
->is_panelized
= TRUE
;
1876 panel_panelize_absolutize_if_needed (panel
);
1877 panel_panelize_save (panel
);
1881 do_search (NULL
); /* force do_search to release resources */
1882 MC_PTR_FREE (old_dir
);
1883 rotate_dash (FALSE
);
1885 return return_value
;
1888 /* --------------------------------------------------------------------------------------------- */
1889 /*** public functions ****************************************************************************/
1890 /* --------------------------------------------------------------------------------------------- */
1893 find_cmd (WPanel
*panel
)
1895 char *start_dir
= NULL
, *ignore_dirs
= NULL
;
1896 ssize_t start_dir_len
;
1898 find_pattern
= NULL
;
1899 content_pattern
= NULL
;
1901 while (find_parameters (panel
, &start_dir
, &start_dir_len
,
1902 &ignore_dirs
, &find_pattern
, &content_pattern
))
1904 char *filename
= NULL
, *dirname
= NULL
;
1907 content_is_empty
= content_pattern
== NULL
;
1909 if (find_pattern
[0] != '\0')
1915 if (!content_is_empty
&& !str_is_valid_string (content_pattern
))
1916 MC_PTR_FREE (content_pattern
);
1918 v
= do_find (panel
, start_dir
, start_dir_len
, ignore_dirs
, &dirname
, &filename
);
1922 g_free (ignore_dirs
);
1923 MC_PTR_FREE (find_pattern
);
1927 if (dirname
!= NULL
)
1929 vfs_path_t
*dirname_vpath
;
1931 dirname_vpath
= vfs_path_from_str (dirname
);
1932 panel_cd (panel
, dirname_vpath
, cd_exact
);
1933 vfs_path_free (dirname_vpath
, TRUE
);
1935 if (filename
!= NULL
)
1939 if (content_pattern
== NULL
)
1942 offset
= strchr (filename
+ 4, ':') - filename
+ 1;
1944 panel_set_current_by_name (panel
, filename
+ offset
);
1947 else if (filename
!= NULL
)
1949 vfs_path_t
*filename_vpath
;
1951 filename_vpath
= vfs_path_from_str (filename
);
1952 panel_cd (panel
, filename_vpath
, cd_exact
);
1953 vfs_path_free (filename_vpath
, TRUE
);
1957 MC_PTR_FREE (content_pattern
);
1961 if (v
== B_ENTER
|| v
== B_CANCEL
)
1964 if (v
== B_PANELIZE
)
1966 panel_re_sort (panel
);
1967 panel_set_current_by_name (panel
, NULL
);
1973 /* --------------------------------------------------------------------------------------------- */