Ticket #2275: find files: support of relative dirs in ignore_dirs.
[midnight-commander.git] / src / filemanager / find.c
blob305bc9b362bc12dd8654ff1680b41c0dd14d5a3c
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
6 Complete rewrite.
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. */
22 /** \file find.c
23 * \brief Source: Find file command
26 #include <config.h>
28 #include <ctype.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <fcntl.h>
33 #include <sys/stat.h>
35 #include "lib/global.h"
37 #include "lib/tty/tty.h"
38 #include "lib/tty/key.h"
39 #include "lib/skin.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 */
51 #include "dir.h"
52 #include "cmd.h" /* view_file_at_line */
53 #include "midnight.h" /* current_panel */
54 #include "boxes.h"
55 #include "layout.h" /* mc_refresh() */
57 #include "find.h"
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 */
71 enum
73 B_STOP = B_USER + 1,
74 B_AGAIN,
75 B_PANELIZE,
76 B_TREE,
77 B_VIEW
80 typedef enum
82 FIND_CONT = 0,
83 FIND_SUSPEND,
84 FIND_ABORT
85 } FindProgressStatus;
87 /* find file options */
88 typedef struct
90 /* file name options */
91 gboolean file_case_sens;
92 gboolean file_pattern;
93 gboolean find_recurs;
94 gboolean skip_hidden;
95 gboolean file_all_charsets;
97 /* file content options */
98 gboolean content_use;
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 */
112 #if HAVE_CHARSET
113 static int FIND_Y = 17;
114 #else
115 static int FIND_Y = 16;
116 #endif
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 */
135 #ifdef HAVE_CHARSET
136 static WCheck *file_all_charsets_cbox;
137 static WCheck *content_all_charsets_cbox;
138 #endif
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 */
150 static int resuming;
151 static int last_line;
152 static int last_pos;
154 static Dlg_head *find_dlg; /* The dialog */
155 static WButton *stop_button; /* pointer to the stop button */
156 static WLabel *status_label; /* Finished, Searching etc. */
157 static WLabel *found_num_label; /* Number of found items */
158 static WListbox *find_list; /* Listbox with the file list */
160 /* This keeps track of the directory stack */
161 #if GLIB_CHECK_VERSION (2, 14, 0)
162 static GQueue dir_queue = G_QUEUE_INIT;
163 #else
164 typedef struct dir_stack
166 char *name;
167 struct dir_stack *prev;
168 } dir_stack;
170 static dir_stack *dir_stack_base = 0;
171 #endif /* GLIB_CHECK_VERSION */
173 /* *INDENT-OFF* */
174 static struct
176 const char *text;
177 int len; /* length including space and brackets */
178 int x;
179 } fbuts[] =
181 {N_("&Suspend"), 11, 29},
182 {N_("Con&tinue"), 12, 29},
183 {N_("&Chdir"), 11, 3},
184 {N_("&Again"), 9, 17},
185 {N_("&Quit"), 8, 43},
186 {N_("Pane&lize"), 12, 3},
187 {N_("&View - F3"), 13, 20},
188 {N_("&Edit - F4"), 13, 38}
190 /* *INDENT-ON* */
192 static find_file_options_t options = {
193 TRUE, TRUE, TRUE, FALSE, FALSE,
194 FALSE, TRUE, FALSE, FALSE, FALSE, FALSE
197 static char *in_start_dir = INPUT_LAST_TEXT;
199 static mc_search_t *search_file_handle = NULL;
200 static mc_search_t *search_content_handle = NULL;
202 /*** file scope functions ************************************************************************/
204 static void
205 find_load_options (void)
207 static gboolean loaded = FALSE;
208 char *ignore_dirs;
210 if (loaded)
211 return;
213 loaded = TRUE;
215 /* Back compatibility: try load old parameter at first */
216 ignore_dirs = mc_config_get_string (mc_main_config, "Misc", "find_ignore_dirs", "");
217 if (ignore_dirs[0] != '\0')
219 find_ignore_dirs = g_strsplit (ignore_dirs, ":", -1);
220 mc_config_set_string (mc_main_config, "FindFile", "ignore_dirs", ignore_dirs);
222 g_free (ignore_dirs);
223 mc_config_del_key (mc_main_config, "Misc", "find_ignore_dirs");
225 /* Then load new parameters */
226 ignore_dirs = mc_config_get_string (mc_main_config, "FindFile", "ignore_dirs", "");
227 if (ignore_dirs[0] != '\0')
229 g_strfreev (find_ignore_dirs);
230 find_ignore_dirs = g_strsplit (ignore_dirs, ":", -1);
232 g_free (ignore_dirs);
234 if (find_ignore_dirs != NULL)
236 /* Values like '/foo::/bar: produce holes in list.
237 Find and remove them */
238 size_t r = 0, w = 0; /* read and write iterators */
240 for (; find_ignore_dirs[r] != NULL; r++)
242 if (find_ignore_dirs[r][0] == '\0')
244 /* empty entry -- skip it */
245 g_free (find_ignore_dirs[r]);
246 find_ignore_dirs[r] = NULL;
247 continue;
250 if (r != w)
252 /* copy entry to the previous free array cell */
253 find_ignore_dirs[w] = find_ignore_dirs[r];
254 find_ignore_dirs[r] = NULL;
257 canonicalize_pathname (find_ignore_dirs[w]);
258 if (find_ignore_dirs[w][0] != '\0')
259 w++;
260 else
262 g_free (find_ignore_dirs[w]);
263 find_ignore_dirs[w] = NULL;
267 if (find_ignore_dirs[0] == NULL)
269 g_strfreev (find_ignore_dirs);
270 find_ignore_dirs = NULL;
274 options.file_case_sens =
275 mc_config_get_bool (mc_main_config, "FindFile", "file_case_sens", TRUE);
276 options.file_pattern =
277 mc_config_get_bool (mc_main_config, "FindFile", "file_shell_pattern", TRUE);
278 options.find_recurs = mc_config_get_bool (mc_main_config, "FindFile", "file_find_recurs", TRUE);
279 options.skip_hidden =
280 mc_config_get_bool (mc_main_config, "FindFile", "file_skip_hidden", FALSE);
281 options.file_all_charsets =
282 mc_config_get_bool (mc_main_config, "FindFile", "file_all_charsets", FALSE);
283 options.content_use = mc_config_get_bool (mc_main_config, "FindFile", "content_use", FALSE);
284 options.content_case_sens =
285 mc_config_get_bool (mc_main_config, "FindFile", "content_case_sens", TRUE);
286 options.content_regexp =
287 mc_config_get_bool (mc_main_config, "FindFile", "content_regexp", FALSE);
288 options.content_first_hit =
289 mc_config_get_bool (mc_main_config, "FindFile", "content_first_hit", FALSE);
290 options.content_whole_words =
291 mc_config_get_bool (mc_main_config, "FindFile", "content_whole_words", FALSE);
292 options.content_all_charsets =
293 mc_config_get_bool (mc_main_config, "FindFile", "content_all_charsets", FALSE);
296 /* --------------------------------------------------------------------------------------------- */
298 static void
299 find_save_options (void)
301 mc_config_set_bool (mc_main_config, "FindFile", "file_case_sens", options.file_case_sens);
302 mc_config_set_bool (mc_main_config, "FindFile", "file_shell_pattern", options.file_pattern);
303 mc_config_set_bool (mc_main_config, "FindFile", "file_find_recurs", options.find_recurs);
304 mc_config_set_bool (mc_main_config, "FindFile", "file_skip_hidden", options.skip_hidden);
305 mc_config_set_bool (mc_main_config, "FindFile", "file_all_charsets", options.file_all_charsets);
306 mc_config_set_bool (mc_main_config, "FindFile", "content_use", options.content_use);
307 mc_config_set_bool (mc_main_config, "FindFile", "content_case_sens", options.content_case_sens);
308 mc_config_set_bool (mc_main_config, "FindFile", "content_regexp", options.content_regexp);
309 mc_config_set_bool (mc_main_config, "FindFile", "content_first_hit", options.content_first_hit);
310 mc_config_set_bool (mc_main_config, "FindFile", "content_whole_words",
311 options.content_whole_words);
312 mc_config_set_bool (mc_main_config, "FindFile", "content_all_charsets",
313 options.content_all_charsets);
316 /* --------------------------------------------------------------------------------------------- */
318 static inline char *
319 add_to_list (const char *text, void *data)
321 return listbox_add_item (find_list, LISTBOX_APPEND_AT_END, 0, text, data);
324 /* --------------------------------------------------------------------------------------------- */
326 static inline void
327 stop_idle (void *data)
329 set_idle_proc (data, 0);
332 /* --------------------------------------------------------------------------------------------- */
334 static inline void
335 status_update (const char *text)
337 label_set_text (status_label, text);
340 /* --------------------------------------------------------------------------------------------- */
342 static void
343 found_num_update (void)
345 char buffer[BUF_TINY];
346 g_snprintf (buffer, sizeof (buffer), _("Found: %ld"), matches);
347 label_set_text (found_num_label, buffer);
350 /* --------------------------------------------------------------------------------------------- */
352 static void
353 get_list_info (char **file, char **dir)
355 listbox_get_current (find_list, file, (void **) dir);
358 /* --------------------------------------------------------------------------------------------- */
359 /** check regular expression */
361 static gboolean
362 find_check_regexp (const char *r)
364 mc_search_t *search;
365 gboolean regexp_ok = FALSE;
367 search = mc_search_new (r, -1);
369 if (search != NULL)
371 search->search_type = MC_SEARCH_T_REGEX;
372 regexp_ok = mc_search_prepare (search);
373 mc_search_free (search);
376 return regexp_ok;
379 /* --------------------------------------------------------------------------------------------- */
381 * Callback for the parameter dialog.
382 * Validate regex, prevent closing the dialog if it's invalid.
385 static cb_ret_t
386 find_parm_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
388 switch (msg)
390 case DLG_ACTION:
391 if (sender == (Widget *) content_use_cbox)
393 gboolean disable = !(content_use_cbox->state & C_BOOL);
395 widget_disable (content_label->widget, disable);
396 send_message ((Widget *) content_label, WIDGET_DRAW, 0);
397 widget_disable (in_with->widget, disable);
398 send_message ((Widget *) in_with, WIDGET_DRAW, 0);
399 widget_disable (content_first_hit_cbox->widget, disable);
400 send_message ((Widget *) content_first_hit_cbox, WIDGET_DRAW, 0);
401 widget_disable (content_regexp_cbox->widget, disable);
402 send_message ((Widget *) content_regexp_cbox, WIDGET_DRAW, 0);
403 widget_disable (content_case_sens_cbox->widget, disable);
404 send_message ((Widget *) content_case_sens_cbox, WIDGET_DRAW, 0);
405 #ifdef HAVE_CHARSET
406 widget_disable (content_all_charsets_cbox->widget, disable);
407 send_message ((Widget *) content_all_charsets_cbox, WIDGET_DRAW, 0);
408 #endif
409 widget_disable (content_whole_words_cbox->widget, disable);
410 send_message ((Widget *) content_whole_words_cbox, WIDGET_DRAW, 0);
412 return MSG_HANDLED;
415 return MSG_NOT_HANDLED;
418 case DLG_VALIDATE:
419 if (h->ret_value != B_ENTER)
420 return MSG_HANDLED;
422 /* check filename regexp */
423 if (!(file_pattern_cbox->state & C_BOOL)
424 && (in_name->buffer[0] != '\0') && !find_check_regexp (in_name->buffer))
426 h->state = DLG_ACTIVE; /* Don't stop the dialog */
427 message (D_ERROR, MSG_ERROR, _("Malformed regular expression"));
428 dlg_select_widget (in_name);
429 return MSG_HANDLED;
432 /* check content regexp */
433 if ((content_use_cbox->state & C_BOOL) && (content_regexp_cbox->state & C_BOOL)
434 && (in_with->buffer[0] != '\0') && !find_check_regexp (in_with->buffer))
436 h->state = DLG_ACTIVE; /* Don't stop the dialog */
437 message (D_ERROR, MSG_ERROR, _("Malformed regular expression"));
438 dlg_select_widget (in_with);
439 return MSG_HANDLED;
442 return MSG_HANDLED;
444 default:
445 return default_dlg_callback (h, sender, msg, parm, data);
449 /* --------------------------------------------------------------------------------------------- */
451 * find_parameters: gets information from the user
453 * If the return value is TRUE, then the following holds:
455 * START_DIR and PATTERN are pointers to char * and upon return they
456 * contain the information provided by the user.
458 * CONTENT holds a strdup of the contents specified by the user if he
459 * asked for them or 0 if not (note, this is different from the
460 * behavior for the other two parameters.
464 static gboolean
465 find_parameters (char **start_dir, char **pattern, char **content)
467 gboolean return_value;
469 /* file name */
470 const char *file_case_label = N_("Cas&e sensitive");
471 const char *file_pattern_label = N_("&Using shell patterns");
472 const char *file_recurs_label = N_("&Find recursively");
473 const char *file_skip_hidden_label = N_("S&kip hidden");
474 #ifdef HAVE_CHARSET
475 const char *file_all_charsets_label = N_("&All charsets");
476 #endif
478 /* file content */
479 const char *content_use_label = N_("Sea&rch for content");
480 const char *content_case_label = N_("Case sens&itive");
481 const char *content_regexp_label = N_("Re&gular expression");
482 const char *content_first_hit_label = N_("Fir&st hit");
483 const char *content_whole_words_label = N_("&Whole words");
484 #ifdef HAVE_CHARSET
485 const char *content_all_charsets_label = N_("A&ll charsets");
486 #endif
488 const char *buts[] = { N_("&OK"), N_("&Cancel"), N_("&Tree") };
490 int b0, b1, b2;
492 int cbox_position;
493 gboolean disable;
495 #ifdef ENABLE_NLS
497 int i = sizeof (buts) / sizeof (buts[0]);
498 while (i-- != 0)
499 buts[i] = _(buts[i]);
501 file_case_label = _(file_case_label);
502 file_pattern_label = _(file_pattern_label);
503 file_recurs_label = _(file_recurs_label);
504 file_skip_hidden_label = _(file_skip_hidden_label);
505 #ifdef HAVE_CHARSET
506 file_all_charsets_label = _(file_all_charsets_label);
507 content_all_charsets_label = _(content_all_charsets_label);
508 #endif
509 content_use_label = _(content_use_label);
510 content_case_label = _(content_case_label);
511 content_regexp_label = _(content_regexp_label);
512 content_first_hit_label = _(content_first_hit_label);
513 content_whole_words_label = _(content_whole_words_label);
515 #endif /* ENABLE_NLS */
517 b0 = str_term_width1 (buts[0]) + 6; /* default button */
518 b1 = str_term_width1 (buts[1]) + 4;
519 b2 = str_term_width1 (buts[2]) + 4;
521 find_load_options ();
523 if (in_start_dir == NULL)
524 in_start_dir = g_strdup (".");
526 disable = !options.content_use;
528 find_dlg =
529 create_dlg (TRUE, 0, 0, FIND_Y, FIND_X, dialog_colors,
530 find_parm_callback, "[Find File]", _("Find File"), DLG_CENTER | DLG_REVERSE);
532 add_widget (find_dlg,
533 button_new (FIND_Y - 3, FIND_X * 3 / 4 - b1 / 2, B_CANCEL, NORMAL_BUTTON, buts[1],
534 0));
535 add_widget (find_dlg,
536 button_new (FIND_Y - 3, FIND_X / 4 - b0 / 2, B_ENTER, DEFPUSH_BUTTON, buts[0], 0));
538 cbox_position = FIND_Y - 5;
540 content_first_hit_cbox =
541 check_new (cbox_position--, FIND_X / 2 + 1, options.content_first_hit,
542 content_first_hit_label);
543 widget_disable (content_first_hit_cbox->widget, disable);
544 add_widget (find_dlg, content_first_hit_cbox);
546 content_whole_words_cbox =
547 check_new (cbox_position--, FIND_X / 2 + 1, options.content_whole_words,
548 content_whole_words_label);
549 widget_disable (content_whole_words_cbox->widget, disable);
550 add_widget (find_dlg, content_whole_words_cbox);
552 #ifdef HAVE_CHARSET
553 content_all_charsets_cbox = check_new (cbox_position--, FIND_X / 2 + 1,
554 options.content_all_charsets,
555 content_all_charsets_label);
556 widget_disable (content_all_charsets_cbox->widget, disable);
557 add_widget (find_dlg, content_all_charsets_cbox);
558 #endif
560 content_case_sens_cbox =
561 check_new (cbox_position--, FIND_X / 2 + 1, options.content_case_sens, content_case_label);
562 widget_disable (content_case_sens_cbox->widget, disable);
563 add_widget (find_dlg, content_case_sens_cbox);
565 content_regexp_cbox =
566 check_new (cbox_position--, FIND_X / 2 + 1, options.content_regexp, content_regexp_label);
567 widget_disable (content_regexp_cbox->widget, disable);
568 add_widget (find_dlg, content_regexp_cbox);
570 cbox_position = FIND_Y - 6;
572 skip_hidden_cbox = check_new (cbox_position--, 3, options.skip_hidden, file_skip_hidden_label);
573 add_widget (find_dlg, skip_hidden_cbox);
575 #ifdef HAVE_CHARSET
576 file_all_charsets_cbox =
577 check_new (cbox_position--, 3, options.file_all_charsets, file_all_charsets_label);
578 add_widget (find_dlg, file_all_charsets_cbox);
579 #endif
581 file_case_sens_cbox = check_new (cbox_position--, 3, options.file_case_sens, file_case_label);
582 add_widget (find_dlg, file_case_sens_cbox);
584 file_pattern_cbox = check_new (cbox_position--, 3, options.file_pattern, file_pattern_label);
585 add_widget (find_dlg, file_pattern_cbox);
587 recursively_cbox = check_new (cbox_position, 3, options.find_recurs, file_recurs_label);
588 add_widget (find_dlg, recursively_cbox);
590 /* This checkbox is located in the second column */
591 content_use_cbox =
592 check_new (cbox_position, FIND_X / 2 + 1, options.content_use, content_use_label);
593 add_widget (find_dlg, content_use_cbox);
595 in_with =
596 input_new (6, FIND_X / 2 + 1, input_get_default_colors (), FIND_X / 2 - 4, INPUT_LAST_TEXT,
597 MC_HISTORY_SHARED_SEARCH, INPUT_COMPLETE_DEFAULT);
598 widget_disable (in_with->widget, disable);
599 add_widget (find_dlg, in_with);
601 content_label = label_new (5, FIND_X / 2 + 1, _("Content:"));
602 widget_disable (content_label->widget, disable);
603 add_widget (find_dlg, content_label);
605 in_name = input_new (6, 3, input_get_default_colors (),
606 FIND_X / 2 - 4, INPUT_LAST_TEXT, "name", INPUT_COMPLETE_DEFAULT);
607 add_widget (find_dlg, in_name);
608 add_widget (find_dlg, label_new (5, 3, _("File name:")));
610 add_widget (find_dlg, button_new (3, FIND_X - b2 - 2, B_TREE, NORMAL_BUTTON, buts[2], 0));
612 in_start = input_new (3, 3, input_get_default_colors (),
613 FIND_X - b2 - 6, in_start_dir, "start", INPUT_COMPLETE_DEFAULT);
614 add_widget (find_dlg, in_start);
615 add_widget (find_dlg, label_new (2, 3, _("Start at:")));
617 find_par_start:
618 dlg_select_widget (in_name);
620 switch (run_dlg (find_dlg))
622 case B_CANCEL:
623 return_value = FALSE;
624 break;
626 case B_TREE:
628 const char *temp_dir = in_start->buffer;
630 if ((temp_dir[0] == '\0') || ((temp_dir[0] == '.') && (temp_dir[1] == '\0')))
631 temp_dir = current_panel->cwd;
633 if (in_start_dir != INPUT_LAST_TEXT)
634 g_free (in_start_dir);
635 in_start_dir = tree_box (temp_dir);
636 if (in_start_dir == NULL)
637 in_start_dir = g_strdup (temp_dir);
639 input_assign_text (in_start, in_start_dir);
641 /* Warning: Dreadful goto */
642 goto find_par_start;
645 default:
646 #ifdef HAVE_CHARSET
647 options.file_all_charsets = file_all_charsets_cbox->state & C_BOOL;
648 options.content_all_charsets = content_all_charsets_cbox->state & C_BOOL;
649 #endif
650 options.content_use = content_use_cbox->state & C_BOOL;
651 options.content_case_sens = content_case_sens_cbox->state & C_BOOL;
652 options.content_regexp = content_regexp_cbox->state & C_BOOL;
653 options.content_first_hit = content_first_hit_cbox->state & C_BOOL;
654 options.content_whole_words = content_whole_words_cbox->state & C_BOOL;
655 options.find_recurs = recursively_cbox->state & C_BOOL;
656 options.file_pattern = file_pattern_cbox->state & C_BOOL;
657 options.file_case_sens = file_case_sens_cbox->state & C_BOOL;
658 options.skip_hidden = skip_hidden_cbox->state & C_BOOL;
660 *content = (options.content_use && in_with->buffer[0] != '\0')
661 ? g_strdup (in_with->buffer) : NULL;
662 *start_dir = in_start->buffer[0] != '\0' ? in_start->buffer : (char *) ".";
663 *pattern = g_strdup (in_name->buffer);
664 if (in_start_dir != INPUT_LAST_TEXT)
665 g_free (in_start_dir);
666 in_start_dir = g_strdup (*start_dir);
667 if ((*start_dir)[0] == '.' && (*start_dir)[1] == '\0')
668 *start_dir = g_strdup (current_panel->cwd);
669 else if (g_path_is_absolute (*start_dir))
670 *start_dir = g_strdup (*start_dir);
671 else
672 *start_dir = g_build_filename (current_panel->cwd, *start_dir, (char *) NULL);
674 canonicalize_pathname (*start_dir);
676 find_save_options ();
678 return_value = TRUE;
681 destroy_dlg (find_dlg);
683 return return_value;
686 /* --------------------------------------------------------------------------------------------- */
688 #if GLIB_CHECK_VERSION (2, 14, 0)
689 static inline void
690 push_directory (const char *dir)
692 g_queue_push_head (&dir_queue, (void *) dir);
695 /* --------------------------------------------------------------------------------------------- */
697 static inline char *
698 pop_directory (void)
700 return (char *) g_queue_pop_tail (&dir_queue);
703 /* --------------------------------------------------------------------------------------------- */
704 /** Remove all the items from the stack */
706 static void
707 clear_stack (void)
709 g_queue_foreach (&dir_queue, (GFunc) g_free, NULL);
710 g_queue_clear (&dir_queue);
713 /* --------------------------------------------------------------------------------------------- */
715 #else /* GLIB_CHECK_VERSION */
716 static void
717 push_directory (const char *dir)
719 dir_stack *new;
721 new = g_new (dir_stack, 1);
722 new->name = (char *) dir;
723 new->prev = dir_stack_base;
724 dir_stack_base = new;
727 /* --------------------------------------------------------------------------------------------- */
729 static char *
730 pop_directory (void)
732 char *name = NULL;
734 if (dir_stack_base != NULL)
736 dir_stack *next;
737 name = dir_stack_base->name;
738 next = dir_stack_base->prev;
739 g_free (dir_stack_base);
740 dir_stack_base = next;
743 return name;
746 /* --------------------------------------------------------------------------------------------- */
747 /** Remove all the items from the stack */
749 static void
750 clear_stack (void)
752 char *dir = NULL;
753 while ((dir = pop_directory ()) != NULL)
754 g_free (dir);
756 #endif /* GLIB_CHECK_VERSION */
758 /* --------------------------------------------------------------------------------------------- */
760 static void
761 insert_file (const char *dir, const char *file)
763 char *tmp_name = NULL;
764 static char *dirname = NULL;
766 while (dir[0] == PATH_SEP && dir[1] == PATH_SEP)
767 dir++;
769 if (old_dir)
771 if (strcmp (old_dir, dir))
773 g_free (old_dir);
774 old_dir = g_strdup (dir);
775 dirname = add_to_list (dir, NULL);
778 else
780 old_dir = g_strdup (dir);
781 dirname = add_to_list (dir, NULL);
784 tmp_name = g_strdup_printf (" %s", file);
785 add_to_list (tmp_name, dirname);
786 g_free (tmp_name);
789 /* --------------------------------------------------------------------------------------------- */
791 static void
792 find_add_match (const char *dir, const char *file)
794 insert_file (dir, file);
796 /* Don't scroll */
797 if (matches == 0)
798 listbox_select_first (find_list);
799 send_message (&find_list->widget, WIDGET_DRAW, 0);
801 matches++;
802 found_num_update ();
805 /* --------------------------------------------------------------------------------------------- */
807 * get_line_at:
809 * Returns malloced null-terminated line from file file_fd.
810 * Input is buffered in buf_size long buffer.
811 * Current pos in buf is stored in pos.
812 * n_read - number of read chars.
813 * has_newline - is there newline ?
816 static char *
817 get_line_at (int file_fd, char *buf, int buf_size, int *pos, int *n_read, gboolean * has_newline)
819 char *buffer = NULL;
820 int buffer_size = 0;
821 char ch = 0;
822 int i = 0;
824 for (;;)
826 if (*pos >= *n_read)
828 *pos = 0;
829 *n_read = mc_read (file_fd, buf, buf_size);
830 if (*n_read <= 0)
831 break;
834 ch = buf[(*pos)++];
835 if (ch == '\0')
837 /* skip possible leading zero(s) */
838 if (i == 0)
839 continue;
840 break;
843 if (i >= buffer_size - 1)
844 buffer = g_realloc (buffer, buffer_size += 80);
846 /* Strip newline */
847 if (ch == '\n')
848 break;
850 buffer[i++] = ch;
853 *has_newline = (ch != '\0');
855 if (buffer != NULL)
856 buffer[i] = '\0';
858 return buffer;
861 /* --------------------------------------------------------------------------------------------- */
863 static FindProgressStatus
864 check_find_events (Dlg_head * h)
866 Gpm_Event event;
867 int c;
869 event.x = -1;
870 c = tty_get_event (&event, h->mouse_status == MOU_REPEAT, FALSE);
871 if (c != EV_NONE)
873 dlg_process_event (h, c, &event);
874 if (h->ret_value == B_ENTER
875 || h->ret_value == B_CANCEL || h->ret_value == B_AGAIN || h->ret_value == B_PANELIZE)
877 /* dialog terminated */
878 return FIND_ABORT;
880 if (!(h->flags & DLG_WANT_IDLE))
882 /* searching suspended */
883 return FIND_SUSPEND;
887 return FIND_CONT;
890 /* --------------------------------------------------------------------------------------------- */
892 * search_content:
894 * Search the content_pattern string in the DIRECTORY/FILE.
895 * It will add the found entries to the find listbox.
897 * returns FALSE if do_search should look for another file
898 * TRUE if do_search should exit and proceed to the event handler
901 static gboolean
902 search_content (Dlg_head * h, const char *directory, const char *filename)
904 struct stat s;
905 char buffer[BUF_4K];
906 char *fname = NULL;
907 int file_fd;
908 gboolean ret_val = FALSE;
910 fname = concat_dir_and_file (directory, filename);
912 if (mc_stat (fname, &s) != 0 || !S_ISREG (s.st_mode))
914 g_free (fname);
915 return FALSE;
918 file_fd = mc_open (fname, O_RDONLY);
919 g_free (fname);
921 if (file_fd == -1)
922 return FALSE;
924 g_snprintf (buffer, sizeof (buffer), _("Grepping in %s"), str_trunc (filename, FIND2_X_USE));
926 status_update (buffer);
927 mc_refresh ();
929 tty_enable_interrupt_key ();
930 tty_got_interrupt ();
933 int line = 1;
934 int pos = 0;
935 int n_read = 0;
936 gboolean has_newline;
937 char *p = NULL;
938 gboolean found = FALSE;
939 gsize found_len;
940 char result[BUF_MEDIUM];
942 if (resuming)
944 /* We've been previously suspended, start from the previous position */
945 resuming = 0;
946 line = last_line;
947 pos = last_pos;
949 while (!ret_val
950 && (p = get_line_at (file_fd, buffer, sizeof (buffer),
951 &pos, &n_read, &has_newline)) != NULL)
953 if (!found /* Search in binary line once */
954 && mc_search_run (search_content_handle,
955 (const void *) p, 0, strlen (p), &found_len))
957 g_snprintf (result, sizeof (result), "%d:%s", line, filename);
958 find_add_match (directory, result);
959 found = TRUE;
961 g_free (p);
963 if (found && options.content_first_hit)
964 break;
966 if (has_newline)
968 found = FALSE;
969 line++;
972 if ((line & 0xff) == 0)
974 FindProgressStatus res;
975 res = check_find_events (h);
976 switch (res)
978 case FIND_ABORT:
979 stop_idle (h);
980 ret_val = TRUE;
981 break;
982 case FIND_SUSPEND:
983 resuming = 1;
984 last_line = line;
985 last_pos = pos;
986 ret_val = TRUE;
987 break;
988 default:
989 break;
994 tty_disable_interrupt_key ();
995 mc_close (file_fd);
996 return ret_val;
999 /* --------------------------------------------------------------------------------------------- */
1002 If dir is absolute, this means we're within dir and searching file here.
1003 If dir is relative, this means we're going to add dir to the directory stack.
1005 static gboolean
1006 find_ignore_dir_search (const char *dir)
1008 if (find_ignore_dirs != NULL)
1010 const size_t dlen = strlen (dir);
1011 const unsigned char dabs = g_path_is_absolute (dir) ? 1 : 0;
1013 char **ignore_dir;
1015 for (ignore_dir = find_ignore_dirs; *ignore_dir != NULL; ignore_dir++)
1017 const size_t ilen = strlen (*ignore_dir);
1018 const unsigned char iabs = g_path_is_absolute (*ignore_dir) ? 2 : 0;
1020 /* ignore dir is too long -- skip it */
1021 if (dlen < ilen)
1022 continue;
1024 /* handle absolute and relative paths */
1025 switch (iabs | dabs)
1027 case 0: /* both paths are relative */
1028 case 3: /* both paths are abolute */
1029 /* if ignore dir is not a path of dir -- skip it */
1030 if (strncmp (dir, *ignore_dir, ilen) == 0)
1032 /* be sure that ignore dir is not a part of dir like:
1033 ignore dir is "h", dir is "home" */
1034 if (dir[ilen] == '\0' || dir[ilen] == PATH_SEP)
1035 return TRUE;
1037 break;
1038 case 1: /* dir is absolute, ignore_dir is relative */
1040 char *d;
1042 d = strstr (dir, *ignore_dir);
1043 if (d != NULL && d[-1] == PATH_SEP && (d[ilen] == '\0' || d[ilen] == PATH_SEP))
1044 return TRUE;
1046 break;
1047 case 2: /* dir is relative, ignore_dir is absolute */
1048 /* FIXME: skip this case */
1049 break;
1050 default: /* this cannot occurs */
1051 return FALSE;
1056 return FALSE;
1059 /* --------------------------------------------------------------------------------------------- */
1061 static void
1062 find_rotate_dash (const Dlg_head * h, gboolean finish)
1064 static const char rotating_dash[] = "|/-\\";
1065 static unsigned int pos = 0;
1067 if (verbose)
1069 pos = (pos + 1) % 4;
1070 tty_setcolor (h->color[DLG_COLOR_NORMAL]);
1071 dlg_move (h, FIND2_Y - 7, FIND2_X - 4);
1072 tty_print_char (finish ? ' ' : rotating_dash[pos]);
1073 mc_refresh ();
1077 /* --------------------------------------------------------------------------------------------- */
1079 static int
1080 do_search (Dlg_head * h)
1082 static struct dirent *dp = NULL;
1083 static DIR *dirp = NULL;
1084 static char *directory = NULL;
1085 struct stat tmp_stat;
1086 static int subdirs_left = 0;
1087 gsize bytes_found;
1088 unsigned short count;
1090 if (h == NULL)
1091 { /* someone forces me to close dirp */
1092 if (dirp != NULL)
1094 mc_closedir (dirp);
1095 dirp = NULL;
1097 g_free (directory);
1098 directory = NULL;
1099 dp = NULL;
1100 return 1;
1103 for (count = 0; count < 32; count++)
1105 while (dp == NULL)
1107 if (dirp != NULL)
1109 mc_closedir (dirp);
1110 dirp = NULL;
1113 while (dirp == NULL)
1115 char *tmp = NULL;
1117 tty_setcolor (REVERSE_COLOR);
1119 while (TRUE)
1121 tmp = pop_directory ();
1122 if (tmp == NULL)
1124 running = FALSE;
1125 status_update (_("Finished"));
1126 find_rotate_dash (h, TRUE);
1127 stop_idle (h);
1128 return 0;
1131 /* handle absolute ignore dirs here */
1132 if (!find_ignore_dir_search (tmp))
1133 break;
1135 g_free (tmp);
1138 g_free (directory);
1139 directory = tmp;
1141 if (verbose)
1143 char buffer[BUF_SMALL];
1145 g_snprintf (buffer, sizeof (buffer), _("Searching %s"),
1146 str_trunc (directory, FIND2_X_USE));
1147 status_update (buffer);
1149 /* mc_stat should not be called after mc_opendir
1150 because vfs_s_opendir modifies the st_nlink
1152 if (mc_stat (directory, &tmp_stat) == 0)
1153 subdirs_left = tmp_stat.st_nlink - 2;
1154 else
1155 subdirs_left = 0;
1157 dirp = mc_opendir (directory);
1158 } /* while (!dirp) */
1160 /* skip invalid filenames */
1161 while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1163 } /* while (!dp) */
1165 if (strcmp (dp->d_name, ".") == 0 || strcmp (dp->d_name, "..") == 0)
1167 /* skip invalid filenames */
1168 while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1171 return 1;
1174 if (!(options.skip_hidden && (dp->d_name[0] == '.')))
1176 gboolean search_ok;
1178 if ((subdirs_left != 0) && options.find_recurs && (directory != NULL))
1179 { /* Can directory be NULL ? */
1180 /* handle relative ignore dirs here */
1181 if (!find_ignore_dir_search (dp->d_name))
1183 char *tmp_name;
1185 tmp_name = g_build_filename (directory, dp->d_name, (char *) NULL);
1187 if (mc_lstat (tmp_name, &tmp_stat) == 0 && S_ISDIR (tmp_stat.st_mode))
1189 push_directory (tmp_name);
1190 subdirs_left--;
1192 else
1193 g_free (tmp_name);
1197 search_ok = mc_search_run (search_file_handle, dp->d_name,
1198 0, strlen (dp->d_name), &bytes_found);
1200 if (search_ok)
1202 if (content_pattern == NULL)
1203 find_add_match (directory, dp->d_name);
1204 else if (search_content (h, directory, dp->d_name))
1205 return 1;
1209 /* skip invalid filenames */
1210 while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1212 } /* for */
1214 find_rotate_dash (h, FALSE);
1216 return 1;
1219 /* --------------------------------------------------------------------------------------------- */
1221 static void
1222 init_find_vars (void)
1224 g_free (old_dir);
1225 old_dir = NULL;
1226 matches = 0;
1228 /* Remove all the items from the stack */
1229 clear_stack ();
1232 /* --------------------------------------------------------------------------------------------- */
1234 static char *
1235 make_fullname (const char *dirname, const char *filename)
1238 if (strcmp (dirname, ".") == 0 || strcmp (dirname, "." PATH_SEP_STR) == 0)
1239 return g_strdup (filename);
1240 if (strncmp (dirname, "." PATH_SEP_STR, 2) == 0)
1241 return concat_dir_and_file (dirname + 2, filename);
1242 return concat_dir_and_file (dirname, filename);
1245 /* --------------------------------------------------------------------------------------------- */
1247 static void
1248 find_do_view_edit (int unparsed_view, int edit, char *dir, char *file)
1250 char *fullname = NULL;
1251 const char *filename = NULL;
1252 int line;
1254 if (content_pattern != NULL)
1256 filename = strchr (file + 4, ':') + 1;
1257 line = atoi (file + 4);
1259 else
1261 filename = file + 4;
1262 line = 0;
1265 fullname = make_fullname (dir, filename);
1266 if (edit)
1267 do_edit_at_line (fullname, use_internal_edit, line);
1268 else
1269 view_file_at_line (fullname, unparsed_view, use_internal_view, line);
1270 g_free (fullname);
1273 /* --------------------------------------------------------------------------------------------- */
1275 static cb_ret_t
1276 view_edit_currently_selected_file (int unparsed_view, int edit)
1278 char *dir = NULL;
1279 char *text = NULL;
1281 listbox_get_current (find_list, &text, (void **) &dir);
1283 if ((text == NULL) || (dir == NULL))
1284 return MSG_NOT_HANDLED;
1286 find_do_view_edit (unparsed_view, edit, dir, text);
1287 return MSG_HANDLED;
1290 /* --------------------------------------------------------------------------------------------- */
1292 static cb_ret_t
1293 find_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
1295 switch (msg)
1297 case DLG_KEY:
1298 if (parm == KEY_F (3) || parm == KEY_F (13))
1300 int unparsed_view = (parm == KEY_F (13));
1301 return view_edit_currently_selected_file (unparsed_view, 0);
1303 if (parm == KEY_F (4))
1305 return view_edit_currently_selected_file (0, 1);
1307 return MSG_NOT_HANDLED;
1309 case DLG_IDLE:
1310 do_search (h);
1311 return MSG_HANDLED;
1313 default:
1314 return default_dlg_callback (h, sender, msg, parm, data);
1318 /* --------------------------------------------------------------------------------------------- */
1319 /** Handles the Stop/Start button in the find window */
1321 static int
1322 start_stop (WButton * button, int action)
1324 (void) button;
1325 (void) action;
1327 running = is_start;
1328 set_idle_proc (find_dlg, running);
1329 is_start = !is_start;
1331 status_update (is_start ? _("Stopped") : _("Searching"));
1332 button_set_text (stop_button, fbuts[is_start ? 1 : 0].text);
1334 return 0;
1337 /* --------------------------------------------------------------------------------------------- */
1338 /** Handle view command, when invoked as a button */
1340 static int
1341 find_do_view_file (WButton * button, int action)
1343 (void) button;
1344 (void) action;
1346 view_edit_currently_selected_file (0, 0);
1347 return 0;
1350 /* --------------------------------------------------------------------------------------------- */
1351 /** Handle edit command, when invoked as a button */
1353 static int
1354 find_do_edit_file (WButton * button, int action)
1356 (void) button;
1357 (void) action;
1359 view_edit_currently_selected_file (0, 1);
1360 return 0;
1363 /* --------------------------------------------------------------------------------------------- */
1365 static void
1366 setup_gui (void)
1368 #ifdef ENABLE_NLS
1369 static gboolean i18n_flag = FALSE;
1371 if (!i18n_flag)
1373 int i = sizeof (fbuts) / sizeof (fbuts[0]);
1374 while (i-- != 0)
1376 fbuts[i].text = _(fbuts[i].text);
1377 fbuts[i].len = str_term_width1 (fbuts[i].text) + 3;
1380 fbuts[2].len += 2; /* DEFPUSH_BUTTON */
1381 i18n_flag = TRUE;
1383 #endif /* ENABLE_NLS */
1386 * Dynamically place buttons centered within current window size
1389 int l0 = max (fbuts[0].len, fbuts[1].len);
1390 int l1 = fbuts[2].len + fbuts[3].len + l0 + fbuts[4].len;
1391 int l2 = fbuts[5].len + fbuts[6].len + fbuts[7].len;
1392 int r1, r2;
1394 /* Check, if both button rows fit within FIND2_X */
1395 FIND2_X = max (l1 + 9, COLS - 16);
1396 FIND2_X = max (l2 + 8, FIND2_X);
1398 /* compute amount of space between buttons for each row */
1399 r1 = (FIND2_X - 4 - l1) % 5;
1400 l1 = (FIND2_X - 4 - l1) / 5;
1401 r2 = (FIND2_X - 4 - l2) % 4;
1402 l2 = (FIND2_X - 4 - l2) / 4;
1404 /* ...and finally, place buttons */
1405 fbuts[2].x = 2 + r1 / 2 + l1;
1406 fbuts[3].x = fbuts[2].x + fbuts[2].len + l1;
1407 fbuts[0].x = fbuts[3].x + fbuts[3].len + l1;
1408 fbuts[4].x = fbuts[0].x + l0 + l1;
1409 fbuts[5].x = 2 + r2 / 2 + l2;
1410 fbuts[6].x = fbuts[5].x + fbuts[5].len + l2;
1411 fbuts[7].x = fbuts[6].x + fbuts[6].len + l2;
1414 find_dlg =
1415 create_dlg (TRUE, 0, 0, FIND2_Y, FIND2_X, dialog_colors, find_callback,
1416 "[Find File]", _("Find File"), DLG_CENTER | DLG_REVERSE);
1418 add_widget (find_dlg,
1419 button_new (FIND2_Y - 3, fbuts[7].x, B_VIEW, NORMAL_BUTTON,
1420 fbuts[7].text, find_do_edit_file));
1421 add_widget (find_dlg,
1422 button_new (FIND2_Y - 3, fbuts[6].x, B_VIEW, NORMAL_BUTTON,
1423 fbuts[6].text, find_do_view_file));
1424 add_widget (find_dlg,
1425 button_new (FIND2_Y - 3, fbuts[5].x, B_PANELIZE, NORMAL_BUTTON, fbuts[5].text,
1426 NULL));
1428 add_widget (find_dlg,
1429 button_new (FIND2_Y - 4, fbuts[4].x, B_CANCEL, NORMAL_BUTTON, fbuts[4].text, NULL));
1430 stop_button =
1431 button_new (FIND2_Y - 4, fbuts[0].x, B_STOP, NORMAL_BUTTON, fbuts[0].text, start_stop);
1432 add_widget (find_dlg, stop_button);
1433 add_widget (find_dlg,
1434 button_new (FIND2_Y - 4, fbuts[3].x, B_AGAIN, NORMAL_BUTTON, fbuts[3].text, NULL));
1435 add_widget (find_dlg,
1436 button_new (FIND2_Y - 4, fbuts[2].x, B_ENTER, DEFPUSH_BUTTON, fbuts[2].text, NULL));
1438 status_label = label_new (FIND2_Y - 7, 4, _("Searching"));
1439 add_widget (find_dlg, status_label);
1441 found_num_label = label_new (FIND2_Y - 6, 4, "");
1442 add_widget (find_dlg, found_num_label);
1444 find_list = listbox_new (2, 2, FIND2_Y - 10, FIND2_X - 4, FALSE, NULL);
1445 add_widget (find_dlg, find_list);
1448 /* --------------------------------------------------------------------------------------------- */
1450 static int
1451 run_process (void)
1453 int ret;
1455 search_content_handle = mc_search_new (content_pattern, -1);
1456 if (search_content_handle)
1458 search_content_handle->search_type =
1459 options.content_regexp ? MC_SEARCH_T_REGEX : MC_SEARCH_T_NORMAL;
1460 search_content_handle->is_case_sensitive = options.content_case_sens;
1461 search_content_handle->whole_words = options.content_whole_words;
1462 search_content_handle->is_all_charsets = options.content_all_charsets;
1464 search_file_handle = mc_search_new (find_pattern, -1);
1465 search_file_handle->search_type = options.file_pattern ? MC_SEARCH_T_GLOB : MC_SEARCH_T_REGEX;
1466 search_file_handle->is_case_sensitive = options.file_case_sens;
1467 search_file_handle->is_all_charsets = options.file_all_charsets;
1468 search_file_handle->is_entire_line = options.file_pattern;
1470 resuming = 0;
1471 set_idle_proc (find_dlg, 1);
1472 ret = run_dlg (find_dlg);
1474 mc_search_free (search_file_handle);
1475 search_file_handle = NULL;
1476 mc_search_free (search_content_handle);
1477 search_content_handle = NULL;
1479 return ret;
1482 /* --------------------------------------------------------------------------------------------- */
1484 static void
1485 kill_gui (void)
1487 set_idle_proc (find_dlg, 0);
1488 destroy_dlg (find_dlg);
1491 /* --------------------------------------------------------------------------------------------- */
1493 static int
1494 find_file (const char *start_dir, const char *pattern, const char *content,
1495 char **dirname, char **filename)
1497 int return_value = 0;
1498 char *dir_tmp = NULL, *file_tmp = NULL;
1500 setup_gui ();
1502 /* FIXME: Need to cleanup this, this ought to be passed non-globaly */
1503 find_pattern = (char *) pattern;
1505 content_pattern = NULL;
1506 if (options.content_use && content != NULL && str_is_valid_string (content))
1507 content_pattern = g_strdup (content);
1509 init_find_vars ();
1510 push_directory (start_dir);
1512 return_value = run_process ();
1514 /* Remove all the items from the stack */
1515 clear_stack ();
1517 get_list_info (&file_tmp, &dir_tmp);
1519 if (dir_tmp)
1520 *dirname = g_strdup (dir_tmp);
1521 if (file_tmp)
1522 *filename = g_strdup (file_tmp);
1524 if (return_value == B_PANELIZE && *filename)
1526 int status, link_to_dir, stale_link;
1527 int next_free = 0;
1528 int i;
1529 struct stat st;
1530 GList *entry;
1531 dir_list *list = &current_panel->dir;
1532 char *name = NULL;
1534 for (i = 0, entry = find_list->list; entry != NULL; i++, entry = g_list_next (entry))
1536 const char *lc_filename = NULL;
1537 WLEntry *le = (WLEntry *) entry->data;
1539 if ((le->text == NULL) || (le->data == NULL))
1540 continue;
1542 if (content_pattern != NULL)
1543 lc_filename = strchr (le->text + 4, ':') + 1;
1544 else
1545 lc_filename = le->text + 4;
1547 name = make_fullname (le->data, lc_filename);
1548 status = handle_path (list, name, &st, next_free, &link_to_dir, &stale_link);
1549 if (status == 0)
1551 g_free (name);
1552 continue;
1554 if (status == -1)
1556 g_free (name);
1557 break;
1560 /* don't add files more than once to the panel */
1561 if (content_pattern != NULL && next_free > 0
1562 && strcmp (list->list[next_free - 1].fname, name) == 0)
1564 g_free (name);
1565 continue;
1568 if (!next_free) /* first turn i.e clean old list */
1569 panel_clean_dir (current_panel);
1570 list->list[next_free].fnamelen = strlen (name);
1571 list->list[next_free].fname = name;
1572 list->list[next_free].f.marked = 0;
1573 list->list[next_free].f.link_to_dir = link_to_dir;
1574 list->list[next_free].f.stale_link = stale_link;
1575 list->list[next_free].f.dir_size_computed = 0;
1576 list->list[next_free].st = st;
1577 list->list[next_free].sort_key = NULL;
1578 list->list[next_free].second_sort_key = NULL;
1579 next_free++;
1580 if (!(next_free & 15))
1581 rotate_dash ();
1584 if (next_free)
1586 current_panel->count = next_free;
1587 current_panel->is_panelized = 1;
1589 if (start_dir[0] == PATH_SEP)
1591 int ret;
1592 strcpy (current_panel->cwd, PATH_SEP_STR);
1593 ret = chdir (PATH_SEP_STR);
1598 g_free (content_pattern);
1599 kill_gui ();
1600 do_search (NULL); /* force do_search to release resources */
1601 g_free (old_dir);
1602 old_dir = NULL;
1604 return return_value;
1607 /* --------------------------------------------------------------------------------------------- */
1608 /*** public functions ****************************************************************************/
1609 /* --------------------------------------------------------------------------------------------- */
1611 void
1612 do_find (void)
1614 char *start_dir = NULL, *pattern = NULL, *content = NULL;
1615 char *filename = NULL, *dirname = NULL;
1616 int v;
1617 gboolean dir_and_file_set;
1619 while (find_parameters (&start_dir, &pattern, &content))
1621 if (pattern[0] == '\0')
1622 break; /* nothing search */
1624 dirname = filename = NULL;
1625 is_start = FALSE;
1626 v = find_file (start_dir, pattern, content, &dirname, &filename);
1627 g_free (pattern);
1629 if (v == B_ENTER)
1631 if (dirname != NULL)
1633 do_cd (dirname, cd_exact);
1634 if (filename != NULL)
1635 try_to_select (current_panel,
1636 filename + (content != NULL
1637 ? strchr (filename + 4, ':') - filename + 1 : 4));
1639 else if (filename != NULL)
1640 do_cd (filename, cd_exact);
1642 g_free (dirname);
1643 g_free (filename);
1644 break;
1647 g_free (content);
1648 dir_and_file_set = (dirname != NULL) && (filename != NULL);
1649 g_free (dirname);
1650 g_free (filename);
1652 if (v == B_CANCEL)
1653 break;
1655 if (v == B_PANELIZE)
1657 if (dir_and_file_set)
1659 try_to_select (current_panel, NULL);
1660 panel_re_sort (current_panel);
1661 try_to_select (current_panel, NULL);
1663 break;
1668 /* --------------------------------------------------------------------------------------------- */