1
1
mc/src/filemanager/find.c
Andrew Borodin 7df67031a2 Refactoring of WListbox widget.
Use GQueue instead of GList to store listbox entries.

g_list_append() function is slow because it uses g_list_last()
internally to traverse from the beginning to the end of a list, so
forming a list of results has O(n*n) complexity instead of O(n).
GQueue contains pointers to head and tail of list and list length.
So in this case we don't need seach end of list every time when we
want append listbox entry to the listbox.

Signed-off-by: Andrew Borodin <aborodin@vmail.ru>
2014-01-14 14:13:38 +04:00

1907 строки
58 KiB
C

/*
Find file command for the Midnight Commander
Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
2006, 2007, 2011, 2013
The Free Software Foundation, Inc.
Written by:
Miguel de Icaza, 1995
Slava Zanko <slavazanko@gmail.com>, 2013
Andrew Borodin <aborodin@vmail.ru>, 2013
This file is part of the Midnight Commander.
The Midnight Commander is free software: you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of the License,
or (at your option) any later version.
The Midnight Commander is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/** \file find.c
* \brief Source: Find file command
*/
#include <config.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/time.h>
#include "lib/global.h"
#include "lib/tty/tty.h"
#include "lib/tty/key.h"
#include "lib/skin.h"
#include "lib/search.h"
#include "lib/mcconfig.h"
#include "lib/vfs/vfs.h"
#include "lib/strutil.h"
#include "lib/widget.h"
#include "lib/util.h" /* canonicalize_pathname() */
#include "src/setup.h" /* verbose */
#include "src/history.h" /* MC_HISTORY_SHARED_SEARCH */
#include "dir.h"
#include "cmd.h" /* view_file_at_line */
#include "midnight.h" /* current_panel */
#include "boxes.h"
#include "panelize.h"
#include "find.h"
/*** global variables ****************************************************************************/
/*** file scope macro definitions ****************************************************************/
#define MAX_REFRESH_INTERVAL (G_USEC_PER_SEC / 20) /* 50 ms */
#define MIN_REFRESH_FILE_SIZE (256 * 1024) /* 256 KB */
/*** file scope type declarations ****************************************************************/
/* A couple of extra messages we need */
enum
{
B_STOP = B_USER + 1,
B_AGAIN,
B_PANELIZE,
B_TREE,
B_VIEW
};
typedef enum
{
FIND_CONT = 0,
FIND_SUSPEND,
FIND_ABORT
} FindProgressStatus;
/* find file options */
typedef struct
{
/* file name options */
gboolean file_case_sens;
gboolean file_pattern;
gboolean find_recurs;
gboolean skip_hidden;
gboolean file_all_charsets;
/* file content options */
gboolean content_use;
gboolean content_case_sens;
gboolean content_regexp;
gboolean content_first_hit;
gboolean content_whole_words;
gboolean content_all_charsets;
/* whether use ignore dirs or not */
gboolean ignore_dirs_enable;
/* list of directories to be ignored, separated by ':' */
char *ignore_dirs;
} find_file_options_t;
/*** file scope variables ************************************************************************/
/* button callbacks */
static int start_stop (WButton * button, int action);
static int find_do_view_file (WButton * button, int action);
static int find_do_edit_file (WButton * button, int action);
/* Parsed ignore dirs */
static char **find_ignore_dirs = NULL;
/* static variables to remember find parameters */
static WInput *in_start; /* Start path */
static WInput *in_name; /* Filename */
static WInput *in_with; /* Text */
static WInput *in_ignore;
static WLabel *content_label; /* 'Content:' label */
static WCheck *file_case_sens_cbox; /* "case sensitive" checkbox */
static WCheck *file_pattern_cbox; /* File name is glob or regexp */
static WCheck *recursively_cbox;
static WCheck *skip_hidden_cbox;
static WCheck *content_use_cbox; /* Take into account the Content field */
static WCheck *content_case_sens_cbox; /* "case sensitive" checkbox */
static WCheck *content_regexp_cbox; /* "find regular expression" checkbox */
static WCheck *content_first_hit_cbox; /* "First hit" checkbox" */
static WCheck *content_whole_words_cbox; /* "whole words" checkbox */
#ifdef HAVE_CHARSET
static WCheck *file_all_charsets_cbox;
static WCheck *content_all_charsets_cbox;
#endif
static WCheck *ignore_dirs_cbox;
static gboolean running = FALSE; /* nice flag */
static char *find_pattern = NULL; /* Pattern to search */
static char *content_pattern = NULL; /* pattern to search inside files; if
content_regexp_flag is true, it contains the
regex pattern, else the search string. */
static unsigned long matches; /* Number of matches */
static gboolean is_start = FALSE; /* Status of the start/stop toggle button */
static char *old_dir = NULL;
static struct timeval last_refresh;
/* Where did we stop */
static gboolean resuming;
static int last_line;
static int last_pos;
static size_t ignore_count = 0;
static WDialog *find_dlg; /* The dialog */
static WLabel *status_label; /* Finished, Searching etc. */
static WLabel *found_num_label; /* Number of found items */
/* This keeps track of the directory stack */
#if GLIB_CHECK_VERSION (2, 14, 0)
static GQueue dir_queue = G_QUEUE_INIT;
#else
typedef struct dir_stack
{
vfs_path_t *name;
struct dir_stack *prev;
} dir_stack;
static dir_stack *dir_stack_base = 0;
#endif /* GLIB_CHECK_VERSION */
/* *INDENT-OFF* */
static struct
{
int ret_cmd;
button_flags_t flags;
const char *text;
int len; /* length including space and brackets */
int x;
Widget *button;
bcback_fn callback;
} fbuts[] =
{
{ B_ENTER, DEFPUSH_BUTTON, N_("&Chdir"), 0, 0, NULL, NULL },
{ B_AGAIN, NORMAL_BUTTON, N_("&Again"), 0, 0, NULL, NULL },
{ B_STOP, NORMAL_BUTTON, N_("S&uspend"), 0, 0, NULL, start_stop },
{ B_STOP, NORMAL_BUTTON, N_("Con&tinue"), 0, 0, NULL, NULL },
{ B_CANCEL, NORMAL_BUTTON, N_("&Quit"), 0, 0, NULL, NULL },
{ B_PANELIZE, NORMAL_BUTTON, N_("Pane&lize"), 0, 0, NULL, NULL },
{ B_VIEW, NORMAL_BUTTON, N_("&View - F3"), 0, 0, NULL, find_do_view_file },
{ B_VIEW, NORMAL_BUTTON, N_("&Edit - F4"), 0, 0, NULL, find_do_edit_file }
};
/* *INDENT-ON* */
static const size_t fbuts_num = G_N_ELEMENTS (fbuts);
const size_t quit_button = 4; /* index of "Quit" button */
static WListbox *find_list; /* Listbox with the file list */
static find_file_options_t options = {
TRUE, TRUE, TRUE, FALSE, FALSE,
FALSE, TRUE, FALSE, FALSE, FALSE, FALSE
};
static char *in_start_dir = INPUT_LAST_TEXT;
static mc_search_t *search_file_handle = NULL;
static mc_search_t *search_content_handle = NULL;
/* --------------------------------------------------------------------------------------------- */
/*** file scope functions ************************************************************************/
/* --------------------------------------------------------------------------------------------- */
/* don't use max macro to avoid double str_term_width1() call in widget length caclulation */
#undef max
static int
max (int a, int b)
{
return (a > b ? a : b);
}
/* --------------------------------------------------------------------------------------------- */
static void
parse_ignore_dirs (const char *ignore_dirs)
{
size_t r = 0, w = 0; /* read and write iterators */
if (!options.ignore_dirs_enable || ignore_dirs == NULL || ignore_dirs[0] == '\0')
return;
find_ignore_dirs = g_strsplit (ignore_dirs, ":", -1);
/* Values like '/foo::/bar: produce holes in list.
* Find and remove them */
for (; find_ignore_dirs[r] != NULL; r++)
{
if (find_ignore_dirs[r][0] == '\0')
{
/* empty entry -- skip it */
g_free (find_ignore_dirs[r]);
find_ignore_dirs[r] = NULL;
continue;
}
if (r != w)
{
/* copy entry to the previous free array cell */
find_ignore_dirs[w] = find_ignore_dirs[r];
find_ignore_dirs[r] = NULL;
}
canonicalize_pathname (find_ignore_dirs[w]);
if (find_ignore_dirs[w][0] != '\0')
w++;
else
{
g_free (find_ignore_dirs[w]);
find_ignore_dirs[w] = NULL;
}
}
if (find_ignore_dirs[0] == NULL)
{
g_strfreev (find_ignore_dirs);
find_ignore_dirs = NULL;
}
}
/* --------------------------------------------------------------------------------------------- */
static void
find_load_options (void)
{
static gboolean loaded = FALSE;
if (loaded)
return;
loaded = TRUE;
options.file_case_sens =
mc_config_get_bool (mc_main_config, "FindFile", "file_case_sens", TRUE);
options.file_pattern =
mc_config_get_bool (mc_main_config, "FindFile", "file_shell_pattern", TRUE);
options.find_recurs = mc_config_get_bool (mc_main_config, "FindFile", "file_find_recurs", TRUE);
options.skip_hidden =
mc_config_get_bool (mc_main_config, "FindFile", "file_skip_hidden", FALSE);
options.file_all_charsets =
mc_config_get_bool (mc_main_config, "FindFile", "file_all_charsets", FALSE);
options.content_use = mc_config_get_bool (mc_main_config, "FindFile", "content_use", TRUE);
options.content_case_sens =
mc_config_get_bool (mc_main_config, "FindFile", "content_case_sens", TRUE);
options.content_regexp =
mc_config_get_bool (mc_main_config, "FindFile", "content_regexp", FALSE);
options.content_first_hit =
mc_config_get_bool (mc_main_config, "FindFile", "content_first_hit", FALSE);
options.content_whole_words =
mc_config_get_bool (mc_main_config, "FindFile", "content_whole_words", FALSE);
options.content_all_charsets =
mc_config_get_bool (mc_main_config, "FindFile", "content_all_charsets", FALSE);
options.ignore_dirs_enable =
mc_config_get_bool (mc_main_config, "FindFile", "ignore_dirs_enable", TRUE);
options.ignore_dirs = mc_config_get_string (mc_main_config, "FindFile", "ignore_dirs", "");
if (options.ignore_dirs[0] == '\0')
{
g_free (options.ignore_dirs);
options.ignore_dirs = NULL;
}
}
/* --------------------------------------------------------------------------------------------- */
static void
find_save_options (void)
{
mc_config_set_bool (mc_main_config, "FindFile", "file_case_sens", options.file_case_sens);
mc_config_set_bool (mc_main_config, "FindFile", "file_shell_pattern", options.file_pattern);
mc_config_set_bool (mc_main_config, "FindFile", "file_find_recurs", options.find_recurs);
mc_config_set_bool (mc_main_config, "FindFile", "file_skip_hidden", options.skip_hidden);
mc_config_set_bool (mc_main_config, "FindFile", "file_all_charsets", options.file_all_charsets);
mc_config_set_bool (mc_main_config, "FindFile", "content_use", options.content_use);
mc_config_set_bool (mc_main_config, "FindFile", "content_case_sens", options.content_case_sens);
mc_config_set_bool (mc_main_config, "FindFile", "content_regexp", options.content_regexp);
mc_config_set_bool (mc_main_config, "FindFile", "content_first_hit", options.content_first_hit);
mc_config_set_bool (mc_main_config, "FindFile", "content_whole_words",
options.content_whole_words);
mc_config_set_bool (mc_main_config, "FindFile", "content_all_charsets",
options.content_all_charsets);
mc_config_set_bool (mc_main_config, "FindFile", "ignore_dirs_enable",
options.ignore_dirs_enable);
mc_config_set_string (mc_main_config, "FindFile", "ignore_dirs", options.ignore_dirs);
}
/* --------------------------------------------------------------------------------------------- */
static inline char *
add_to_list (const char *text, void *data)
{
return listbox_add_item (find_list, LISTBOX_APPEND_AT_END, 0, text, data);
}
/* --------------------------------------------------------------------------------------------- */
static inline void
stop_idle (void *data)
{
widget_want_idle (WIDGET (data), FALSE);
}
/* --------------------------------------------------------------------------------------------- */
static inline void
status_update (const char *text)
{
label_set_text (status_label, text);
}
/* --------------------------------------------------------------------------------------------- */
static void
found_num_update (void)
{
char buffer[BUF_TINY];
g_snprintf (buffer, sizeof (buffer), _("Found: %ld"), matches);
label_set_text (found_num_label, buffer);
}
/* --------------------------------------------------------------------------------------------- */
static void
get_list_info (char **file, char **dir)
{
listbox_get_current (find_list, file, (void **) dir);
}
/* --------------------------------------------------------------------------------------------- */
/** check regular expression */
static gboolean
find_check_regexp (const char *r)
{
mc_search_t *search;
gboolean regexp_ok = FALSE;
search = mc_search_new (r, -1, NULL);
if (search != NULL)
{
search->search_type = MC_SEARCH_T_REGEX;
regexp_ok = mc_search_prepare (search);
mc_search_free (search);
}
return regexp_ok;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Callback for the parameter dialog.
* Validate regex, prevent closing the dialog if it's invalid.
*/
static cb_ret_t
find_parm_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
{
WDialog *h = DIALOG (w);
switch (msg)
{
case MSG_ACTION:
if (sender == WIDGET (content_use_cbox))
{
gboolean disable = !(content_use_cbox->state & C_BOOL);
widget_disable (WIDGET (in_with), disable);
widget_disable (WIDGET (content_first_hit_cbox), disable);
widget_disable (WIDGET (content_regexp_cbox), disable);
widget_disable (WIDGET (content_case_sens_cbox), disable);
#ifdef HAVE_CHARSET
widget_disable (WIDGET (content_all_charsets_cbox), disable);
#endif
widget_disable (WIDGET (content_whole_words_cbox), disable);
return MSG_HANDLED;
}
if (sender == WIDGET (ignore_dirs_cbox))
{
gboolean disable = !(ignore_dirs_cbox->state & C_BOOL);
widget_disable (WIDGET (in_ignore), disable);
return MSG_HANDLED;
}
return MSG_NOT_HANDLED;
case MSG_VALIDATE:
if (h->ret_value != B_ENTER)
return MSG_HANDLED;
/* check filename regexp */
if (!(file_pattern_cbox->state & C_BOOL)
&& (in_name->buffer[0] != '\0') && !find_check_regexp (in_name->buffer))
{
h->state = DLG_ACTIVE; /* Don't stop the dialog */
message (D_ERROR, MSG_ERROR, _("Malformed regular expression"));
dlg_select_widget (in_name);
return MSG_HANDLED;
}
/* check content regexp */
if ((content_use_cbox->state & C_BOOL) && (content_regexp_cbox->state & C_BOOL)
&& (in_with->buffer[0] != '\0') && !find_check_regexp (in_with->buffer))
{
h->state = DLG_ACTIVE; /* Don't stop the dialog */
message (D_ERROR, MSG_ERROR, _("Malformed regular expression"));
dlg_select_widget (in_with);
return MSG_HANDLED;
}
return MSG_HANDLED;
default:
return dlg_default_callback (w, sender, msg, parm, data);
}
}
/* --------------------------------------------------------------------------------------------- */
/**
* find_parameters: gets information from the user
*
* If the return value is TRUE, then the following holds:
*
* start_dir, ignore_dirs, pattern and content contain the information provided by the user.
* They are newly allocated strings and must be freed when uneeded.
*
* start_dir_len is -1 when user entered an absolute path, otherwise it is a length
* of start_dir (which is absolute). It is used to get a relative pats of find results.
*/
static gboolean
find_parameters (char **start_dir, ssize_t * start_dir_len,
char **ignore_dirs, char **pattern, char **content)
{
/* Size of the find parameters window */
#ifdef HAVE_CHARSET
const int lines = 19;
#else
const int lines = 18;
#endif
int cols = 68;
gboolean return_value;
/* file name */
const char *file_name_label = N_("File name:");
const char *file_recurs_label = N_("&Find recursively");
const char *file_pattern_label = N_("&Using shell patterns");
#ifdef HAVE_CHARSET
const char *file_all_charsets_label = N_("&All charsets");
#endif
const char *file_case_label = N_("Cas&e sensitive");
const char *file_skip_hidden_label = N_("S&kip hidden");
/* file content */
const char *content_content_label = N_("Content:");
const char *content_use_label = N_("Sea&rch for content");
const char *content_regexp_label = N_("Re&gular expression");
const char *content_case_label = N_("Case sens&itive");
#ifdef HAVE_CHARSET
const char *content_all_charsets_label = N_("A&ll charsets");
#endif
const char *content_whole_words_label = N_("&Whole words");
const char *content_first_hit_label = N_("Fir&st hit");
const char *buts[] = { N_("&Tree"), N_("&OK"), N_("&Cancel") };
/* button lengths */
int b0, b1, b2, b12;
int y1, y2, x1, x2;
/* column width */
int cw;
gboolean disable;
#ifdef ENABLE_NLS
{
size_t i;
file_name_label = _(file_name_label);
file_recurs_label = _(file_recurs_label);
file_pattern_label = _(file_pattern_label);
#ifdef HAVE_CHARSET
file_all_charsets_label = _(file_all_charsets_label);
#endif
file_case_label = _(file_case_label);
file_skip_hidden_label = _(file_skip_hidden_label);
/* file content */
content_content_label = _(content_content_label);
content_use_label = _(content_use_label);
content_regexp_label = _(content_regexp_label);
content_case_label = _(content_case_label);
#ifdef HAVE_CHARSET
content_all_charsets_label = _(content_all_charsets_label);
#endif
content_whole_words_label = _(content_whole_words_label);
content_first_hit_label = _(content_first_hit_label);
for (i = 0; i < G_N_ELEMENTS (buts); i++)
buts[i] = _(buts[i]);
}
#endif /* ENABLE_NLS */
/* caclulate dialog width */
/* widget widths */
cw = str_term_width1 (file_name_label);
cw = max (cw, str_term_width1 (file_recurs_label) + 4);
cw = max (cw, str_term_width1 (file_pattern_label) + 4);
#ifdef HAVE_CHARSET
cw = max (cw, str_term_width1 (file_all_charsets_label) + 4);
#endif
cw = max (cw, str_term_width1 (file_case_label) + 4);
cw = max (cw, str_term_width1 (file_skip_hidden_label) + 4);
cw = max (cw, str_term_width1 (content_content_label) + 4);
cw = max (cw, str_term_width1 (content_use_label) + 4);
cw = max (cw, str_term_width1 (content_regexp_label) + 4);
cw = max (cw, str_term_width1 (content_case_label) + 4);
#ifdef HAVE_CHARSET
cw = max (cw, str_term_width1 (content_all_charsets_label) + 4);
#endif
cw = max (cw, str_term_width1 (content_whole_words_label) + 4);
cw = max (cw, str_term_width1 (content_first_hit_label) + 4);
/* button width */
b0 = str_term_width1 (buts[0]) + 3;
b1 = str_term_width1 (buts[1]) + 5; /* default button */
b2 = str_term_width1 (buts[2]) + 3;
b12 = b1 + b2 + 1;
cols = max (cols, max (b12, cw * 2 + 1) + 6);
find_load_options ();
if (in_start_dir == NULL)
in_start_dir = g_strdup (".");
disable = !options.content_use;
find_dlg =
dlg_create (TRUE, 0, 0, lines, cols, dialog_colors, find_parm_callback, NULL, "[Find File]",
_("Find File"), DLG_CENTER);
x1 = 3;
x2 = cols / 2 + 1;
cw = (cols - 7) / 2;
y1 = 2;
add_widget (find_dlg, label_new (y1++, x1, _("Start at:")));
in_start =
input_new (y1, x1, input_get_default_colors (), cols - b0 - 7, in_start_dir, "start",
INPUT_COMPLETE_CD | INPUT_COMPLETE_FILENAMES);
add_widget (find_dlg, in_start);
add_widget (find_dlg, button_new (y1++, cols - b0 - 3, B_TREE, NORMAL_BUTTON, buts[0], NULL));
ignore_dirs_cbox =
check_new (y1++, x1, options.ignore_dirs_enable, _("Ena&ble ignore directories:"));
add_widget (find_dlg, ignore_dirs_cbox);
in_ignore =
input_new (y1++, x1, input_get_default_colors (), cols - 6,
options.ignore_dirs != NULL ? options.ignore_dirs : "", "ignoredirs",
INPUT_COMPLETE_CD | INPUT_COMPLETE_FILENAMES);
widget_disable (WIDGET (in_ignore), !options.ignore_dirs_enable);
add_widget (find_dlg, in_ignore);
add_widget (find_dlg, hline_new (y1++, -1, -1));
y2 = y1;
/* Start 1st column */
add_widget (find_dlg, label_new (y1++, x1, file_name_label));
in_name =
input_new (y1++, x1, input_get_default_colors (), cw, INPUT_LAST_TEXT, "name",
INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_CD);
add_widget (find_dlg, in_name);
/* Start 2nd column */
content_label = label_new (y2++, x2, content_content_label);
add_widget (find_dlg, content_label);
in_with =
input_new (y2++, x2, input_get_default_colors (), cw, INPUT_LAST_TEXT,
MC_HISTORY_SHARED_SEARCH, INPUT_COMPLETE_NONE);
in_with->label = content_label;
widget_disable (WIDGET (in_with), disable);
add_widget (find_dlg, in_with);
content_use_cbox = check_new (y2++, x2, options.content_use, content_use_label);
add_widget (find_dlg, content_use_cbox);
/* Continue 1st column */
recursively_cbox = check_new (y1++, x1, options.find_recurs, file_recurs_label);
add_widget (find_dlg, recursively_cbox);
file_pattern_cbox = check_new (y1++, x1, options.file_pattern, file_pattern_label);
add_widget (find_dlg, file_pattern_cbox);
file_case_sens_cbox = check_new (y1++, x1, options.file_case_sens, file_case_label);
add_widget (find_dlg, file_case_sens_cbox);
#ifdef HAVE_CHARSET
file_all_charsets_cbox =
check_new (y1++, x1, options.file_all_charsets, file_all_charsets_label);
add_widget (find_dlg, file_all_charsets_cbox);
#endif
skip_hidden_cbox = check_new (y1++, x1, options.skip_hidden, file_skip_hidden_label);
add_widget (find_dlg, skip_hidden_cbox);
/* Continue 2nd column */
content_regexp_cbox = check_new (y2++, x2, options.content_regexp, content_regexp_label);
widget_disable (WIDGET (content_regexp_cbox), disable);
add_widget (find_dlg, content_regexp_cbox);
content_case_sens_cbox = check_new (y2++, x2, options.content_case_sens, content_case_label);
widget_disable (WIDGET (content_case_sens_cbox), disable);
add_widget (find_dlg, content_case_sens_cbox);
#ifdef HAVE_CHARSET
content_all_charsets_cbox =
check_new (y2++, x2, options.content_all_charsets, content_all_charsets_label);
widget_disable (WIDGET (content_all_charsets_cbox), disable);
add_widget (find_dlg, content_all_charsets_cbox);
#endif
content_whole_words_cbox =
check_new (y2++, x2, options.content_whole_words, content_whole_words_label);
widget_disable (WIDGET (content_whole_words_cbox), disable);
add_widget (find_dlg, content_whole_words_cbox);
content_first_hit_cbox =
check_new (y2++, x2, options.content_first_hit, content_first_hit_label);
widget_disable (WIDGET (content_first_hit_cbox), disable);
add_widget (find_dlg, content_first_hit_cbox);
/* buttons */
y1 = max (y1, y2);
x1 = (cols - b12) / 2;
add_widget (find_dlg, hline_new (y1++, -1, -1));
add_widget (find_dlg, button_new (y1, x1, B_ENTER, DEFPUSH_BUTTON, buts[1], NULL));
add_widget (find_dlg, button_new (y1, x1 + b1 + 1, B_CANCEL, NORMAL_BUTTON, buts[2], NULL));
find_par_start:
dlg_select_widget (in_name);
switch (dlg_run (find_dlg))
{
case B_CANCEL:
return_value = FALSE;
break;
case B_TREE:
{
char *temp_dir;
temp_dir = in_start->buffer;
if (*temp_dir == '\0' || DIR_IS_DOT (temp_dir))
temp_dir = g_strdup (vfs_path_as_str (current_panel->cwd_vpath));
else
temp_dir = g_strdup (temp_dir);
if (in_start_dir != INPUT_LAST_TEXT)
g_free (in_start_dir);
in_start_dir = tree_box (temp_dir);
if (in_start_dir == NULL)
in_start_dir = temp_dir;
else
g_free (temp_dir);
input_assign_text (in_start, in_start_dir);
/* Warning: Dreadful goto */
goto find_par_start;
}
default:
{
char *s;
#ifdef HAVE_CHARSET
options.file_all_charsets = file_all_charsets_cbox->state & C_BOOL;
options.content_all_charsets = content_all_charsets_cbox->state & C_BOOL;
#endif
options.content_use = content_use_cbox->state & C_BOOL;
options.content_case_sens = content_case_sens_cbox->state & C_BOOL;
options.content_regexp = content_regexp_cbox->state & C_BOOL;
options.content_first_hit = content_first_hit_cbox->state & C_BOOL;
options.content_whole_words = content_whole_words_cbox->state & C_BOOL;
options.find_recurs = recursively_cbox->state & C_BOOL;
options.file_pattern = file_pattern_cbox->state & C_BOOL;
options.file_case_sens = file_case_sens_cbox->state & C_BOOL;
options.skip_hidden = skip_hidden_cbox->state & C_BOOL;
options.ignore_dirs_enable = ignore_dirs_cbox->state & C_BOOL;
g_free (options.ignore_dirs);
options.ignore_dirs = g_strdup (in_ignore->buffer);
*content = (options.content_use && in_with->buffer[0] != '\0')
? g_strdup (in_with->buffer) : NULL;
*start_dir = in_start->buffer[0] != '\0' ? in_start->buffer : (char *) ".";
*pattern = g_strdup (in_name->buffer);
if (in_start_dir != INPUT_LAST_TEXT)
g_free (in_start_dir);
in_start_dir = g_strdup (*start_dir);
s = tilde_expand (*start_dir);
canonicalize_pathname (s);
if (DIR_IS_DOT (s))
{
*start_dir = g_strdup (vfs_path_as_str (current_panel->cwd_vpath));
/* FIXME: is current_panel->cwd_vpath canonicalized? */
/* relative paths will be used in panelization */
*start_dir_len = (ssize_t) strlen (*start_dir);
g_free (s);
}
else if (g_path_is_absolute (s))
{
*start_dir = s;
*start_dir_len = -1;
}
else
{
/* relative paths will be used in panelization */
*start_dir =
mc_build_filename (vfs_path_as_str (current_panel->cwd_vpath), s,
(char *) NULL);
*start_dir_len = (ssize_t) strlen (vfs_path_as_str (current_panel->cwd_vpath));
g_free (s);
}
if (!options.ignore_dirs_enable || in_ignore->buffer[0] == '\0'
|| DIR_IS_DOT (in_ignore->buffer))
*ignore_dirs = NULL;
else
*ignore_dirs = g_strdup (in_ignore->buffer);
find_save_options ();
return_value = TRUE;
}
}
dlg_destroy (find_dlg);
return return_value;
}
/* --------------------------------------------------------------------------------------------- */
#if GLIB_CHECK_VERSION (2, 14, 0)
static inline void
push_directory (const vfs_path_t * dir)
{
g_queue_push_head (&dir_queue, (void *) dir);
}
/* --------------------------------------------------------------------------------------------- */
static inline vfs_path_t *
pop_directory (void)
{
return (vfs_path_t *) g_queue_pop_tail (&dir_queue);
}
/* --------------------------------------------------------------------------------------------- */
/** Remove all the items from the stack */
static void
clear_stack (void)
{
g_queue_foreach (&dir_queue, (GFunc) vfs_path_free, NULL);
g_queue_clear (&dir_queue);
}
/* --------------------------------------------------------------------------------------------- */
#else /* GLIB_CHECK_VERSION */
static void
push_directory (const vfs_path_t * dir)
{
dir_stack *new;
new = g_new (dir_stack, 1);
new->name = (vfs_path_t *) dir;
new->prev = dir_stack_base;
dir_stack_base = new;
}
/* --------------------------------------------------------------------------------------------- */
static vfs_path_t *
pop_directory (void)
{
vfs_path_t *name = NULL;
if (dir_stack_base != NULL)
{
dir_stack *next;
name = dir_stack_base->name;
next = dir_stack_base->prev;
g_free (dir_stack_base);
dir_stack_base = next;
}
return name;
}
/* --------------------------------------------------------------------------------------------- */
/** Remove all the items from the stack */
static void
clear_stack (void)
{
vfs_path_t *dir = NULL;
while ((dir = pop_directory ()) != NULL)
vfs_path_free (dir);
}
#endif /* GLIB_CHECK_VERSION */
/* --------------------------------------------------------------------------------------------- */
static void
insert_file (const char *dir, const char *file)
{
char *tmp_name = NULL;
static char *dirname = NULL;
while (dir[0] == PATH_SEP && dir[1] == PATH_SEP)
dir++;
if (old_dir)
{
if (strcmp (old_dir, dir))
{
g_free (old_dir);
old_dir = g_strdup (dir);
dirname = add_to_list (dir, NULL);
}
}
else
{
old_dir = g_strdup (dir);
dirname = add_to_list (dir, NULL);
}
tmp_name = g_strdup_printf (" %s", file);
add_to_list (tmp_name, dirname);
g_free (tmp_name);
}
/* --------------------------------------------------------------------------------------------- */
static void
find_add_match (const char *dir, const char *file)
{
insert_file (dir, file);
/* Don't scroll */
if (matches == 0)
listbox_select_first (find_list);
widget_redraw (WIDGET (find_list));
matches++;
found_num_update ();
}
/* --------------------------------------------------------------------------------------------- */
static FindProgressStatus
check_find_events (WDialog * h)
{
Gpm_Event event;
int c;
event.x = -1;
c = tty_get_event (&event, h->mouse_status == MOU_REPEAT, FALSE);
if (c != EV_NONE)
{
dlg_process_event (h, c, &event);
if (h->ret_value == B_ENTER
|| h->ret_value == B_CANCEL || h->ret_value == B_AGAIN || h->ret_value == B_PANELIZE)
{
/* dialog terminated */
return FIND_ABORT;
}
if ((WIDGET (h)->options & W_WANT_IDLE) == 0)
{
/* searching suspended */
return FIND_SUSPEND;
}
}
return FIND_CONT;
}
/* --------------------------------------------------------------------------------------------- */
/**
* search_content:
*
* Search the content_pattern string in the DIRECTORY/FILE.
* It will add the found entries to the find listbox.
*
* returns FALSE if do_search should look for another file
* TRUE if do_search should exit and proceed to the event handler
*/
static gboolean
search_content (WDialog * h, const char *directory, const char *filename)
{
struct stat s;
char buffer[BUF_4K]; /* raw input buffer */
int file_fd;
gboolean ret_val = FALSE;
vfs_path_t *vpath;
struct timeval tv;
time_t seconds;
suseconds_t useconds;
gboolean status_updated = FALSE;
vpath = vfs_path_build_filename (directory, filename, (char *) NULL);
if (mc_stat (vpath, &s) != 0 || !S_ISREG (s.st_mode))
{
vfs_path_free (vpath);
return FALSE;
}
file_fd = mc_open (vpath, O_RDONLY);
vfs_path_free (vpath);
if (file_fd == -1)
return FALSE;
/* get time elapsed from last refresh */
if (gettimeofday (&tv, NULL) == -1)
{
tv.tv_sec = 0;
tv.tv_usec = 0;
last_refresh = tv;
}
seconds = tv.tv_sec - last_refresh.tv_sec;
useconds = tv.tv_usec - last_refresh.tv_usec;
if (useconds < 0)
{
seconds--;
useconds += G_USEC_PER_SEC;
}
if (s.st_size >= MIN_REFRESH_FILE_SIZE || seconds > 0 || useconds > MAX_REFRESH_INTERVAL)
{
g_snprintf (buffer, sizeof (buffer), _("Grepping in %s"), filename);
status_update (str_trunc (buffer, WIDGET (h)->cols - 8));
mc_refresh ();
last_refresh = tv;
status_updated = TRUE;
}
tty_enable_interrupt_key ();
tty_got_interrupt ();
{
int line = 1;
int pos = 0;
int n_read = 0;
gboolean found = FALSE;
gsize found_len;
char result[BUF_MEDIUM];
char *strbuf = NULL; /* buffer for fetched string */
int strbuf_size = 0;
if (resuming)
{
/* We've been previously suspended, start from the previous position */
resuming = FALSE;
line = last_line;
pos = last_pos;
}
while (!ret_val)
{
char ch = '\0';
int i = 0;
/* read to buffer and get line from there */
while (TRUE)
{
if (pos >= n_read)
{
pos = 0;
n_read = mc_read (file_fd, buffer, sizeof (buffer));
if (n_read <= 0)
break;
}
ch = buffer[pos++];
if (ch == '\0')
{
/* skip possible leading zero(s) */
if (i == 0)
continue;
break;
}
if (i >= strbuf_size - 1)
{
strbuf_size += 128;
strbuf = g_realloc (strbuf, strbuf_size);
}
/* Strip newline */
if (ch == '\n')
break;
strbuf[i++] = ch;
}
if (i == 0)
{
if (ch == '\0')
break;
/* if (ch == '\n'): do not search in empty strings */
goto skip_search;
}
strbuf[i] = '\0';
if (!found /* Search in binary line once */
&& mc_search_run (search_content_handle, (const void *) strbuf, 0, i, &found_len))
{
if (!status_updated)
{
/* if we add results for a file, we have to ensure that
name of this file is shown in status bar */
g_snprintf (result, sizeof (result), _("Grepping in %s"), filename);
status_update (str_trunc (result, WIDGET (h)->cols - 8));
mc_refresh ();
last_refresh = tv;
status_updated = TRUE;
}
g_snprintf (result, sizeof (result), "%d:%s", line, filename);
find_add_match (directory, result);
found = TRUE;
}
if (found && options.content_first_hit)
break;
if (ch == '\n')
{
skip_search:
found = FALSE;
line++;
}
if ((line & 0xff) == 0)
{
FindProgressStatus res;
res = check_find_events (h);
switch (res)
{
case FIND_ABORT:
stop_idle (h);
ret_val = TRUE;
break;
case FIND_SUSPEND:
resuming = TRUE;
last_line = line;
last_pos = pos;
ret_val = TRUE;
break;
default:
break;
}
}
}
g_free (strbuf);
}
tty_disable_interrupt_key ();
mc_close (file_fd);
return ret_val;
}
/* --------------------------------------------------------------------------------------------- */
/**
If dir is absolute, this means we're within dir and searching file here.
If dir is relative, this means we're going to add dir to the directory stack.
**/
static gboolean
find_ignore_dir_search (const char *dir)
{
if (find_ignore_dirs != NULL)
{
const size_t dlen = strlen (dir);
const unsigned char dabs = g_path_is_absolute (dir) ? 1 : 0;
char **ignore_dir;
for (ignore_dir = find_ignore_dirs; *ignore_dir != NULL; ignore_dir++)
{
const size_t ilen = strlen (*ignore_dir);
const unsigned char iabs = g_path_is_absolute (*ignore_dir) ? 2 : 0;
/* ignore dir is too long -- skip it */
if (dlen < ilen)
continue;
/* handle absolute and relative paths */
switch (iabs | dabs)
{
case 0: /* both paths are relative */
case 3: /* both paths are abolute */
/* if ignore dir is not a path of dir -- skip it */
if (strncmp (dir, *ignore_dir, ilen) == 0)
{
/* be sure that ignore dir is not a part of dir like:
ignore dir is "h", dir is "home" */
if (dir[ilen] == '\0' || dir[ilen] == PATH_SEP)
return TRUE;
}
break;
case 1: /* dir is absolute, ignore_dir is relative */
{
char *d;
d = strstr (dir, *ignore_dir);
if (d != NULL && d[-1] == PATH_SEP && (d[ilen] == '\0' || d[ilen] == PATH_SEP))
return TRUE;
}
break;
case 2: /* dir is relative, ignore_dir is absolute */
/* FIXME: skip this case */
break;
default: /* this cannot occurs */
return FALSE;
}
}
}
return FALSE;
}
/* --------------------------------------------------------------------------------------------- */
static void
find_rotate_dash (const WDialog * h, gboolean show)
{
static size_t pos = 0;
static const char rotating_dash[4] = "|/-\\";
const Widget *w = WIDGET (h);
if (!verbose)
return;
tty_setcolor (h->color[DLG_COLOR_NORMAL]);
widget_move (h, w->lines - 7, w->cols - 4);
tty_print_char (show ? rotating_dash[pos] : ' ');
pos = (pos + 1) % sizeof (rotating_dash);
mc_refresh ();
}
/* --------------------------------------------------------------------------------------------- */
static int
do_search (WDialog * h)
{
static struct dirent *dp = NULL;
static DIR *dirp = NULL;
static char *directory = NULL;
struct stat tmp_stat;
static int subdirs_left = 0;
gsize bytes_found;
unsigned short count;
if (h == NULL)
{ /* someone forces me to close dirp */
if (dirp != NULL)
{
mc_closedir (dirp);
dirp = NULL;
}
g_free (directory);
directory = NULL;
dp = NULL;
return 1;
}
for (count = 0; count < 32; count++)
{
while (dp == NULL)
{
if (dirp != NULL)
{
mc_closedir (dirp);
dirp = NULL;
}
while (dirp == NULL)
{
vfs_path_t *tmp_vpath = NULL;
tty_setcolor (REVERSE_COLOR);
while (TRUE)
{
tmp_vpath = pop_directory ();
if (tmp_vpath == NULL)
{
running = FALSE;
if (ignore_count == 0)
status_update (_("Finished"));
else
{
char msg[BUF_SMALL];
g_snprintf (msg, sizeof (msg),
ngettext ("Finished (ignored %zd directory)",
"Finished (ignored %zd directories)",
ignore_count), ignore_count);
status_update (msg);
}
find_rotate_dash (h, FALSE);
stop_idle (h);
return 0;
}
/* handle absolute ignore dirs here */
{
gboolean ok;
ok = find_ignore_dir_search (vfs_path_as_str (tmp_vpath));
if (!ok)
break;
}
vfs_path_free (tmp_vpath);
ignore_count++;
}
g_free (directory);
directory = g_strdup (vfs_path_as_str (tmp_vpath));
if (verbose)
{
char buffer[BUF_MEDIUM];
g_snprintf (buffer, sizeof (buffer), _("Searching %s"), directory);
status_update (str_trunc (directory, WIDGET (h)->cols - 8));
}
/* mc_stat should not be called after mc_opendir
because vfs_s_opendir modifies the st_nlink
*/
if (mc_stat (tmp_vpath, &tmp_stat) == 0)
subdirs_left = tmp_stat.st_nlink - 2;
else
subdirs_left = 0;
dirp = mc_opendir (tmp_vpath);
vfs_path_free (tmp_vpath);
} /* while (!dirp) */
/* skip invalid filenames */
while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
;
} /* while (!dp) */
if (DIR_IS_DOT (dp->d_name) || DIR_IS_DOTDOT (dp->d_name))
{
/* skip invalid filenames */
while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
;
return 1;
}
if (!(options.skip_hidden && (dp->d_name[0] == '.')))
{
gboolean search_ok;
if ((subdirs_left != 0) && options.find_recurs && (directory != NULL))
{ /* Can directory be NULL ? */
/* handle relative ignore dirs here */
if (options.ignore_dirs_enable && find_ignore_dir_search (dp->d_name))
ignore_count++;
else
{
vfs_path_t *tmp_vpath;
tmp_vpath = vfs_path_build_filename (directory, dp->d_name, (char *) NULL);
if (mc_lstat (tmp_vpath, &tmp_stat) == 0 && S_ISDIR (tmp_stat.st_mode))
{
push_directory (tmp_vpath);
subdirs_left--;
}
else
vfs_path_free (tmp_vpath);
}
}
search_ok = mc_search_run (search_file_handle, dp->d_name,
0, strlen (dp->d_name), &bytes_found);
if (search_ok)
{
if (content_pattern == NULL)
find_add_match (directory, dp->d_name);
else if (search_content (h, directory, dp->d_name))
return 1;
}
}
/* skip invalid filenames */
while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
;
} /* for */
find_rotate_dash (h, TRUE);
return 1;
}
/* --------------------------------------------------------------------------------------------- */
static void
init_find_vars (void)
{
g_free (old_dir);
old_dir = NULL;
matches = 0;
ignore_count = 0;
/* Remove all the items from the stack */
clear_stack ();
g_strfreev (find_ignore_dirs);
find_ignore_dirs = NULL;
}
/* --------------------------------------------------------------------------------------------- */
static void
find_do_view_edit (gboolean unparsed_view, gboolean edit, char *dir, char *file)
{
char *fullname = NULL;
const char *filename = NULL;
int line;
vfs_path_t *fullname_vpath;
if (content_pattern != NULL)
{
filename = strchr (file + 4, ':') + 1;
line = atoi (file + 4);
}
else
{
filename = file + 4;
line = 0;
}
fullname_vpath = vfs_path_build_filename (dir, filename, (char *) NULL);
if (edit)
do_edit_at_line (fullname_vpath, use_internal_edit, line);
else
view_file_at_line (fullname_vpath, unparsed_view ? 1 : 0, use_internal_view, line);
vfs_path_free (fullname_vpath);
g_free (fullname);
}
/* --------------------------------------------------------------------------------------------- */
static cb_ret_t
view_edit_currently_selected_file (gboolean unparsed_view, gboolean edit)
{
char *dir = NULL;
char *text = NULL;
listbox_get_current (find_list, &text, (void **) &dir);
if ((text == NULL) || (dir == NULL))
return MSG_NOT_HANDLED;
find_do_view_edit (unparsed_view, edit, dir, text);
return MSG_HANDLED;
}
/* --------------------------------------------------------------------------------------------- */
static void
find_calc_button_locations (const WDialog * h, gboolean all_buttons)
{
const int cols = WIDGET (h)->cols;
int l1, l2;
l1 = fbuts[0].len + fbuts[1].len + fbuts[is_start ? 3 : 2].len + fbuts[4].len + 3;
l2 = fbuts[5].len + fbuts[6].len + fbuts[7].len + 2;
fbuts[0].x = (cols - l1) / 2;
fbuts[1].x = fbuts[0].x + fbuts[0].len + 1;
fbuts[2].x = fbuts[1].x + fbuts[1].len + 1;
fbuts[3].x = fbuts[2].x;
fbuts[4].x = fbuts[2].x + fbuts[is_start ? 3 : 2].len + 1;
if (all_buttons)
{
fbuts[5].x = (cols - l2) / 2;
fbuts[6].x = fbuts[5].x + fbuts[5].len + 1;
fbuts[7].x = fbuts[6].x + fbuts[6].len + 1;
}
}
/* --------------------------------------------------------------------------------------------- */
static void
find_relocate_buttons (const WDialog * h, gboolean all_buttons)
{
size_t i;
find_calc_button_locations (h, all_buttons);
for (i = 0; i < fbuts_num; i++)
fbuts[i].button->x = WIDGET (h)->x + fbuts[i].x;
}
/* --------------------------------------------------------------------------------------------- */
static cb_ret_t
find_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
{
WDialog *h = DIALOG (w);
switch (msg)
{
case MSG_KEY:
if (parm == KEY_F (3) || parm == KEY_F (13))
{
gboolean unparsed_view = (parm == KEY_F (13));
return view_edit_currently_selected_file (unparsed_view, FALSE);
}
if (parm == KEY_F (4))
return view_edit_currently_selected_file (FALSE, TRUE);
return MSG_NOT_HANDLED;
case MSG_RESIZE:
dlg_set_size (h, LINES - 4, COLS - 16);
find_relocate_buttons (h, TRUE);
return MSG_HANDLED;
case MSG_IDLE:
do_search (h);
return MSG_HANDLED;
default:
return dlg_default_callback (w, sender, msg, parm, data);
}
}
/* --------------------------------------------------------------------------------------------- */
/** Handles the Stop/Start button in the find window */
static int
start_stop (WButton * button, int action)
{
Widget *w = WIDGET (button);
(void) action;
running = is_start;
widget_want_idle (WIDGET (find_dlg), running);
is_start = !is_start;
status_update (is_start ? _("Stopped") : _("Searching"));
button_set_text (button, fbuts[is_start ? 3 : 2].text);
find_relocate_buttons (w->owner, FALSE);
dlg_redraw (w->owner);
return 0;
}
/* --------------------------------------------------------------------------------------------- */
/** Handle view command, when invoked as a button */
static int
find_do_view_file (WButton * button, int action)
{
(void) button;
(void) action;
view_edit_currently_selected_file (FALSE, FALSE);
return 0;
}
/* --------------------------------------------------------------------------------------------- */
/** Handle edit command, when invoked as a button */
static int
find_do_edit_file (WButton * button, int action)
{
(void) button;
(void) action;
view_edit_currently_selected_file (FALSE, TRUE);
return 0;
}
/* --------------------------------------------------------------------------------------------- */
static void
setup_gui (void)
{
size_t i;
int lines, cols;
int y;
static gboolean i18n_flag = FALSE;
if (!i18n_flag)
{
for (i = 0; i < fbuts_num; i++)
{
#ifdef ENABLE_NLS
fbuts[i].text = _(fbuts[i].text);
#endif /* ENABLE_NLS */
fbuts[i].len = str_term_width1 (fbuts[i].text) + 3;
if (fbuts[i].flags == DEFPUSH_BUTTON)
fbuts[i].len += 2;
}
i18n_flag = TRUE;
}
lines = LINES - 4;
cols = COLS - 16;
find_dlg =
dlg_create (TRUE, 0, 0, lines, cols, dialog_colors, find_callback, NULL, "[Find File]",
_("Find File"), DLG_CENTER);
find_calc_button_locations (find_dlg, TRUE);
y = 2;
find_list = listbox_new (y, 2, lines - 10, cols - 4, FALSE, NULL);
add_widget_autopos (find_dlg, find_list, WPOS_KEEP_ALL, NULL);
y += WIDGET (find_list)->lines;
add_widget_autopos (find_dlg, hline_new (y++, -1, -1), WPOS_KEEP_BOTTOM, NULL);
found_num_label = label_new (y++, 4, "");
add_widget_autopos (find_dlg, found_num_label, WPOS_KEEP_BOTTOM, NULL);
status_label = label_new (y++, 4, _("Searching"));
add_widget_autopos (find_dlg, status_label, WPOS_KEEP_BOTTOM, NULL);
add_widget_autopos (find_dlg, hline_new (y++, -1, -1), WPOS_KEEP_BOTTOM, NULL);
for (i = 0; i < fbuts_num; i++)
{
if (i == 3)
fbuts[3].button = fbuts[2].button;
else
{
fbuts[i].button =
WIDGET (button_new
(y, fbuts[i].x, fbuts[i].ret_cmd, fbuts[i].flags, fbuts[i].text,
fbuts[i].callback));
add_widget_autopos (find_dlg, fbuts[i].button, WPOS_KEEP_BOTTOM, NULL);
}
if (i == quit_button)
y++;
}
dlg_select_widget (find_list);
}
/* --------------------------------------------------------------------------------------------- */
static int
run_process (void)
{
int ret;
search_content_handle = mc_search_new (content_pattern, -1, NULL);
if (search_content_handle)
{
search_content_handle->search_type =
options.content_regexp ? MC_SEARCH_T_REGEX : MC_SEARCH_T_NORMAL;
search_content_handle->is_case_sensitive = options.content_case_sens;
search_content_handle->whole_words = options.content_whole_words;
#ifdef HAVE_CHARSET
search_content_handle->is_all_charsets = options.content_all_charsets;
#endif
}
search_file_handle = mc_search_new (find_pattern, -1, NULL);
search_file_handle->search_type = options.file_pattern ? MC_SEARCH_T_GLOB : MC_SEARCH_T_REGEX;
search_file_handle->is_case_sensitive = options.file_case_sens;
#ifdef HAVE_CHARSET
search_file_handle->is_all_charsets = options.file_all_charsets;
#endif
search_file_handle->is_entire_line = options.file_pattern;
resuming = FALSE;
widget_want_idle (WIDGET (find_dlg), TRUE);
ret = dlg_run (find_dlg);
mc_search_free (search_file_handle);
search_file_handle = NULL;
mc_search_free (search_content_handle);
search_content_handle = NULL;
return ret;
}
/* --------------------------------------------------------------------------------------------- */
static void
kill_gui (void)
{
widget_want_idle (WIDGET (find_dlg), FALSE);
dlg_destroy (find_dlg);
}
/* --------------------------------------------------------------------------------------------- */
static int
do_find (const char *start_dir, ssize_t start_dir_len, const char *ignore_dirs,
const char *pattern, const char *content, char **dirname, char **filename)
{
int return_value = 0;
char *dir_tmp = NULL, *file_tmp = NULL;
setup_gui ();
/* FIXME: Need to cleanup this, this ought to be passed non-globaly */
find_pattern = (char *) pattern;
content_pattern = NULL;
if (options.content_use && content != NULL && str_is_valid_string (content))
content_pattern = g_strdup (content);
init_find_vars ();
parse_ignore_dirs (ignore_dirs);
push_directory (vfs_path_from_str (start_dir));
return_value = run_process ();
/* Clear variables */
init_find_vars ();
get_list_info (&file_tmp, &dir_tmp);
if (dir_tmp)
*dirname = g_strdup (dir_tmp);
if (file_tmp)
*filename = g_strdup (file_tmp);
if (return_value == B_PANELIZE && *filename)
{
int link_to_dir, stale_link;
int i;
struct stat st;
GList *entry;
dir_list *list = &current_panel->dir;
char *name = NULL;
dir_list_init (list);
for (i = 0, entry = listbox_get_first_link (find_list); entry != NULL;
i++, entry = g_list_next (entry))
{
const char *lc_filename = NULL;
WLEntry *le = LENTRY (entry->data);
char *p;
if ((le->text == NULL) || (le->data == NULL))
continue;
if (content_pattern != NULL)
lc_filename = strchr (le->text + 4, ':') + 1;
else
lc_filename = le->text + 4;
name = mc_build_filename (le->data, lc_filename, (char *) NULL);
/* skip initial start dir */
if (start_dir_len < 0)
p = name;
else
{
p = name + (size_t) start_dir_len;
if (*p == PATH_SEP)
p++;
}
if (!handle_path (p, &st, &link_to_dir, &stale_link))
{
g_free (name);
continue;
}
/* Need to grow the *list? */
if (list->len == list->size && !dir_list_grow (list, DIR_LIST_RESIZE_STEP))
{
g_free (name);
break;
}
/* don't add files more than once to the panel */
if (content_pattern != NULL && list->len != 0
&& strcmp (list->list[list->len - 1].fname, p) == 0)
{
g_free (name);
continue;
}
if (list->len == 0) /* first turn i.e clean old list */
panel_clean_dir (current_panel);
list->list[list->len].fnamelen = strlen (p);
list->list[list->len].fname = g_strndup (p, list->list[list->len].fnamelen);
list->list[list->len].f.marked = 0;
list->list[list->len].f.link_to_dir = link_to_dir;
list->list[list->len].f.stale_link = stale_link;
list->list[list->len].f.dir_size_computed = 0;
list->list[list->len].st = st;
list->list[list->len].sort_key = NULL;
list->list[list->len].second_sort_key = NULL;
list->len++;
g_free (name);
if ((list->len & 15) == 0)
rotate_dash (TRUE);
}
if (list->len != 0)
{
current_panel->is_panelized = TRUE;
/* absolute path */
if (start_dir_len < 0)
{
int ret;
vfs_path_free (current_panel->cwd_vpath);
current_panel->cwd_vpath = vfs_path_from_str (PATH_SEP_STR);
ret = chdir (PATH_SEP_STR);
(void) ret;
}
panelize_save_panel (current_panel);
}
}
g_free (content_pattern);
kill_gui ();
do_search (NULL); /* force do_search to release resources */
g_free (old_dir);
old_dir = NULL;
rotate_dash (FALSE);
return return_value;
}
/* --------------------------------------------------------------------------------------------- */
/*** public functions ****************************************************************************/
/* --------------------------------------------------------------------------------------------- */
void
find_file (void)
{
char *start_dir = NULL, *pattern = NULL, *content = NULL, *ignore_dirs = NULL;
ssize_t start_dir_len;
char *filename = NULL, *dirname = NULL;
while (find_parameters (&start_dir, &start_dir_len, &ignore_dirs, &pattern, &content))
{
int v;
if (pattern[0] == '\0')
break; /* nothing search */
last_refresh.tv_sec = 0;
last_refresh.tv_usec = 0;
dirname = filename = NULL;
is_start = FALSE;
v = do_find (start_dir, start_dir_len, ignore_dirs, pattern, content, &dirname, &filename);
g_free (ignore_dirs);
g_free (pattern);
if (v == B_ENTER)
{
if (dirname != NULL)
{
vfs_path_t *dirname_vpath;
dirname_vpath = vfs_path_from_str (dirname);
do_cd (dirname_vpath, cd_exact);
vfs_path_free (dirname_vpath);
if (filename != NULL)
try_to_select (current_panel,
filename + (content != NULL
? strchr (filename + 4, ':') - filename + 1 : 4));
}
else if (filename != NULL)
{
vfs_path_t *filename_vpath;
filename_vpath = vfs_path_from_str (filename);
do_cd (filename_vpath, cd_exact);
vfs_path_free (filename_vpath);
}
g_free (dirname);
g_free (filename);
break;
}
g_free (content);
g_free (dirname);
g_free (filename);
if (v == B_CANCEL)
break;
if (v == B_PANELIZE)
{
panel_re_sort (current_panel);
try_to_select (current_panel, NULL);
break;
}
}
}
/* --------------------------------------------------------------------------------------------- */