1 /* Find file command for the Midnight Commander
2 Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
3 2006, 2007 Free Software Foundation, Inc.
4 Written 1995 by Miguel de Icaza
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
23 * \brief Source: Find file command
35 #include "lib/global.h"
37 #include "lib/tty/tty.h"
38 #include "lib/tty/key.h"
40 #include "lib/search.h"
41 #include "lib/mcconfig.h"
42 #include "lib/strutil.h"
43 #include "lib/widget.h"
44 #include "lib/vfs/mc-vfs/vfs.h"
45 #include "lib/util.h" /* canonicalize_pathname() */
47 #include "src/setup.h" /* verbose */
48 #include "src/history.h" /* MC_HISTORY_SHARED_SEARCH */
49 #include "src/main.h" /* do_cd */
52 #include "cmd.h" /* view_file_at_line */
53 #include "midnight.h" /* current_panel */
55 #include "layout.h" /* mc_refresh() */
59 /*** global variables ****************************************************************************/
61 /*** file scope macro definitions ****************************************************************/
63 /* Size of the find window */
64 #define FIND2_Y (LINES - 4)
66 #define FIND2_X_USE (FIND2_X - 20)
68 /*** file scope type declarations ****************************************************************/
70 /* A couple of extra messages we need */
87 /* find file options */
90 /* file name options */
91 gboolean file_case_sens
;
92 gboolean file_pattern
;
95 gboolean file_all_charsets
;
97 /* file content options */
99 gboolean content_case_sens
;
100 gboolean content_regexp
;
101 gboolean content_first_hit
;
102 gboolean content_whole_words
;
103 gboolean content_all_charsets
;
104 } find_file_options_t
;
106 /*** file scope variables ************************************************************************/
108 /* Parsed ignore dirs */
109 static char **find_ignore_dirs
= NULL
;
111 /* Size of the find parameters window */
113 static int FIND_Y
= 17;
115 static int FIND_Y
= 16;
117 static int FIND_X
= 68;
119 static int FIND2_X
= 64;
121 /* static variables to remember find parameters */
122 static WInput
*in_start
; /* Start path */
123 static WInput
*in_name
; /* Filename */
124 static WInput
*in_with
; /* Text */
125 static WLabel
*content_label
; /* 'Content:' label */
126 static WCheck
*file_case_sens_cbox
; /* "case sensitive" checkbox */
127 static WCheck
*file_pattern_cbox
; /* File name is glob or regexp */
128 static WCheck
*recursively_cbox
;
129 static WCheck
*skip_hidden_cbox
;
130 static WCheck
*content_use_cbox
; /* Take into account the Content field */
131 static WCheck
*content_case_sens_cbox
; /* "case sensitive" checkbox */
132 static WCheck
*content_regexp_cbox
; /* "find regular expression" checkbox */
133 static WCheck
*content_first_hit_cbox
; /* "First hit" checkbox" */
134 static WCheck
*content_whole_words_cbox
; /* "whole words" checkbox */
136 static WCheck
*file_all_charsets_cbox
;
137 static WCheck
*content_all_charsets_cbox
;
140 static gboolean running
= FALSE
; /* nice flag */
141 static char *find_pattern
= NULL
; /* Pattern to search */
142 static char *content_pattern
= NULL
; /* pattern to search inside files; if
143 content_regexp_flag is true, it contains the
144 regex pattern, else the search string. */
145 static unsigned long matches
; /* Number of matches */
146 static gboolean is_start
= FALSE
; /* Status of the start/stop toggle button */
147 static char *old_dir
= NULL
;
149 /* Where did we stop */
151 static int last_line
;
154 static size_t ignore_count
= 0;
156 static Dlg_head
*find_dlg
; /* The dialog */
157 static WButton
*stop_button
; /* pointer to the stop button */
158 static WLabel
*status_label
; /* Finished, Searching etc. */
159 static WLabel
*found_num_label
; /* Number of found items */
160 static WListbox
*find_list
; /* Listbox with the file list */
162 /* This keeps track of the directory stack */
163 #if GLIB_CHECK_VERSION (2, 14, 0)
164 static GQueue dir_queue
= G_QUEUE_INIT
;
166 typedef struct dir_stack
169 struct dir_stack
*prev
;
172 static dir_stack
*dir_stack_base
= 0;
173 #endif /* GLIB_CHECK_VERSION */
179 int len
; /* length including space and brackets */
183 {N_("&Suspend"), 11, 29},
184 {N_("Con&tinue"), 12, 29},
185 {N_("&Chdir"), 11, 3},
186 {N_("&Again"), 9, 17},
187 {N_("&Quit"), 8, 43},
188 {N_("Pane&lize"), 12, 3},
189 {N_("&View - F3"), 13, 20},
190 {N_("&Edit - F4"), 13, 38}
194 static find_file_options_t options
= {
195 TRUE
, TRUE
, TRUE
, FALSE
, FALSE
,
196 FALSE
, TRUE
, FALSE
, FALSE
, FALSE
, FALSE
199 static char *in_start_dir
= INPUT_LAST_TEXT
;
201 static mc_search_t
*search_file_handle
= NULL
;
202 static mc_search_t
*search_content_handle
= NULL
;
204 /*** file scope functions ************************************************************************/
207 find_load_options (void)
209 static gboolean loaded
= FALSE
;
217 /* Back compatibility: try load old parameter at first */
218 ignore_dirs
= mc_config_get_string (mc_main_config
, "Misc", "find_ignore_dirs", "");
219 if (ignore_dirs
[0] != '\0')
221 find_ignore_dirs
= g_strsplit (ignore_dirs
, ":", -1);
222 mc_config_set_string (mc_main_config
, "FindFile", "ignore_dirs", ignore_dirs
);
224 g_free (ignore_dirs
);
225 mc_config_del_key (mc_main_config
, "Misc", "find_ignore_dirs");
227 /* Then load new parameters */
228 ignore_dirs
= mc_config_get_string (mc_main_config
, "FindFile", "ignore_dirs", "");
229 if (ignore_dirs
[0] != '\0')
231 g_strfreev (find_ignore_dirs
);
232 find_ignore_dirs
= g_strsplit (ignore_dirs
, ":", -1);
234 g_free (ignore_dirs
);
236 if (find_ignore_dirs
!= NULL
)
238 /* Values like '/foo::/bar: produce holes in list.
239 Find and remove them */
240 size_t r
= 0, w
= 0; /* read and write iterators */
242 for (; find_ignore_dirs
[r
] != NULL
; r
++)
244 if (find_ignore_dirs
[r
][0] == '\0')
246 /* empty entry -- skip it */
247 g_free (find_ignore_dirs
[r
]);
248 find_ignore_dirs
[r
] = NULL
;
254 /* copy entry to the previous free array cell */
255 find_ignore_dirs
[w
] = find_ignore_dirs
[r
];
256 find_ignore_dirs
[r
] = NULL
;
259 canonicalize_pathname (find_ignore_dirs
[w
]);
260 if (find_ignore_dirs
[w
][0] != '\0')
264 g_free (find_ignore_dirs
[w
]);
265 find_ignore_dirs
[w
] = NULL
;
269 if (find_ignore_dirs
[0] == NULL
)
271 g_strfreev (find_ignore_dirs
);
272 find_ignore_dirs
= NULL
;
276 options
.file_case_sens
=
277 mc_config_get_bool (mc_main_config
, "FindFile", "file_case_sens", TRUE
);
278 options
.file_pattern
=
279 mc_config_get_bool (mc_main_config
, "FindFile", "file_shell_pattern", TRUE
);
280 options
.find_recurs
= mc_config_get_bool (mc_main_config
, "FindFile", "file_find_recurs", TRUE
);
281 options
.skip_hidden
=
282 mc_config_get_bool (mc_main_config
, "FindFile", "file_skip_hidden", FALSE
);
283 options
.file_all_charsets
=
284 mc_config_get_bool (mc_main_config
, "FindFile", "file_all_charsets", FALSE
);
285 options
.content_use
= mc_config_get_bool (mc_main_config
, "FindFile", "content_use", FALSE
);
286 options
.content_case_sens
=
287 mc_config_get_bool (mc_main_config
, "FindFile", "content_case_sens", TRUE
);
288 options
.content_regexp
=
289 mc_config_get_bool (mc_main_config
, "FindFile", "content_regexp", FALSE
);
290 options
.content_first_hit
=
291 mc_config_get_bool (mc_main_config
, "FindFile", "content_first_hit", FALSE
);
292 options
.content_whole_words
=
293 mc_config_get_bool (mc_main_config
, "FindFile", "content_whole_words", FALSE
);
294 options
.content_all_charsets
=
295 mc_config_get_bool (mc_main_config
, "FindFile", "content_all_charsets", FALSE
);
298 /* --------------------------------------------------------------------------------------------- */
301 find_save_options (void)
303 mc_config_set_bool (mc_main_config
, "FindFile", "file_case_sens", options
.file_case_sens
);
304 mc_config_set_bool (mc_main_config
, "FindFile", "file_shell_pattern", options
.file_pattern
);
305 mc_config_set_bool (mc_main_config
, "FindFile", "file_find_recurs", options
.find_recurs
);
306 mc_config_set_bool (mc_main_config
, "FindFile", "file_skip_hidden", options
.skip_hidden
);
307 mc_config_set_bool (mc_main_config
, "FindFile", "file_all_charsets", options
.file_all_charsets
);
308 mc_config_set_bool (mc_main_config
, "FindFile", "content_use", options
.content_use
);
309 mc_config_set_bool (mc_main_config
, "FindFile", "content_case_sens", options
.content_case_sens
);
310 mc_config_set_bool (mc_main_config
, "FindFile", "content_regexp", options
.content_regexp
);
311 mc_config_set_bool (mc_main_config
, "FindFile", "content_first_hit", options
.content_first_hit
);
312 mc_config_set_bool (mc_main_config
, "FindFile", "content_whole_words",
313 options
.content_whole_words
);
314 mc_config_set_bool (mc_main_config
, "FindFile", "content_all_charsets",
315 options
.content_all_charsets
);
318 /* --------------------------------------------------------------------------------------------- */
321 add_to_list (const char *text
, void *data
)
323 return listbox_add_item (find_list
, LISTBOX_APPEND_AT_END
, 0, text
, data
);
326 /* --------------------------------------------------------------------------------------------- */
329 stop_idle (void *data
)
331 set_idle_proc (data
, 0);
334 /* --------------------------------------------------------------------------------------------- */
337 status_update (const char *text
)
339 label_set_text (status_label
, text
);
342 /* --------------------------------------------------------------------------------------------- */
345 found_num_update (void)
347 char buffer
[BUF_TINY
];
348 g_snprintf (buffer
, sizeof (buffer
), _("Found: %ld"), matches
);
349 label_set_text (found_num_label
, buffer
);
352 /* --------------------------------------------------------------------------------------------- */
355 get_list_info (char **file
, char **dir
)
357 listbox_get_current (find_list
, file
, (void **) dir
);
360 /* --------------------------------------------------------------------------------------------- */
361 /** check regular expression */
364 find_check_regexp (const char *r
)
367 gboolean regexp_ok
= FALSE
;
369 search
= mc_search_new (r
, -1);
373 search
->search_type
= MC_SEARCH_T_REGEX
;
374 regexp_ok
= mc_search_prepare (search
);
375 mc_search_free (search
);
381 /* --------------------------------------------------------------------------------------------- */
383 * Callback for the parameter dialog.
384 * Validate regex, prevent closing the dialog if it's invalid.
388 find_parm_callback (Dlg_head
* h
, Widget
* sender
, dlg_msg_t msg
, int parm
, void *data
)
393 if (sender
== (Widget
*) content_use_cbox
)
395 gboolean disable
= !(content_use_cbox
->state
& C_BOOL
);
397 widget_disable (content_label
->widget
, disable
);
398 send_message ((Widget
*) content_label
, WIDGET_DRAW
, 0);
399 widget_disable (in_with
->widget
, disable
);
400 send_message ((Widget
*) in_with
, WIDGET_DRAW
, 0);
401 widget_disable (content_first_hit_cbox
->widget
, disable
);
402 send_message ((Widget
*) content_first_hit_cbox
, WIDGET_DRAW
, 0);
403 widget_disable (content_regexp_cbox
->widget
, disable
);
404 send_message ((Widget
*) content_regexp_cbox
, WIDGET_DRAW
, 0);
405 widget_disable (content_case_sens_cbox
->widget
, disable
);
406 send_message ((Widget
*) content_case_sens_cbox
, WIDGET_DRAW
, 0);
408 widget_disable (content_all_charsets_cbox
->widget
, disable
);
409 send_message ((Widget
*) content_all_charsets_cbox
, WIDGET_DRAW
, 0);
411 widget_disable (content_whole_words_cbox
->widget
, disable
);
412 send_message ((Widget
*) content_whole_words_cbox
, WIDGET_DRAW
, 0);
417 return MSG_NOT_HANDLED
;
421 if (h
->ret_value
!= B_ENTER
)
424 /* check filename regexp */
425 if (!(file_pattern_cbox
->state
& C_BOOL
)
426 && (in_name
->buffer
[0] != '\0') && !find_check_regexp (in_name
->buffer
))
428 h
->state
= DLG_ACTIVE
; /* Don't stop the dialog */
429 message (D_ERROR
, MSG_ERROR
, _("Malformed regular expression"));
430 dlg_select_widget (in_name
);
434 /* check content regexp */
435 if ((content_use_cbox
->state
& C_BOOL
) && (content_regexp_cbox
->state
& C_BOOL
)
436 && (in_with
->buffer
[0] != '\0') && !find_check_regexp (in_with
->buffer
))
438 h
->state
= DLG_ACTIVE
; /* Don't stop the dialog */
439 message (D_ERROR
, MSG_ERROR
, _("Malformed regular expression"));
440 dlg_select_widget (in_with
);
447 return default_dlg_callback (h
, sender
, msg
, parm
, data
);
451 /* --------------------------------------------------------------------------------------------- */
453 * find_parameters: gets information from the user
455 * If the return value is TRUE, then the following holds:
457 * START_DIR and PATTERN are pointers to char * and upon return they
458 * contain the information provided by the user.
460 * CONTENT holds a strdup of the contents specified by the user if he
461 * asked for them or 0 if not (note, this is different from the
462 * behavior for the other two parameters.
467 find_parameters (char **start_dir
, char **pattern
, char **content
)
469 gboolean return_value
;
472 const char *file_case_label
= N_("Cas&e sensitive");
473 const char *file_pattern_label
= N_("&Using shell patterns");
474 const char *file_recurs_label
= N_("&Find recursively");
475 const char *file_skip_hidden_label
= N_("S&kip hidden");
477 const char *file_all_charsets_label
= N_("&All charsets");
481 const char *content_use_label
= N_("Sea&rch for content");
482 const char *content_case_label
= N_("Case sens&itive");
483 const char *content_regexp_label
= N_("Re&gular expression");
484 const char *content_first_hit_label
= N_("Fir&st hit");
485 const char *content_whole_words_label
= N_("&Whole words");
487 const char *content_all_charsets_label
= N_("A&ll charsets");
490 const char *buts
[] = { N_("&OK"), N_("&Cancel"), N_("&Tree") };
499 int i
= sizeof (buts
) / sizeof (buts
[0]);
501 buts
[i
] = _(buts
[i
]);
503 file_case_label
= _(file_case_label
);
504 file_pattern_label
= _(file_pattern_label
);
505 file_recurs_label
= _(file_recurs_label
);
506 file_skip_hidden_label
= _(file_skip_hidden_label
);
508 file_all_charsets_label
= _(file_all_charsets_label
);
509 content_all_charsets_label
= _(content_all_charsets_label
);
511 content_use_label
= _(content_use_label
);
512 content_case_label
= _(content_case_label
);
513 content_regexp_label
= _(content_regexp_label
);
514 content_first_hit_label
= _(content_first_hit_label
);
515 content_whole_words_label
= _(content_whole_words_label
);
517 #endif /* ENABLE_NLS */
519 b0
= str_term_width1 (buts
[0]) + 6; /* default button */
520 b1
= str_term_width1 (buts
[1]) + 4;
521 b2
= str_term_width1 (buts
[2]) + 4;
523 find_load_options ();
525 if (in_start_dir
== NULL
)
526 in_start_dir
= g_strdup (".");
528 disable
= !options
.content_use
;
531 create_dlg (TRUE
, 0, 0, FIND_Y
, FIND_X
, dialog_colors
,
532 find_parm_callback
, "[Find File]", _("Find File"), DLG_CENTER
| DLG_REVERSE
);
534 add_widget (find_dlg
,
535 button_new (FIND_Y
- 3, FIND_X
* 3 / 4 - b1
/ 2, B_CANCEL
, NORMAL_BUTTON
, buts
[1],
537 add_widget (find_dlg
,
538 button_new (FIND_Y
- 3, FIND_X
/ 4 - b0
/ 2, B_ENTER
, DEFPUSH_BUTTON
, buts
[0], 0));
540 cbox_position
= FIND_Y
- 5;
542 content_first_hit_cbox
=
543 check_new (cbox_position
--, FIND_X
/ 2 + 1, options
.content_first_hit
,
544 content_first_hit_label
);
545 widget_disable (content_first_hit_cbox
->widget
, disable
);
546 add_widget (find_dlg
, content_first_hit_cbox
);
548 content_whole_words_cbox
=
549 check_new (cbox_position
--, FIND_X
/ 2 + 1, options
.content_whole_words
,
550 content_whole_words_label
);
551 widget_disable (content_whole_words_cbox
->widget
, disable
);
552 add_widget (find_dlg
, content_whole_words_cbox
);
555 content_all_charsets_cbox
= check_new (cbox_position
--, FIND_X
/ 2 + 1,
556 options
.content_all_charsets
,
557 content_all_charsets_label
);
558 widget_disable (content_all_charsets_cbox
->widget
, disable
);
559 add_widget (find_dlg
, content_all_charsets_cbox
);
562 content_case_sens_cbox
=
563 check_new (cbox_position
--, FIND_X
/ 2 + 1, options
.content_case_sens
, content_case_label
);
564 widget_disable (content_case_sens_cbox
->widget
, disable
);
565 add_widget (find_dlg
, content_case_sens_cbox
);
567 content_regexp_cbox
=
568 check_new (cbox_position
--, FIND_X
/ 2 + 1, options
.content_regexp
, content_regexp_label
);
569 widget_disable (content_regexp_cbox
->widget
, disable
);
570 add_widget (find_dlg
, content_regexp_cbox
);
572 cbox_position
= FIND_Y
- 6;
574 skip_hidden_cbox
= check_new (cbox_position
--, 3, options
.skip_hidden
, file_skip_hidden_label
);
575 add_widget (find_dlg
, skip_hidden_cbox
);
578 file_all_charsets_cbox
=
579 check_new (cbox_position
--, 3, options
.file_all_charsets
, file_all_charsets_label
);
580 add_widget (find_dlg
, file_all_charsets_cbox
);
583 file_case_sens_cbox
= check_new (cbox_position
--, 3, options
.file_case_sens
, file_case_label
);
584 add_widget (find_dlg
, file_case_sens_cbox
);
586 file_pattern_cbox
= check_new (cbox_position
--, 3, options
.file_pattern
, file_pattern_label
);
587 add_widget (find_dlg
, file_pattern_cbox
);
589 recursively_cbox
= check_new (cbox_position
, 3, options
.find_recurs
, file_recurs_label
);
590 add_widget (find_dlg
, recursively_cbox
);
592 /* This checkbox is located in the second column */
594 check_new (cbox_position
, FIND_X
/ 2 + 1, options
.content_use
, content_use_label
);
595 add_widget (find_dlg
, content_use_cbox
);
598 input_new (6, FIND_X
/ 2 + 1, input_get_default_colors (), FIND_X
/ 2 - 4, INPUT_LAST_TEXT
,
599 MC_HISTORY_SHARED_SEARCH
, INPUT_COMPLETE_DEFAULT
);
600 widget_disable (in_with
->widget
, disable
);
601 add_widget (find_dlg
, in_with
);
603 content_label
= label_new (5, FIND_X
/ 2 + 1, _("Content:"));
604 widget_disable (content_label
->widget
, disable
);
605 add_widget (find_dlg
, content_label
);
607 in_name
= input_new (6, 3, input_get_default_colors (),
608 FIND_X
/ 2 - 4, INPUT_LAST_TEXT
, "name", INPUT_COMPLETE_DEFAULT
);
609 add_widget (find_dlg
, in_name
);
610 add_widget (find_dlg
, label_new (5, 3, _("File name:")));
612 add_widget (find_dlg
, button_new (3, FIND_X
- b2
- 2, B_TREE
, NORMAL_BUTTON
, buts
[2], 0));
614 in_start
= input_new (3, 3, input_get_default_colors (),
615 FIND_X
- b2
- 6, in_start_dir
, "start", INPUT_COMPLETE_DEFAULT
);
616 add_widget (find_dlg
, in_start
);
617 add_widget (find_dlg
, label_new (2, 3, _("Start at:")));
620 dlg_select_widget (in_name
);
622 switch (run_dlg (find_dlg
))
625 return_value
= FALSE
;
630 const char *temp_dir
= in_start
->buffer
;
632 if ((temp_dir
[0] == '\0') || ((temp_dir
[0] == '.') && (temp_dir
[1] == '\0')))
633 temp_dir
= current_panel
->cwd
;
635 if (in_start_dir
!= INPUT_LAST_TEXT
)
636 g_free (in_start_dir
);
637 in_start_dir
= tree_box (temp_dir
);
638 if (in_start_dir
== NULL
)
639 in_start_dir
= g_strdup (temp_dir
);
641 input_assign_text (in_start
, in_start_dir
);
643 /* Warning: Dreadful goto */
649 options
.file_all_charsets
= file_all_charsets_cbox
->state
& C_BOOL
;
650 options
.content_all_charsets
= content_all_charsets_cbox
->state
& C_BOOL
;
652 options
.content_use
= content_use_cbox
->state
& C_BOOL
;
653 options
.content_case_sens
= content_case_sens_cbox
->state
& C_BOOL
;
654 options
.content_regexp
= content_regexp_cbox
->state
& C_BOOL
;
655 options
.content_first_hit
= content_first_hit_cbox
->state
& C_BOOL
;
656 options
.content_whole_words
= content_whole_words_cbox
->state
& C_BOOL
;
657 options
.find_recurs
= recursively_cbox
->state
& C_BOOL
;
658 options
.file_pattern
= file_pattern_cbox
->state
& C_BOOL
;
659 options
.file_case_sens
= file_case_sens_cbox
->state
& C_BOOL
;
660 options
.skip_hidden
= skip_hidden_cbox
->state
& C_BOOL
;
662 *content
= (options
.content_use
&& in_with
->buffer
[0] != '\0')
663 ? g_strdup (in_with
->buffer
) : NULL
;
664 *start_dir
= in_start
->buffer
[0] != '\0' ? in_start
->buffer
: (char *) ".";
665 *pattern
= g_strdup (in_name
->buffer
);
666 if (in_start_dir
!= INPUT_LAST_TEXT
)
667 g_free (in_start_dir
);
668 in_start_dir
= g_strdup (*start_dir
);
669 if ((*start_dir
)[0] == '.' && (*start_dir
)[1] == '\0')
670 *start_dir
= g_strdup (current_panel
->cwd
);
671 else if (g_path_is_absolute (*start_dir
))
672 *start_dir
= g_strdup (*start_dir
);
674 *start_dir
= g_build_filename (current_panel
->cwd
, *start_dir
, (char *) NULL
);
676 canonicalize_pathname (*start_dir
);
678 find_save_options ();
683 destroy_dlg (find_dlg
);
688 /* --------------------------------------------------------------------------------------------- */
690 #if GLIB_CHECK_VERSION (2, 14, 0)
692 push_directory (const char *dir
)
694 g_queue_push_head (&dir_queue
, (void *) dir
);
697 /* --------------------------------------------------------------------------------------------- */
702 return (char *) g_queue_pop_tail (&dir_queue
);
705 /* --------------------------------------------------------------------------------------------- */
706 /** Remove all the items from the stack */
711 g_queue_foreach (&dir_queue
, (GFunc
) g_free
, NULL
);
712 g_queue_clear (&dir_queue
);
715 /* --------------------------------------------------------------------------------------------- */
717 #else /* GLIB_CHECK_VERSION */
719 push_directory (const char *dir
)
723 new = g_new (dir_stack
, 1);
724 new->name
= (char *) dir
;
725 new->prev
= dir_stack_base
;
726 dir_stack_base
= new;
729 /* --------------------------------------------------------------------------------------------- */
736 if (dir_stack_base
!= NULL
)
739 name
= dir_stack_base
->name
;
740 next
= dir_stack_base
->prev
;
741 g_free (dir_stack_base
);
742 dir_stack_base
= next
;
748 /* --------------------------------------------------------------------------------------------- */
749 /** Remove all the items from the stack */
755 while ((dir
= pop_directory ()) != NULL
)
758 #endif /* GLIB_CHECK_VERSION */
760 /* --------------------------------------------------------------------------------------------- */
763 insert_file (const char *dir
, const char *file
)
765 char *tmp_name
= NULL
;
766 static char *dirname
= NULL
;
768 while (dir
[0] == PATH_SEP
&& dir
[1] == PATH_SEP
)
773 if (strcmp (old_dir
, dir
))
776 old_dir
= g_strdup (dir
);
777 dirname
= add_to_list (dir
, NULL
);
782 old_dir
= g_strdup (dir
);
783 dirname
= add_to_list (dir
, NULL
);
786 tmp_name
= g_strdup_printf (" %s", file
);
787 add_to_list (tmp_name
, dirname
);
791 /* --------------------------------------------------------------------------------------------- */
794 find_add_match (const char *dir
, const char *file
)
796 insert_file (dir
, file
);
800 listbox_select_first (find_list
);
801 send_message (&find_list
->widget
, WIDGET_DRAW
, 0);
807 /* --------------------------------------------------------------------------------------------- */
811 * Returns malloced null-terminated line from file file_fd.
812 * Input is buffered in buf_size long buffer.
813 * Current pos in buf is stored in pos.
814 * n_read - number of read chars.
815 * has_newline - is there newline ?
819 get_line_at (int file_fd
, char *buf
, int buf_size
, int *pos
, int *n_read
, gboolean
* has_newline
)
831 *n_read
= mc_read (file_fd
, buf
, buf_size
);
839 /* skip possible leading zero(s) */
845 if (i
>= buffer_size
- 1)
846 buffer
= g_realloc (buffer
, buffer_size
+= 80);
855 *has_newline
= (ch
!= '\0');
863 /* --------------------------------------------------------------------------------------------- */
865 static FindProgressStatus
866 check_find_events (Dlg_head
* h
)
872 c
= tty_get_event (&event
, h
->mouse_status
== MOU_REPEAT
, FALSE
);
875 dlg_process_event (h
, c
, &event
);
876 if (h
->ret_value
== B_ENTER
877 || h
->ret_value
== B_CANCEL
|| h
->ret_value
== B_AGAIN
|| h
->ret_value
== B_PANELIZE
)
879 /* dialog terminated */
882 if (!(h
->flags
& DLG_WANT_IDLE
))
884 /* searching suspended */
892 /* --------------------------------------------------------------------------------------------- */
896 * Search the content_pattern string in the DIRECTORY/FILE.
897 * It will add the found entries to the find listbox.
899 * returns FALSE if do_search should look for another file
900 * TRUE if do_search should exit and proceed to the event handler
904 search_content (Dlg_head
* h
, const char *directory
, const char *filename
)
910 gboolean ret_val
= FALSE
;
912 fname
= concat_dir_and_file (directory
, filename
);
914 if (mc_stat (fname
, &s
) != 0 || !S_ISREG (s
.st_mode
))
920 file_fd
= mc_open (fname
, O_RDONLY
);
926 g_snprintf (buffer
, sizeof (buffer
), _("Grepping in %s"), str_trunc (filename
, FIND2_X_USE
));
928 status_update (buffer
);
931 tty_enable_interrupt_key ();
932 tty_got_interrupt ();
938 gboolean has_newline
;
940 gboolean found
= FALSE
;
942 char result
[BUF_MEDIUM
];
946 /* We've been previously suspended, start from the previous position */
952 && (p
= get_line_at (file_fd
, buffer
, sizeof (buffer
),
953 &pos
, &n_read
, &has_newline
)) != NULL
)
955 if (!found
/* Search in binary line once */
956 && mc_search_run (search_content_handle
,
957 (const void *) p
, 0, strlen (p
), &found_len
))
959 g_snprintf (result
, sizeof (result
), "%d:%s", line
, filename
);
960 find_add_match (directory
, result
);
965 if (found
&& options
.content_first_hit
)
974 if ((line
& 0xff) == 0)
976 FindProgressStatus res
;
977 res
= check_find_events (h
);
996 tty_disable_interrupt_key ();
1001 /* --------------------------------------------------------------------------------------------- */
1004 If dir is absolute, this means we're within dir and searching file here.
1005 If dir is relative, this means we're going to add dir to the directory stack.
1008 find_ignore_dir_search (const char *dir
)
1010 if (find_ignore_dirs
!= NULL
)
1012 const size_t dlen
= strlen (dir
);
1013 const unsigned char dabs
= g_path_is_absolute (dir
) ? 1 : 0;
1017 for (ignore_dir
= find_ignore_dirs
; *ignore_dir
!= NULL
; ignore_dir
++)
1019 const size_t ilen
= strlen (*ignore_dir
);
1020 const unsigned char iabs
= g_path_is_absolute (*ignore_dir
) ? 2 : 0;
1022 /* ignore dir is too long -- skip it */
1026 /* handle absolute and relative paths */
1027 switch (iabs
| dabs
)
1029 case 0: /* both paths are relative */
1030 case 3: /* both paths are abolute */
1031 /* if ignore dir is not a path of dir -- skip it */
1032 if (strncmp (dir
, *ignore_dir
, ilen
) == 0)
1034 /* be sure that ignore dir is not a part of dir like:
1035 ignore dir is "h", dir is "home" */
1036 if (dir
[ilen
] == '\0' || dir
[ilen
] == PATH_SEP
)
1040 case 1: /* dir is absolute, ignore_dir is relative */
1044 d
= strstr (dir
, *ignore_dir
);
1045 if (d
!= NULL
&& d
[-1] == PATH_SEP
&& (d
[ilen
] == '\0' || d
[ilen
] == PATH_SEP
))
1049 case 2: /* dir is relative, ignore_dir is absolute */
1050 /* FIXME: skip this case */
1052 default: /* this cannot occurs */
1061 /* --------------------------------------------------------------------------------------------- */
1064 find_rotate_dash (const Dlg_head
* h
, gboolean finish
)
1066 static const char rotating_dash
[] = "|/-\\";
1067 static unsigned int pos
= 0;
1071 pos
= (pos
+ 1) % 4;
1072 tty_setcolor (h
->color
[DLG_COLOR_NORMAL
]);
1073 dlg_move (h
, FIND2_Y
- 7, FIND2_X
- 4);
1074 tty_print_char (finish
? ' ' : rotating_dash
[pos
]);
1079 /* --------------------------------------------------------------------------------------------- */
1082 do_search (Dlg_head
* h
)
1084 static struct dirent
*dp
= NULL
;
1085 static DIR *dirp
= NULL
;
1086 static char *directory
= NULL
;
1087 struct stat tmp_stat
;
1088 static int subdirs_left
= 0;
1090 unsigned short count
;
1093 { /* someone forces me to close dirp */
1105 for (count
= 0; count
< 32; count
++)
1115 while (dirp
== NULL
)
1119 tty_setcolor (REVERSE_COLOR
);
1123 tmp
= pop_directory ();
1127 if (ignore_count
== 0)
1128 status_update (_("Finished"));
1131 char msg
[BUF_SMALL
];
1132 g_snprintf (msg
, sizeof (msg
),
1133 ngettext ("Finished (ignored %zd directory)",
1134 "Finished (ignored %zd directories)",
1135 ignore_count
), ignore_count
);
1136 status_update (msg
);
1138 find_rotate_dash (h
, TRUE
);
1143 /* handle absolute ignore dirs here */
1144 if (!find_ignore_dir_search (tmp
))
1156 char buffer
[BUF_SMALL
];
1158 g_snprintf (buffer
, sizeof (buffer
), _("Searching %s"),
1159 str_trunc (directory
, FIND2_X_USE
));
1160 status_update (buffer
);
1162 /* mc_stat should not be called after mc_opendir
1163 because vfs_s_opendir modifies the st_nlink
1165 if (mc_stat (directory
, &tmp_stat
) == 0)
1166 subdirs_left
= tmp_stat
.st_nlink
- 2;
1170 dirp
= mc_opendir (directory
);
1171 } /* while (!dirp) */
1173 /* skip invalid filenames */
1174 while ((dp
= mc_readdir (dirp
)) != NULL
&& !str_is_valid_string (dp
->d_name
))
1178 if (strcmp (dp
->d_name
, ".") == 0 || strcmp (dp
->d_name
, "..") == 0)
1180 /* skip invalid filenames */
1181 while ((dp
= mc_readdir (dirp
)) != NULL
&& !str_is_valid_string (dp
->d_name
))
1187 if (!(options
.skip_hidden
&& (dp
->d_name
[0] == '.')))
1191 if ((subdirs_left
!= 0) && options
.find_recurs
&& (directory
!= NULL
))
1192 { /* Can directory be NULL ? */
1193 /* handle relative ignore dirs here */
1194 if (find_ignore_dir_search (dp
->d_name
))
1200 tmp_name
= g_build_filename (directory
, dp
->d_name
, (char *) NULL
);
1202 if (mc_lstat (tmp_name
, &tmp_stat
) == 0 && S_ISDIR (tmp_stat
.st_mode
))
1204 push_directory (tmp_name
);
1212 search_ok
= mc_search_run (search_file_handle
, dp
->d_name
,
1213 0, strlen (dp
->d_name
), &bytes_found
);
1217 if (content_pattern
== NULL
)
1218 find_add_match (directory
, dp
->d_name
);
1219 else if (search_content (h
, directory
, dp
->d_name
))
1224 /* skip invalid filenames */
1225 while ((dp
= mc_readdir (dirp
)) != NULL
&& !str_is_valid_string (dp
->d_name
))
1229 find_rotate_dash (h
, FALSE
);
1234 /* --------------------------------------------------------------------------------------------- */
1237 init_find_vars (void)
1244 /* Remove all the items from the stack */
1248 /* --------------------------------------------------------------------------------------------- */
1251 make_fullname (const char *dirname
, const char *filename
)
1254 if (strcmp (dirname
, ".") == 0 || strcmp (dirname
, "." PATH_SEP_STR
) == 0)
1255 return g_strdup (filename
);
1256 if (strncmp (dirname
, "." PATH_SEP_STR
, 2) == 0)
1257 return concat_dir_and_file (dirname
+ 2, filename
);
1258 return concat_dir_and_file (dirname
, filename
);
1261 /* --------------------------------------------------------------------------------------------- */
1264 find_do_view_edit (int unparsed_view
, int edit
, char *dir
, char *file
)
1266 char *fullname
= NULL
;
1267 const char *filename
= NULL
;
1270 if (content_pattern
!= NULL
)
1272 filename
= strchr (file
+ 4, ':') + 1;
1273 line
= atoi (file
+ 4);
1277 filename
= file
+ 4;
1281 fullname
= make_fullname (dir
, filename
);
1283 do_edit_at_line (fullname
, use_internal_edit
, line
);
1285 view_file_at_line (fullname
, unparsed_view
, use_internal_view
, line
);
1289 /* --------------------------------------------------------------------------------------------- */
1292 view_edit_currently_selected_file (int unparsed_view
, int edit
)
1297 listbox_get_current (find_list
, &text
, (void **) &dir
);
1299 if ((text
== NULL
) || (dir
== NULL
))
1300 return MSG_NOT_HANDLED
;
1302 find_do_view_edit (unparsed_view
, edit
, dir
, text
);
1306 /* --------------------------------------------------------------------------------------------- */
1309 find_callback (Dlg_head
* h
, Widget
* sender
, dlg_msg_t msg
, int parm
, void *data
)
1314 if (parm
== KEY_F (3) || parm
== KEY_F (13))
1316 int unparsed_view
= (parm
== KEY_F (13));
1317 return view_edit_currently_selected_file (unparsed_view
, 0);
1319 if (parm
== KEY_F (4))
1321 return view_edit_currently_selected_file (0, 1);
1323 return MSG_NOT_HANDLED
;
1330 return default_dlg_callback (h
, sender
, msg
, parm
, data
);
1334 /* --------------------------------------------------------------------------------------------- */
1335 /** Handles the Stop/Start button in the find window */
1338 start_stop (WButton
* button
, int action
)
1344 set_idle_proc (find_dlg
, running
);
1345 is_start
= !is_start
;
1347 status_update (is_start
? _("Stopped") : _("Searching"));
1348 button_set_text (stop_button
, fbuts
[is_start
? 1 : 0].text
);
1353 /* --------------------------------------------------------------------------------------------- */
1354 /** Handle view command, when invoked as a button */
1357 find_do_view_file (WButton
* button
, int action
)
1362 view_edit_currently_selected_file (0, 0);
1366 /* --------------------------------------------------------------------------------------------- */
1367 /** Handle edit command, when invoked as a button */
1370 find_do_edit_file (WButton
* button
, int action
)
1375 view_edit_currently_selected_file (0, 1);
1379 /* --------------------------------------------------------------------------------------------- */
1385 static gboolean i18n_flag
= FALSE
;
1389 int i
= sizeof (fbuts
) / sizeof (fbuts
[0]);
1392 fbuts
[i
].text
= _(fbuts
[i
].text
);
1393 fbuts
[i
].len
= str_term_width1 (fbuts
[i
].text
) + 3;
1396 fbuts
[2].len
+= 2; /* DEFPUSH_BUTTON */
1399 #endif /* ENABLE_NLS */
1402 * Dynamically place buttons centered within current window size
1405 int l0
= max (fbuts
[0].len
, fbuts
[1].len
);
1406 int l1
= fbuts
[2].len
+ fbuts
[3].len
+ l0
+ fbuts
[4].len
;
1407 int l2
= fbuts
[5].len
+ fbuts
[6].len
+ fbuts
[7].len
;
1410 /* Check, if both button rows fit within FIND2_X */
1411 FIND2_X
= max (l1
+ 9, COLS
- 16);
1412 FIND2_X
= max (l2
+ 8, FIND2_X
);
1414 /* compute amount of space between buttons for each row */
1415 r1
= (FIND2_X
- 4 - l1
) % 5;
1416 l1
= (FIND2_X
- 4 - l1
) / 5;
1417 r2
= (FIND2_X
- 4 - l2
) % 4;
1418 l2
= (FIND2_X
- 4 - l2
) / 4;
1420 /* ...and finally, place buttons */
1421 fbuts
[2].x
= 2 + r1
/ 2 + l1
;
1422 fbuts
[3].x
= fbuts
[2].x
+ fbuts
[2].len
+ l1
;
1423 fbuts
[0].x
= fbuts
[3].x
+ fbuts
[3].len
+ l1
;
1424 fbuts
[4].x
= fbuts
[0].x
+ l0
+ l1
;
1425 fbuts
[5].x
= 2 + r2
/ 2 + l2
;
1426 fbuts
[6].x
= fbuts
[5].x
+ fbuts
[5].len
+ l2
;
1427 fbuts
[7].x
= fbuts
[6].x
+ fbuts
[6].len
+ l2
;
1431 create_dlg (TRUE
, 0, 0, FIND2_Y
, FIND2_X
, dialog_colors
, find_callback
,
1432 "[Find File]", _("Find File"), DLG_CENTER
| DLG_REVERSE
);
1434 add_widget (find_dlg
,
1435 button_new (FIND2_Y
- 3, fbuts
[7].x
, B_VIEW
, NORMAL_BUTTON
,
1436 fbuts
[7].text
, find_do_edit_file
));
1437 add_widget (find_dlg
,
1438 button_new (FIND2_Y
- 3, fbuts
[6].x
, B_VIEW
, NORMAL_BUTTON
,
1439 fbuts
[6].text
, find_do_view_file
));
1440 add_widget (find_dlg
,
1441 button_new (FIND2_Y
- 3, fbuts
[5].x
, B_PANELIZE
, NORMAL_BUTTON
, fbuts
[5].text
,
1444 add_widget (find_dlg
,
1445 button_new (FIND2_Y
- 4, fbuts
[4].x
, B_CANCEL
, NORMAL_BUTTON
, fbuts
[4].text
, NULL
));
1447 button_new (FIND2_Y
- 4, fbuts
[0].x
, B_STOP
, NORMAL_BUTTON
, fbuts
[0].text
, start_stop
);
1448 add_widget (find_dlg
, stop_button
);
1449 add_widget (find_dlg
,
1450 button_new (FIND2_Y
- 4, fbuts
[3].x
, B_AGAIN
, NORMAL_BUTTON
, fbuts
[3].text
, NULL
));
1451 add_widget (find_dlg
,
1452 button_new (FIND2_Y
- 4, fbuts
[2].x
, B_ENTER
, DEFPUSH_BUTTON
, fbuts
[2].text
, NULL
));
1454 status_label
= label_new (FIND2_Y
- 7, 4, _("Searching"));
1455 add_widget (find_dlg
, status_label
);
1457 found_num_label
= label_new (FIND2_Y
- 6, 4, "");
1458 add_widget (find_dlg
, found_num_label
);
1460 find_list
= listbox_new (2, 2, FIND2_Y
- 10, FIND2_X
- 4, FALSE
, NULL
);
1461 add_widget (find_dlg
, find_list
);
1464 /* --------------------------------------------------------------------------------------------- */
1471 search_content_handle
= mc_search_new (content_pattern
, -1);
1472 if (search_content_handle
)
1474 search_content_handle
->search_type
=
1475 options
.content_regexp
? MC_SEARCH_T_REGEX
: MC_SEARCH_T_NORMAL
;
1476 search_content_handle
->is_case_sensitive
= options
.content_case_sens
;
1477 search_content_handle
->whole_words
= options
.content_whole_words
;
1478 search_content_handle
->is_all_charsets
= options
.content_all_charsets
;
1480 search_file_handle
= mc_search_new (find_pattern
, -1);
1481 search_file_handle
->search_type
= options
.file_pattern
? MC_SEARCH_T_GLOB
: MC_SEARCH_T_REGEX
;
1482 search_file_handle
->is_case_sensitive
= options
.file_case_sens
;
1483 search_file_handle
->is_all_charsets
= options
.file_all_charsets
;
1484 search_file_handle
->is_entire_line
= options
.file_pattern
;
1487 set_idle_proc (find_dlg
, 1);
1488 ret
= run_dlg (find_dlg
);
1490 mc_search_free (search_file_handle
);
1491 search_file_handle
= NULL
;
1492 mc_search_free (search_content_handle
);
1493 search_content_handle
= NULL
;
1498 /* --------------------------------------------------------------------------------------------- */
1503 set_idle_proc (find_dlg
, 0);
1504 destroy_dlg (find_dlg
);
1507 /* --------------------------------------------------------------------------------------------- */
1510 find_file (const char *start_dir
, const char *pattern
, const char *content
,
1511 char **dirname
, char **filename
)
1513 int return_value
= 0;
1514 char *dir_tmp
= NULL
, *file_tmp
= NULL
;
1518 /* FIXME: Need to cleanup this, this ought to be passed non-globaly */
1519 find_pattern
= (char *) pattern
;
1521 content_pattern
= NULL
;
1522 if (options
.content_use
&& content
!= NULL
&& str_is_valid_string (content
))
1523 content_pattern
= g_strdup (content
);
1526 push_directory (start_dir
);
1528 return_value
= run_process ();
1530 /* Remove all the items from the stack */
1533 get_list_info (&file_tmp
, &dir_tmp
);
1536 *dirname
= g_strdup (dir_tmp
);
1538 *filename
= g_strdup (file_tmp
);
1540 if (return_value
== B_PANELIZE
&& *filename
)
1542 int status
, link_to_dir
, stale_link
;
1547 dir_list
*list
= ¤t_panel
->dir
;
1550 for (i
= 0, entry
= find_list
->list
; entry
!= NULL
; i
++, entry
= g_list_next (entry
))
1552 const char *lc_filename
= NULL
;
1553 WLEntry
*le
= (WLEntry
*) entry
->data
;
1555 if ((le
->text
== NULL
) || (le
->data
== NULL
))
1558 if (content_pattern
!= NULL
)
1559 lc_filename
= strchr (le
->text
+ 4, ':') + 1;
1561 lc_filename
= le
->text
+ 4;
1563 name
= make_fullname (le
->data
, lc_filename
);
1564 status
= handle_path (list
, name
, &st
, next_free
, &link_to_dir
, &stale_link
);
1576 /* don't add files more than once to the panel */
1577 if (content_pattern
!= NULL
&& next_free
> 0
1578 && strcmp (list
->list
[next_free
- 1].fname
, name
) == 0)
1584 if (!next_free
) /* first turn i.e clean old list */
1585 panel_clean_dir (current_panel
);
1586 list
->list
[next_free
].fnamelen
= strlen (name
);
1587 list
->list
[next_free
].fname
= name
;
1588 list
->list
[next_free
].f
.marked
= 0;
1589 list
->list
[next_free
].f
.link_to_dir
= link_to_dir
;
1590 list
->list
[next_free
].f
.stale_link
= stale_link
;
1591 list
->list
[next_free
].f
.dir_size_computed
= 0;
1592 list
->list
[next_free
].st
= st
;
1593 list
->list
[next_free
].sort_key
= NULL
;
1594 list
->list
[next_free
].second_sort_key
= NULL
;
1596 if (!(next_free
& 15))
1602 current_panel
->count
= next_free
;
1603 current_panel
->is_panelized
= 1;
1605 if (start_dir
[0] == PATH_SEP
)
1608 strcpy (current_panel
->cwd
, PATH_SEP_STR
);
1609 ret
= chdir (PATH_SEP_STR
);
1614 g_free (content_pattern
);
1616 do_search (NULL
); /* force do_search to release resources */
1620 return return_value
;
1623 /* --------------------------------------------------------------------------------------------- */
1624 /*** public functions ****************************************************************************/
1625 /* --------------------------------------------------------------------------------------------- */
1630 char *start_dir
= NULL
, *pattern
= NULL
, *content
= NULL
;
1631 char *filename
= NULL
, *dirname
= NULL
;
1633 gboolean dir_and_file_set
;
1635 while (find_parameters (&start_dir
, &pattern
, &content
))
1637 if (pattern
[0] == '\0')
1638 break; /* nothing search */
1640 dirname
= filename
= NULL
;
1642 v
= find_file (start_dir
, pattern
, content
, &dirname
, &filename
);
1647 if (dirname
!= NULL
)
1649 do_cd (dirname
, cd_exact
);
1650 if (filename
!= NULL
)
1651 try_to_select (current_panel
,
1652 filename
+ (content
!= NULL
1653 ? strchr (filename
+ 4, ':') - filename
+ 1 : 4));
1655 else if (filename
!= NULL
)
1656 do_cd (filename
, cd_exact
);
1664 dir_and_file_set
= (dirname
!= NULL
) && (filename
!= NULL
);
1671 if (v
== B_PANELIZE
)
1673 if (dir_and_file_set
)
1675 try_to_select (current_panel
, NULL
);
1676 panel_re_sort (current_panel
);
1677 try_to_select (current_panel
, NULL
);
1684 /* --------------------------------------------------------------------------------------------- */