root/src/filemanager/find.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. max
  2. parse_ignore_dirs
  3. find_load_options
  4. find_save_options
  5. add_to_list
  6. stop_idle
  7. status_update
  8. found_num_update
  9. get_list_info
  10. find_check_regexp
  11. find_toggle_enable_ignore_dirs
  12. find_toggle_enable_params
  13. find_toggle_enable_content
  14. find_parm_callback
  15. find_parameters
  16. push_directory
  17. pop_directory
  18. queue_dir_free
  19. clear_stack
  20. insert_file
  21. find_add_match
  22. check_find_events
  23. search_content
  24. find_ignore_dir_search
  25. find_rotate_dash
  26. do_search
  27. init_find_vars
  28. find_do_view_edit
  29. view_edit_currently_selected_file
  30. find_calc_button_locations
  31. find_adjust_header
  32. find_relocate_buttons
  33. find_resize
  34. find_callback
  35. start_stop
  36. find_do_view_file
  37. find_do_edit_file
  38. setup_gui
  39. run_process
  40. kill_gui
  41. do_find
  42. find_cmd

   1 /*
   2    Find file command for the Midnight Commander
   3 
   4    Copyright (C) 1995-2021
   5    Free Software Foundation, Inc.
   6 
   7    Written  by:
   8    Miguel de Icaza, 1995
   9    Slava Zanko <slavazanko@gmail.com>, 2013
  10    Andrew Borodin <aborodin@vmail.ru>, 2013
  11 
  12    This file is part of the Midnight Commander.
  13 
  14    The Midnight Commander is free software: you can redistribute it
  15    and/or modify it under the terms of the GNU General Public License as
  16    published by the Free Software Foundation, either version 3 of the License,
  17    or (at your option) any later version.
  18 
  19    The Midnight Commander is distributed in the hope that it will be useful,
  20    but WITHOUT ANY WARRANTY; without even the implied warranty of
  21    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  22    GNU General Public License for more details.
  23 
  24    You should have received a copy of the GNU General Public License
  25    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  26  */
  27 
  28 /** \file find.c
  29  *  \brief Source: Find file command
  30  */
  31 
  32 #include <config.h>
  33 
  34 #include <ctype.h>
  35 #include <stdio.h>
  36 #include <stdlib.h>
  37 #include <string.h>
  38 #include <sys/stat.h>
  39 
  40 #include "lib/global.h"
  41 
  42 #include "lib/tty/tty.h"
  43 #include "lib/tty/key.h"
  44 #include "lib/skin.h"
  45 #include "lib/search.h"
  46 #include "lib/mcconfig.h"
  47 #include "lib/vfs/vfs.h"
  48 #include "lib/strutil.h"
  49 #include "lib/widget.h"
  50 #include "lib/util.h"           /* canonicalize_pathname() */
  51 
  52 #include "src/setup.h"          /* verbose */
  53 #include "src/history.h"        /* MC_HISTORY_SHARED_SEARCH */
  54 
  55 #include "dir.h"
  56 #include "cmd.h"                /* find_cmd(), view_file_at_line() */
  57 #include "boxes.h"
  58 #include "panelize.h"
  59 
  60 /*** global variables ****************************************************************************/
  61 
  62 /*** file scope macro definitions ****************************************************************/
  63 
  64 #define MAX_REFRESH_INTERVAL (G_USEC_PER_SEC / 20)      /* 50 ms */
  65 #define MIN_REFRESH_FILE_SIZE (256 * 1024)      /* 256 KB */
  66 
  67 /*** file scope type declarations ****************************************************************/
  68 
  69 /* A couple of extra messages we need */
  70 enum
  71 {
  72     B_STOP = B_USER + 1,
  73     B_AGAIN,
  74     B_PANELIZE,
  75     B_TREE,
  76     B_VIEW
  77 };
  78 
  79 typedef enum
  80 {
  81     FIND_CONT = 0,
  82     FIND_SUSPEND,
  83     FIND_ABORT
  84 } FindProgressStatus;
  85 
  86 /* find file options */
  87 typedef struct
  88 {
  89     /* file name options */
  90     gboolean file_case_sens;
  91     gboolean file_pattern;
  92     gboolean find_recurs;
  93     gboolean follow_symlinks;
  94     gboolean skip_hidden;
  95     gboolean file_all_charsets;
  96 
  97     /* file content options */
  98     gboolean content_case_sens;
  99     gboolean content_regexp;
 100     gboolean content_first_hit;
 101     gboolean content_whole_words;
 102     gboolean content_all_charsets;
 103 
 104     /* whether use ignore dirs or not */
 105     gboolean ignore_dirs_enable;
 106     /* list of directories to be ignored, separated by ':' */
 107     char *ignore_dirs;
 108 } find_file_options_t;
 109 
 110 typedef struct
 111 {
 112     char *dir;
 113     gsize start;
 114     gsize end;
 115 } find_match_location_t;
 116 
 117 /*** file scope variables ************************************************************************/
 118 
 119 /* button callbacks */
 120 static int start_stop (WButton * button, int action);
 121 static int find_do_view_file (WButton * button, int action);
 122 static int find_do_edit_file (WButton * button, int action);
 123 
 124 /* Parsed ignore dirs */
 125 static char **find_ignore_dirs = NULL;
 126 
 127 /* static variables to remember find parameters */
 128 static WInput *in_start;        /* Start path */
 129 static WInput *in_name;         /* Filename */
 130 static WInput *in_with;         /* Text */
 131 static WInput *in_ignore;
 132 static WLabel *content_label;   /* 'Content:' label */
 133 static WCheck *file_case_sens_cbox;     /* "case sensitive" checkbox */
 134 static WCheck *file_pattern_cbox;       /* File name is glob or regexp */
 135 static WCheck *recursively_cbox;
 136 static WCheck *follow_sym_cbox;
 137 static WCheck *skip_hidden_cbox;
 138 static WCheck *content_case_sens_cbox;  /* "case sensitive" checkbox */
 139 static WCheck *content_regexp_cbox;     /* "find regular expression" checkbox */
 140 static WCheck *content_first_hit_cbox;  /* "First hit" checkbox" */
 141 static WCheck *content_whole_words_cbox;        /* "whole words" checkbox */
 142 #ifdef HAVE_CHARSET
 143 static WCheck *file_all_charsets_cbox;
 144 static WCheck *content_all_charsets_cbox;
 145 #endif
 146 static WCheck *ignore_dirs_cbox;
 147 
 148 static gboolean running = FALSE;        /* nice flag */
 149 static char *find_pattern = NULL;       /* Pattern to search */
 150 static char *content_pattern = NULL;    /* pattern to search inside files; if
 151                                            content_regexp_flag is true, it contains the
 152                                            regex pattern, else the search string. */
 153 static gboolean content_is_empty = TRUE;        /* remember content field state; initially is empty */
 154 static unsigned long matches;   /* Number of matches */
 155 static gboolean is_start = FALSE;       /* Status of the start/stop toggle button */
 156 static char *old_dir = NULL;
 157 
 158 static gint64 last_refresh;
 159 
 160 /* Where did we stop */
 161 static gboolean resuming;
 162 static int last_line;
 163 static int last_pos;
 164 static off_t last_off;
 165 static int last_i;
 166 
 167 static size_t ignore_count = 0;
 168 
 169 static WDialog *find_dlg;       /* The dialog */
 170 static WLabel *status_label;    /* Finished, Searching etc. */
 171 static WLabel *found_num_label; /* Number of found items */
 172 
 173 /* This keeps track of the directory stack */
 174 static GQueue dir_queue = G_QUEUE_INIT;
 175 
 176 /* *INDENT-OFF* */
 177 static struct
 178 {
 179     int ret_cmd;
 180     button_flags_t flags;
 181     const char *text;
 182     int len;                    /* length including space and brackets */
 183     int x;
 184     Widget *button;
 185     bcback_fn callback;
 186 } fbuts[] =
 187 {
 188     { B_ENTER, DEFPUSH_BUTTON, N_("&Chdir"), 0, 0, NULL, NULL },
 189     { B_AGAIN, NORMAL_BUTTON, N_("&Again"), 0, 0, NULL, NULL },
 190     { B_STOP, NORMAL_BUTTON, N_("S&uspend"), 0, 0, NULL, start_stop },
 191     { B_STOP, NORMAL_BUTTON, N_("Con&tinue"), 0, 0, NULL, NULL },
 192     { B_CANCEL, NORMAL_BUTTON, N_("&Quit"), 0, 0, NULL, NULL },
 193 
 194     { B_PANELIZE, NORMAL_BUTTON, N_("Pane&lize"), 0, 0, NULL, NULL },
 195     { B_VIEW, NORMAL_BUTTON, N_("&View - F3"), 0, 0, NULL, find_do_view_file },
 196     { B_VIEW, NORMAL_BUTTON, N_("&Edit - F4"), 0, 0, NULL, find_do_edit_file }
 197 };
 198 /* *INDENT-ON* */
 199 
 200 static const size_t fbuts_num = G_N_ELEMENTS (fbuts);
 201 static const size_t quit_button = 4;    /* index of "Quit" button */
 202 
 203 static WListbox *find_list;     /* Listbox with the file list */
 204 
 205 static find_file_options_t options = {
 206     TRUE, TRUE, TRUE, FALSE, FALSE, FALSE,
 207     TRUE, FALSE, FALSE, FALSE, FALSE,
 208     FALSE, NULL
 209 };
 210 
 211 static char *in_start_dir = INPUT_LAST_TEXT;
 212 
 213 static mc_search_t *search_file_handle = NULL;
 214 static mc_search_t *search_content_handle = NULL;
 215 
 216 /* --------------------------------------------------------------------------------------------- */
 217 /*** file scope functions ************************************************************************/
 218 /* --------------------------------------------------------------------------------------------- */
 219 
 220 /* don't use max macro to avoid double str_term_width1() call in widget length caclulation */
 221 #undef max
 222 
 223 static int
 224 max (int a, int b)
     /* [previous][next][first][last][top][bottom][index][help]  */
 225 {
 226     return (a > b ? a : b);
 227 }
 228 
 229 /* --------------------------------------------------------------------------------------------- */
 230 
 231 static void
 232 parse_ignore_dirs (const char *ignore_dirs)
     /* [previous][next][first][last][top][bottom][index][help]  */
 233 {
 234     size_t r = 0, w = 0;        /* read and write iterators */
 235 
 236     if (!options.ignore_dirs_enable || ignore_dirs == NULL || ignore_dirs[0] == '\0')
 237         return;
 238 
 239     find_ignore_dirs = g_strsplit (ignore_dirs, ":", -1);
 240 
 241     /* Values like '/foo::/bar: produce holes in list.
 242      * Find and remove them */
 243     for (; find_ignore_dirs[r] != NULL; r++)
 244     {
 245         if (find_ignore_dirs[r][0] == '\0')
 246         {
 247             /* empty entry -- skip it */
 248             MC_PTR_FREE (find_ignore_dirs[r]);
 249             continue;
 250         }
 251 
 252         if (r != w)
 253         {
 254             /* copy entry to the previous free array cell */
 255             find_ignore_dirs[w] = find_ignore_dirs[r];
 256             find_ignore_dirs[r] = NULL;
 257         }
 258 
 259         canonicalize_pathname (find_ignore_dirs[w]);
 260         if (find_ignore_dirs[w][0] != '\0')
 261             w++;
 262         else
 263             MC_PTR_FREE (find_ignore_dirs[w]);
 264     }
 265 
 266     if (find_ignore_dirs[0] == NULL)
 267     {
 268         g_strfreev (find_ignore_dirs);
 269         find_ignore_dirs = NULL;
 270     }
 271 }
 272 
 273 /* --------------------------------------------------------------------------------------------- */
 274 
 275 static void
 276 find_load_options (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 277 {
 278     static gboolean loaded = FALSE;
 279 
 280     if (loaded)
 281         return;
 282 
 283     loaded = TRUE;
 284 
 285     options.file_case_sens =
 286         mc_config_get_bool (mc_global.main_config, "FindFile", "file_case_sens", TRUE);
 287     options.file_pattern =
 288         mc_config_get_bool (mc_global.main_config, "FindFile", "file_shell_pattern", TRUE);
 289     options.find_recurs =
 290         mc_config_get_bool (mc_global.main_config, "FindFile", "file_find_recurs", TRUE);
 291     options.follow_symlinks =
 292         mc_config_get_bool (mc_global.main_config, "FindFile", "follow_symlinks", FALSE);
 293     options.skip_hidden =
 294         mc_config_get_bool (mc_global.main_config, "FindFile", "file_skip_hidden", FALSE);
 295     options.file_all_charsets =
 296         mc_config_get_bool (mc_global.main_config, "FindFile", "file_all_charsets", FALSE);
 297     options.content_case_sens =
 298         mc_config_get_bool (mc_global.main_config, "FindFile", "content_case_sens", TRUE);
 299     options.content_regexp =
 300         mc_config_get_bool (mc_global.main_config, "FindFile", "content_regexp", FALSE);
 301     options.content_first_hit =
 302         mc_config_get_bool (mc_global.main_config, "FindFile", "content_first_hit", FALSE);
 303     options.content_whole_words =
 304         mc_config_get_bool (mc_global.main_config, "FindFile", "content_whole_words", FALSE);
 305     options.content_all_charsets =
 306         mc_config_get_bool (mc_global.main_config, "FindFile", "content_all_charsets", FALSE);
 307     options.ignore_dirs_enable =
 308         mc_config_get_bool (mc_global.main_config, "FindFile", "ignore_dirs_enable", TRUE);
 309     options.ignore_dirs =
 310         mc_config_get_string (mc_global.main_config, "FindFile", "ignore_dirs", "");
 311 
 312     if (options.ignore_dirs[0] == '\0')
 313         MC_PTR_FREE (options.ignore_dirs);
 314 }
 315 
 316 /* --------------------------------------------------------------------------------------------- */
 317 
 318 static void
 319 find_save_options (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 320 {
 321     mc_config_set_bool (mc_global.main_config, "FindFile", "file_case_sens",
 322                         options.file_case_sens);
 323     mc_config_set_bool (mc_global.main_config, "FindFile", "file_shell_pattern",
 324                         options.file_pattern);
 325     mc_config_set_bool (mc_global.main_config, "FindFile", "file_find_recurs", options.find_recurs);
 326     mc_config_set_bool (mc_global.main_config, "FindFile", "follow_symlinks",
 327                         options.follow_symlinks);
 328     mc_config_set_bool (mc_global.main_config, "FindFile", "file_skip_hidden", options.skip_hidden);
 329     mc_config_set_bool (mc_global.main_config, "FindFile", "file_all_charsets",
 330                         options.file_all_charsets);
 331     mc_config_set_bool (mc_global.main_config, "FindFile", "content_case_sens",
 332                         options.content_case_sens);
 333     mc_config_set_bool (mc_global.main_config, "FindFile", "content_regexp",
 334                         options.content_regexp);
 335     mc_config_set_bool (mc_global.main_config, "FindFile", "content_first_hit",
 336                         options.content_first_hit);
 337     mc_config_set_bool (mc_global.main_config, "FindFile", "content_whole_words",
 338                         options.content_whole_words);
 339     mc_config_set_bool (mc_global.main_config, "FindFile", "content_all_charsets",
 340                         options.content_all_charsets);
 341     mc_config_set_bool (mc_global.main_config, "FindFile", "ignore_dirs_enable",
 342                         options.ignore_dirs_enable);
 343     mc_config_set_string (mc_global.main_config, "FindFile", "ignore_dirs", options.ignore_dirs);
 344 }
 345 
 346 /* --------------------------------------------------------------------------------------------- */
 347 
 348 static inline char *
 349 add_to_list (const char *text, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 350 {
 351     return listbox_add_item (find_list, LISTBOX_APPEND_AT_END, 0, text, data, TRUE);
 352 }
 353 
 354 /* --------------------------------------------------------------------------------------------- */
 355 
 356 static inline void
 357 stop_idle (void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 358 {
 359     widget_idle (WIDGET (data), FALSE);
 360 }
 361 
 362 /* --------------------------------------------------------------------------------------------- */
 363 
 364 static inline void
 365 status_update (const char *text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 366 {
 367     label_set_text (status_label, text);
 368 }
 369 
 370 /* --------------------------------------------------------------------------------------------- */
 371 
 372 static inline void
 373 found_num_update (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 374 {
 375     label_set_textv (found_num_label, _("Found: %lu"), matches);
 376 }
 377 
 378 /* --------------------------------------------------------------------------------------------- */
 379 
 380 static void
 381 get_list_info (char **file, char **dir, gsize * start, gsize * end)
     /* [previous][next][first][last][top][bottom][index][help]  */
 382 {
 383     find_match_location_t *location;
 384 
 385     listbox_get_current (find_list, file, (void **) &location);
 386     if (location != NULL)
 387     {
 388         if (dir != NULL)
 389             *dir = location->dir;
 390         if (start != NULL)
 391             *start = location->start;
 392         if (end != NULL)
 393             *end = location->end;
 394     }
 395     else
 396     {
 397         if (dir != NULL)
 398             *dir = NULL;
 399     }
 400 }
 401 
 402 /* --------------------------------------------------------------------------------------------- */
 403 /** check regular expression */
 404 
 405 static gboolean
 406 find_check_regexp (const char *r)
     /* [previous][next][first][last][top][bottom][index][help]  */
 407 {
 408     mc_search_t *search;
 409     gboolean regexp_ok = FALSE;
 410 
 411     search = mc_search_new (r, NULL);
 412 
 413     if (search != NULL)
 414     {
 415         search->search_type = MC_SEARCH_T_REGEX;
 416         regexp_ok = mc_search_prepare (search);
 417         mc_search_free (search);
 418     }
 419 
 420     return regexp_ok;
 421 }
 422 
 423 /* --------------------------------------------------------------------------------------------- */
 424 
 425 static void
 426 find_toggle_enable_ignore_dirs (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 427 {
 428     widget_disable (WIDGET (in_ignore), !ignore_dirs_cbox->state);
 429 }
 430 
 431 /* --------------------------------------------------------------------------------------------- */
 432 
 433 static void
 434 find_toggle_enable_params (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 435 {
 436     gboolean disable = input_is_empty (in_name);
 437 
 438     widget_disable (WIDGET (file_pattern_cbox), disable);
 439     widget_disable (WIDGET (file_case_sens_cbox), disable);
 440 #ifdef HAVE_CHARSET
 441     widget_disable (WIDGET (file_all_charsets_cbox), disable);
 442 #endif
 443 }
 444 
 445 /* --------------------------------------------------------------------------------------------- */
 446 
 447 static void
 448 find_toggle_enable_content (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 449 {
 450     widget_disable (WIDGET (content_regexp_cbox), content_is_empty);
 451     widget_disable (WIDGET (content_case_sens_cbox), content_is_empty);
 452 #ifdef HAVE_CHARSET
 453     widget_disable (WIDGET (content_all_charsets_cbox), content_is_empty);
 454 #endif
 455     widget_disable (WIDGET (content_whole_words_cbox), content_is_empty);
 456     widget_disable (WIDGET (content_first_hit_cbox), content_is_empty);
 457 }
 458 
 459 /* --------------------------------------------------------------------------------------------- */
 460 /**
 461  * Callback for the parameter dialog.
 462  * Validate regex, prevent closing the dialog if it's invalid.
 463  */
 464 
 465 static cb_ret_t
 466 find_parm_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 467 {
 468     /* FIXME: HACK: use first draw of dialog to resolve widget state dependencies.
 469      * Use this time moment to check input field content. We can't do that in MSG_INIT
 470      * because history is not loaded yet.
 471      * Probably, we want new MSG_ACTIVATE message as complement to MSG_VALIDATE one. Or
 472      * we could name it MSG_POST_INIT.
 473      *
 474      * In one or two other places we use MSG_IDLE instead of MSG_DRAW for a similar
 475      * purpose. We should remember to fix those places too when we introduce the new
 476      * message.
 477      */
 478     static gboolean first_draw = TRUE;
 479 
 480     WDialog *h = DIALOG (w);
 481 
 482     switch (msg)
 483     {
 484     case MSG_INIT:
 485         group_default_callback (w, NULL, MSG_INIT, 0, NULL);
 486         first_draw = TRUE;
 487         return MSG_HANDLED;
 488 
 489     case MSG_NOTIFY:
 490         if (sender == WIDGET (ignore_dirs_cbox))
 491         {
 492             find_toggle_enable_ignore_dirs ();
 493             return MSG_HANDLED;
 494         }
 495 
 496         return MSG_NOT_HANDLED;
 497 
 498     case MSG_VALIDATE:
 499         if (h->ret_value != B_ENTER)
 500             return MSG_HANDLED;
 501 
 502         /* check filename regexp */
 503         if (!file_pattern_cbox->state && !input_is_empty (in_name)
 504             && !find_check_regexp (in_name->buffer))
 505         {
 506             /* Don't stop the dialog */
 507             widget_set_state (w, WST_ACTIVE, TRUE);
 508             message (D_ERROR, MSG_ERROR, _("Malformed regular expression"));
 509             widget_select (WIDGET (in_name));
 510             return MSG_HANDLED;
 511         }
 512 
 513         /* check content regexp */
 514         if (content_regexp_cbox->state && !content_is_empty && !find_check_regexp (in_with->buffer))
 515         {
 516             /* Don't stop the dialog */
 517             widget_set_state (w, WST_ACTIVE, TRUE);
 518             message (D_ERROR, MSG_ERROR, _("Malformed regular expression"));
 519             widget_select (WIDGET (in_with));
 520             return MSG_HANDLED;
 521         }
 522 
 523         return MSG_HANDLED;
 524 
 525     case MSG_POST_KEY:
 526         if (GROUP (h)->current->data == in_name)
 527             find_toggle_enable_params ();
 528         else if (GROUP (h)->current->data == in_with)
 529         {
 530             content_is_empty = input_is_empty (in_with);
 531             find_toggle_enable_content ();
 532         }
 533         return MSG_HANDLED;
 534 
 535     case MSG_DRAW:
 536         if (first_draw)
 537         {
 538             find_toggle_enable_ignore_dirs ();
 539             find_toggle_enable_params ();
 540             find_toggle_enable_content ();
 541         }
 542 
 543         first_draw = FALSE;
 544         MC_FALLTHROUGH;         /* to call MSG_DRAW default handler */
 545 
 546     default:
 547         return dlg_default_callback (w, sender, msg, parm, data);
 548     }
 549 }
 550 
 551 /* --------------------------------------------------------------------------------------------- */
 552 /**
 553  * find_parameters: gets information from the user
 554  *
 555  * If the return value is TRUE, then the following holds:
 556  *
 557  * start_dir, ignore_dirs, pattern and content contain the information provided by the user.
 558  * They are newly allocated strings and must be freed when uneeded.
 559  *
 560  * start_dir_len is -1 when user entered an absolute path, otherwise it is a length
 561  * of start_dir (which is absolute). It is used to get a relative pats of find results.
 562  */
 563 
 564 static gboolean
 565 find_parameters (WPanel * panel, char **start_dir, ssize_t * start_dir_len,
     /* [previous][next][first][last][top][bottom][index][help]  */
 566                  char **ignore_dirs, char **pattern, char **content)
 567 {
 568     WGroup *g;
 569 
 570     /* Size of the find parameters window */
 571 #ifdef HAVE_CHARSET
 572     const int lines = 19;
 573 #else
 574     const int lines = 18;
 575 #endif
 576     int cols = 68;
 577 
 578     gboolean return_value;
 579 
 580     /* file name */
 581     const char *file_name_label = N_("File name:");
 582     const char *file_recurs_label = N_("&Find recursively");
 583     const char *file_follow_symlinks = N_("Follow s&ymlinks");
 584     const char *file_pattern_label = N_("&Using shell patterns");
 585 #ifdef HAVE_CHARSET
 586     const char *file_all_charsets_label = N_("&All charsets");
 587 #endif
 588     const char *file_case_label = N_("Cas&e sensitive");
 589     const char *file_skip_hidden_label = N_("S&kip hidden");
 590 
 591     /* file content */
 592     const char *content_content_label = N_("Content:");
 593     const char *content_use_label = N_("Sea&rch for content");
 594     const char *content_regexp_label = N_("Re&gular expression");
 595     const char *content_case_label = N_("Case sens&itive");
 596 #ifdef HAVE_CHARSET
 597     const char *content_all_charsets_label = N_("A&ll charsets");
 598 #endif
 599     const char *content_whole_words_label = N_("&Whole words");
 600     const char *content_first_hit_label = N_("Fir&st hit");
 601 
 602     const char *buts[] = { N_("&Tree"), N_("&OK"), N_("&Cancel") };
 603 
 604     /* button lengths */
 605     int b0, b1, b2, b12;
 606     int y1, y2, x1, x2;
 607     /* column width */
 608     int cw;
 609 
 610 #ifdef ENABLE_NLS
 611     {
 612         size_t i;
 613 
 614         file_name_label = _(file_name_label);
 615         file_recurs_label = _(file_recurs_label);
 616         file_follow_symlinks = _(file_follow_symlinks);
 617         file_pattern_label = _(file_pattern_label);
 618 #ifdef HAVE_CHARSET
 619         file_all_charsets_label = _(file_all_charsets_label);
 620 #endif
 621         file_case_label = _(file_case_label);
 622         file_skip_hidden_label = _(file_skip_hidden_label);
 623 
 624         /* file content */
 625         content_content_label = _(content_content_label);
 626         content_use_label = _(content_use_label);
 627         content_regexp_label = _(content_regexp_label);
 628         content_case_label = _(content_case_label);
 629 #ifdef HAVE_CHARSET
 630         content_all_charsets_label = _(content_all_charsets_label);
 631 #endif
 632         content_whole_words_label = _(content_whole_words_label);
 633         content_first_hit_label = _(content_first_hit_label);
 634 
 635         for (i = 0; i < G_N_ELEMENTS (buts); i++)
 636             buts[i] = _(buts[i]);
 637     }
 638 #endif /* ENABLE_NLS */
 639 
 640     /* caclulate dialog width */
 641 
 642     /* widget widths */
 643     cw = str_term_width1 (file_name_label);
 644     cw = max (cw, str_term_width1 (file_recurs_label) + 4);
 645     cw = max (cw, str_term_width1 (file_follow_symlinks) + 4);
 646     cw = max (cw, str_term_width1 (file_pattern_label) + 4);
 647 #ifdef HAVE_CHARSET
 648     cw = max (cw, str_term_width1 (file_all_charsets_label) + 4);
 649 #endif
 650     cw = max (cw, str_term_width1 (file_case_label) + 4);
 651     cw = max (cw, str_term_width1 (file_skip_hidden_label) + 4);
 652 
 653     cw = max (cw, str_term_width1 (content_content_label) + 4);
 654     cw = max (cw, str_term_width1 (content_use_label) + 4);
 655     cw = max (cw, str_term_width1 (content_regexp_label) + 4);
 656     cw = max (cw, str_term_width1 (content_case_label) + 4);
 657 #ifdef HAVE_CHARSET
 658     cw = max (cw, str_term_width1 (content_all_charsets_label) + 4);
 659 #endif
 660     cw = max (cw, str_term_width1 (content_whole_words_label) + 4);
 661     cw = max (cw, str_term_width1 (content_first_hit_label) + 4);
 662 
 663     /* button width */
 664     b0 = str_term_width1 (buts[0]) + 3;
 665     b1 = str_term_width1 (buts[1]) + 5; /* default button */
 666     b2 = str_term_width1 (buts[2]) + 3;
 667     b12 = b1 + b2 + 1;
 668 
 669     cols = max (cols, max (b12, cw * 2 + 1) + 6);
 670 
 671     find_load_options ();
 672 
 673     if (in_start_dir == NULL)
 674         in_start_dir = g_strdup (".");
 675 
 676     find_dlg =
 677         dlg_create (TRUE, 0, 0, lines, cols, WPOS_CENTER, FALSE, dialog_colors, find_parm_callback,
 678                     NULL, "[Find File]", _("Find File"));
 679     g = GROUP (find_dlg);
 680 
 681     x1 = 3;
 682     x2 = cols / 2 + 1;
 683     cw = (cols - 7) / 2;
 684     y1 = 2;
 685 
 686     group_add_widget (g, label_new (y1++, x1, _("Start at:")));
 687     in_start =
 688         input_new (y1, x1, input_colors, cols - b0 - 7, in_start_dir, "start",
 689                    INPUT_COMPLETE_CD | INPUT_COMPLETE_FILENAMES);
 690     group_add_widget (g, in_start);
 691 
 692     group_add_widget (g, button_new (y1++, cols - b0 - 3, B_TREE, NORMAL_BUTTON, buts[0], NULL));
 693 
 694     ignore_dirs_cbox =
 695         check_new (y1++, x1, options.ignore_dirs_enable, _("Ena&ble ignore directories:"));
 696     group_add_widget (g, ignore_dirs_cbox);
 697 
 698     in_ignore =
 699         input_new (y1++, x1, input_colors, cols - 6,
 700                    options.ignore_dirs != NULL ? options.ignore_dirs : "", "ignoredirs",
 701                    INPUT_COMPLETE_CD | INPUT_COMPLETE_FILENAMES);
 702     group_add_widget (g, in_ignore);
 703 
 704     group_add_widget (g, hline_new (y1++, -1, -1));
 705 
 706     y2 = y1;
 707 
 708     /* Start 1st column */
 709     group_add_widget (g, label_new (y1++, x1, file_name_label));
 710     in_name =
 711         input_new (y1++, x1, input_colors, cw, INPUT_LAST_TEXT, "name",
 712                    INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_CD);
 713     group_add_widget (g, in_name);
 714 
 715     /* Start 2nd column */
 716     content_label = label_new (y2++, x2, content_content_label);
 717     group_add_widget (g, content_label);
 718     in_with =
 719         input_new (y2++, x2, input_colors, cw, content_is_empty ? "" : INPUT_LAST_TEXT,
 720                    MC_HISTORY_SHARED_SEARCH, INPUT_COMPLETE_NONE);
 721     in_with->label = content_label;
 722     group_add_widget (g, in_with);
 723 
 724     /* Continue 1st column */
 725     recursively_cbox = check_new (y1++, x1, options.find_recurs, file_recurs_label);
 726     group_add_widget (g, recursively_cbox);
 727 
 728     follow_sym_cbox = check_new (y1++, x1, options.follow_symlinks, file_follow_symlinks);
 729     group_add_widget (g, follow_sym_cbox);
 730 
 731     file_pattern_cbox = check_new (y1++, x1, options.file_pattern, file_pattern_label);
 732     group_add_widget (g, file_pattern_cbox);
 733 
 734     file_case_sens_cbox = check_new (y1++, x1, options.file_case_sens, file_case_label);
 735     group_add_widget (g, file_case_sens_cbox);
 736 
 737 #ifdef HAVE_CHARSET
 738     file_all_charsets_cbox =
 739         check_new (y1++, x1, options.file_all_charsets, file_all_charsets_label);
 740     group_add_widget (g, file_all_charsets_cbox);
 741 #endif
 742 
 743     skip_hidden_cbox = check_new (y1++, x1, options.skip_hidden, file_skip_hidden_label);
 744     group_add_widget (g, skip_hidden_cbox);
 745 
 746     /* Continue 2nd column */
 747     content_whole_words_cbox =
 748         check_new (y2++, x2, options.content_whole_words, content_whole_words_label);
 749     group_add_widget (g, content_whole_words_cbox);
 750 
 751     content_regexp_cbox = check_new (y2++, x2, options.content_regexp, content_regexp_label);
 752     group_add_widget (g, content_regexp_cbox);
 753 
 754     content_case_sens_cbox = check_new (y2++, x2, options.content_case_sens, content_case_label);
 755     group_add_widget (g, content_case_sens_cbox);
 756 
 757 #ifdef HAVE_CHARSET
 758     content_all_charsets_cbox =
 759         check_new (y2++, x2, options.content_all_charsets, content_all_charsets_label);
 760     group_add_widget (g, content_all_charsets_cbox);
 761 #endif
 762 
 763     content_first_hit_cbox =
 764         check_new (y2++, x2, options.content_first_hit, content_first_hit_label);
 765     group_add_widget (g, content_first_hit_cbox);
 766 
 767     /* buttons */
 768     y1 = max (y1, y2);
 769     x1 = (cols - b12) / 2;
 770     group_add_widget (g, hline_new (y1++, -1, -1));
 771     group_add_widget (g, button_new (y1, x1, B_ENTER, DEFPUSH_BUTTON, buts[1], NULL));
 772     group_add_widget (g, button_new (y1, x1 + b1 + 1, B_CANCEL, NORMAL_BUTTON, buts[2], NULL));
 773 
 774   find_par_start:
 775     widget_select (WIDGET (in_name));
 776 
 777     switch (dlg_run (find_dlg))
 778     {
 779     case B_CANCEL:
 780         return_value = FALSE;
 781         break;
 782 
 783     case B_TREE:
 784         {
 785             char *temp_dir;
 786 
 787             temp_dir = in_start->buffer;
 788             if (*temp_dir == '\0' || DIR_IS_DOT (temp_dir))
 789                 temp_dir = g_strdup (vfs_path_as_str (panel->cwd_vpath));
 790             else
 791                 temp_dir = g_strdup (temp_dir);
 792 
 793             if (in_start_dir != INPUT_LAST_TEXT)
 794                 g_free (in_start_dir);
 795             in_start_dir = tree_box (temp_dir);
 796             if (in_start_dir == NULL)
 797                 in_start_dir = temp_dir;
 798             else
 799                 g_free (temp_dir);
 800 
 801             input_assign_text (in_start, in_start_dir);
 802 
 803             /* Warning: Dreadful goto */
 804             goto find_par_start;
 805         }
 806 
 807     default:
 808         {
 809             char *s;
 810 
 811 #ifdef HAVE_CHARSET
 812             options.file_all_charsets = file_all_charsets_cbox->state;
 813             options.content_all_charsets = content_all_charsets_cbox->state;
 814 #endif
 815             options.content_case_sens = content_case_sens_cbox->state;
 816             options.content_regexp = content_regexp_cbox->state;
 817             options.content_first_hit = content_first_hit_cbox->state;
 818             options.content_whole_words = content_whole_words_cbox->state;
 819             options.find_recurs = recursively_cbox->state;
 820             options.follow_symlinks = follow_sym_cbox->state;
 821             options.file_pattern = file_pattern_cbox->state;
 822             options.file_case_sens = file_case_sens_cbox->state;
 823             options.skip_hidden = skip_hidden_cbox->state;
 824             options.ignore_dirs_enable = ignore_dirs_cbox->state;
 825             g_free (options.ignore_dirs);
 826             options.ignore_dirs = g_strdup (in_ignore->buffer);
 827 
 828             *content = !input_is_empty (in_with) ? g_strdup (in_with->buffer) : NULL;
 829             if (!input_is_empty (in_name))
 830                 *pattern = g_strdup (in_name->buffer);
 831             else
 832                 *pattern = g_strdup (options.file_pattern ? "*" : ".*");
 833             *start_dir = !input_is_empty (in_start) ? in_start->buffer : (char *) ".";
 834             if (in_start_dir != INPUT_LAST_TEXT)
 835                 g_free (in_start_dir);
 836             in_start_dir = g_strdup (*start_dir);
 837 
 838             s = tilde_expand (*start_dir);
 839             canonicalize_pathname (s);
 840 
 841             if (DIR_IS_DOT (s))
 842             {
 843                 *start_dir = g_strdup (vfs_path_as_str (panel->cwd_vpath));
 844                 /* FIXME: is panel->cwd_vpath canonicalized? */
 845                 /* relative paths will be used in panelization */
 846                 *start_dir_len = (ssize_t) strlen (*start_dir);
 847                 g_free (s);
 848             }
 849             else if (g_path_is_absolute (s))
 850             {
 851                 *start_dir = s;
 852                 *start_dir_len = -1;
 853             }
 854             else
 855             {
 856                 /* relative paths will be used in panelization */
 857                 *start_dir =
 858                     mc_build_filename (vfs_path_as_str (panel->cwd_vpath), s, (char *) NULL);
 859                 *start_dir_len = (ssize_t) strlen (vfs_path_as_str (panel->cwd_vpath));
 860                 g_free (s);
 861             }
 862 
 863             if (!options.ignore_dirs_enable || input_is_empty (in_ignore)
 864                 || DIR_IS_DOT (in_ignore->buffer))
 865                 *ignore_dirs = NULL;
 866             else
 867                 *ignore_dirs = g_strdup (in_ignore->buffer);
 868 
 869             find_save_options ();
 870 
 871             return_value = TRUE;
 872         }
 873     }
 874 
 875     dlg_destroy (find_dlg);
 876 
 877     return return_value;
 878 }
 879 
 880 /* --------------------------------------------------------------------------------------------- */
 881 
 882 static inline void
 883 push_directory (vfs_path_t * dir)
     /* [previous][next][first][last][top][bottom][index][help]  */
 884 {
 885     g_queue_push_head (&dir_queue, (void *) dir);
 886 }
 887 
 888 /* --------------------------------------------------------------------------------------------- */
 889 
 890 static inline vfs_path_t *
 891 pop_directory (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 892 {
 893     return (vfs_path_t *) g_queue_pop_head (&dir_queue);
 894 }
 895 
 896 /* --------------------------------------------------------------------------------------------- */
 897 
 898 static void
 899 queue_dir_free (gpointer data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 900 {
 901     vfs_path_free ((vfs_path_t *) data, TRUE);
 902 }
 903 
 904 /* --------------------------------------------------------------------------------------------- */
 905 /** Remove all the items from the stack */
 906 
 907 static void
 908 clear_stack (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 909 {
 910     g_queue_clear_full (&dir_queue, queue_dir_free);
 911 }
 912 
 913 /* --------------------------------------------------------------------------------------------- */
 914 
 915 static void
 916 insert_file (const char *dir, const char *file, gsize start, gsize end)
     /* [previous][next][first][last][top][bottom][index][help]  */
 917 {
 918     char *tmp_name = NULL;
 919     static char *dirname = NULL;
 920     find_match_location_t *location;
 921 
 922     while (IS_PATH_SEP (dir[0]) && IS_PATH_SEP (dir[1]))
 923         dir++;
 924 
 925     if (old_dir != NULL)
 926     {
 927         if (strcmp (old_dir, dir) != 0)
 928         {
 929             g_free (old_dir);
 930             old_dir = g_strdup (dir);
 931             dirname = add_to_list (dir, NULL);
 932         }
 933     }
 934     else
 935     {
 936         old_dir = g_strdup (dir);
 937         dirname = add_to_list (dir, NULL);
 938     }
 939 
 940     tmp_name = g_strdup_printf ("    %s", file);
 941     location = g_malloc (sizeof (*location));
 942     location->dir = dirname;
 943     location->start = start;
 944     location->end = end;
 945     add_to_list (tmp_name, location);
 946     g_free (tmp_name);
 947 }
 948 
 949 /* --------------------------------------------------------------------------------------------- */
 950 
 951 static void
 952 find_add_match (const char *dir, const char *file, gsize start, gsize end)
     /* [previous][next][first][last][top][bottom][index][help]  */
 953 {
 954     insert_file (dir, file, start, end);
 955 
 956     /* Don't scroll */
 957     if (matches == 0)
 958         listbox_select_first (find_list);
 959     widget_draw (WIDGET (find_list));
 960 
 961     matches++;
 962     found_num_update ();
 963 }
 964 
 965 /* --------------------------------------------------------------------------------------------- */
 966 
 967 static FindProgressStatus
 968 check_find_events (WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
 969 {
 970     Gpm_Event event;
 971     int c;
 972 
 973     event.x = -1;
 974     c = tty_get_event (&event, GROUP (h)->mouse_status == MOU_REPEAT, FALSE);
 975     if (c != EV_NONE)
 976     {
 977         dlg_process_event (h, c, &event);
 978         if (h->ret_value == B_ENTER
 979             || h->ret_value == B_CANCEL || h->ret_value == B_AGAIN || h->ret_value == B_PANELIZE)
 980         {
 981             /* dialog terminated */
 982             return FIND_ABORT;
 983         }
 984         if (!widget_get_state (WIDGET (h), WST_IDLE))
 985         {
 986             /* searching suspended */
 987             return FIND_SUSPEND;
 988         }
 989     }
 990 
 991     return FIND_CONT;
 992 }
 993 
 994 /* --------------------------------------------------------------------------------------------- */
 995 /**
 996  * search_content:
 997  *
 998  * Search the content_pattern string in the DIRECTORY/FILE.
 999  * It will add the found entries to the find listbox.
1000  *
1001  * returns FALSE if do_search should look for another file
1002  *         TRUE if do_search should exit and proceed to the event handler
1003  */
1004 
1005 static gboolean
1006 search_content (WDialog * h, const char *directory, const char *filename)
     /* [previous][next][first][last][top][bottom][index][help]  */
1007 {
1008     struct stat s;
1009     char buffer[BUF_4K] = "";   /* raw input buffer */
1010     int file_fd;
1011     gboolean ret_val = FALSE;
1012     vfs_path_t *vpath;
1013     gint64 tv;
1014     gboolean status_updated = FALSE;
1015 
1016     vpath = vfs_path_build_filename (directory, filename, (char *) NULL);
1017 
1018     if (mc_stat (vpath, &s) != 0 || !S_ISREG (s.st_mode))
1019     {
1020         vfs_path_free (vpath, TRUE);
1021         return FALSE;
1022     }
1023 
1024     file_fd = mc_open (vpath, O_RDONLY);
1025     vfs_path_free (vpath, TRUE);
1026 
1027     if (file_fd == -1)
1028         return FALSE;
1029 
1030     /* get time elapsed from last refresh */
1031     tv = g_get_real_time ();
1032 
1033     if (s.st_size >= MIN_REFRESH_FILE_SIZE || (tv - last_refresh) > MAX_REFRESH_INTERVAL)
1034     {
1035         g_snprintf (buffer, sizeof (buffer), _("Grepping in %s"), filename);
1036         status_update (str_trunc (buffer, WIDGET (h)->cols - 8));
1037         mc_refresh ();
1038         last_refresh = tv;
1039         status_updated = TRUE;
1040     }
1041 
1042     tty_enable_interrupt_key ();
1043     tty_got_interrupt ();
1044 
1045     {
1046         int line = 1;
1047         int pos = 0;
1048         int n_read = 0;
1049         off_t off = 0;          /* file_fd's offset corresponding to strbuf[0] */
1050         gboolean found = FALSE;
1051         gsize found_len;
1052         gsize found_start;
1053         char result[BUF_MEDIUM];
1054         char *strbuf = NULL;    /* buffer for fetched string */
1055         int strbuf_size = 0;
1056         int i = -1;             /* compensate for a newline we'll add when we first enter the loop */
1057 
1058         if (resuming)
1059         {
1060             /* We've been previously suspended, start from the previous position */
1061             resuming = FALSE;
1062             line = last_line;
1063             pos = last_pos;
1064             off = last_off;
1065             i = last_i;
1066         }
1067 
1068         while (!ret_val)
1069         {
1070             char ch = '\0';
1071 
1072             off += i + 1;       /* the previous line, plus a newline character */
1073             i = 0;
1074 
1075             /* read to buffer and get line from there */
1076             while (TRUE)
1077             {
1078                 if (pos >= n_read)
1079                 {
1080                     pos = 0;
1081                     n_read = mc_read (file_fd, buffer, sizeof (buffer));
1082                     if (n_read <= 0)
1083                         break;
1084                 }
1085 
1086                 ch = buffer[pos++];
1087                 if (ch == '\0')
1088                 {
1089                     /* skip possible leading zero(s) */
1090                     if (i == 0)
1091                     {
1092                         off++;
1093                         continue;
1094                     }
1095                     break;
1096                 }
1097 
1098                 if (i >= strbuf_size - 1)
1099                 {
1100                     strbuf_size += 128;
1101                     strbuf = g_realloc (strbuf, strbuf_size);
1102                 }
1103 
1104                 /* Strip newline */
1105                 if (ch == '\n')
1106                     break;
1107 
1108                 strbuf[i++] = ch;
1109             }
1110 
1111             if (i == 0)
1112             {
1113                 if (ch == '\0')
1114                     break;
1115 
1116                 /* if (ch == '\n'): do not search in empty strings */
1117                 goto skip_search;
1118             }
1119 
1120             strbuf[i] = '\0';
1121 
1122             if (!found          /* Search in binary line once */
1123                 && mc_search_run (search_content_handle, (const void *) strbuf, 0, i, &found_len))
1124             {
1125                 if (!status_updated)
1126                 {
1127                     /* if we add results for a file, we have to ensure that
1128                        name of this file is shown in status bar */
1129                     g_snprintf (result, sizeof (result), _("Grepping in %s"), filename);
1130                     status_update (str_trunc (result, WIDGET (h)->cols - 8));
1131                     mc_refresh ();
1132                     last_refresh = tv;
1133                     status_updated = TRUE;
1134                 }
1135 
1136                 g_snprintf (result, sizeof (result), "%d:%s", line, filename);
1137                 found_start = off + search_content_handle->normal_offset + 1;   /* off by one: ticket 3280 */
1138                 find_add_match (directory, result, found_start, found_start + found_len);
1139                 found = TRUE;
1140             }
1141 
1142             if (found && options.content_first_hit)
1143                 break;
1144 
1145             if (ch == '\n')
1146             {
1147               skip_search:
1148                 found = FALSE;
1149                 line++;
1150             }
1151 
1152             if ((line & 0xff) == 0)
1153             {
1154                 FindProgressStatus res;
1155 
1156                 res = check_find_events (h);
1157                 switch (res)
1158                 {
1159                 case FIND_ABORT:
1160                     stop_idle (h);
1161                     ret_val = TRUE;
1162                     break;
1163                 case FIND_SUSPEND:
1164                     resuming = TRUE;
1165                     last_line = line;
1166                     last_pos = pos;
1167                     last_off = off;
1168                     last_i = i;
1169                     ret_val = TRUE;
1170                     break;
1171                 default:
1172                     break;
1173                 }
1174             }
1175         }
1176 
1177         g_free (strbuf);
1178     }
1179 
1180     tty_disable_interrupt_key ();
1181     mc_close (file_fd);
1182     return ret_val;
1183 }
1184 
1185 /* --------------------------------------------------------------------------------------------- */
1186 
1187 /**
1188   If dir is absolute, this means we're within dir and searching file here.
1189   If dir is relative, this means we're going to add dir to the directory stack.
1190 **/
1191 static gboolean
1192 find_ignore_dir_search (const char *dir)
     /* [previous][next][first][last][top][bottom][index][help]  */
1193 {
1194     if (find_ignore_dirs != NULL)
1195     {
1196         const size_t dlen = strlen (dir);
1197         const unsigned char dabs = g_path_is_absolute (dir) ? 1 : 0;
1198 
1199         char **ignore_dir;
1200 
1201         for (ignore_dir = find_ignore_dirs; *ignore_dir != NULL; ignore_dir++)
1202         {
1203             const size_t ilen = strlen (*ignore_dir);
1204             const unsigned char iabs = g_path_is_absolute (*ignore_dir) ? 2 : 0;
1205 
1206             /* ignore dir is too long -- skip it */
1207             if (dlen < ilen)
1208                 continue;
1209 
1210             /* handle absolute and relative paths */
1211             switch (iabs | dabs)
1212             {
1213             case 0:            /* both paths are relative */
1214             case 3:            /* both paths are abolute */
1215                 /* if ignore dir is not a path  of dir -- skip it */
1216                 if (strncmp (dir, *ignore_dir, ilen) == 0)
1217                 {
1218                     /* be sure that ignore dir is not a part of dir like:
1219                        ignore dir is "h", dir is "home" */
1220                     if (dir[ilen] == '\0' || IS_PATH_SEP (dir[ilen]))
1221                         return TRUE;
1222                 }
1223                 break;
1224             case 1:            /* dir is absolute, ignore_dir is relative */
1225                 {
1226                     char *d;
1227 
1228                     d = strstr (dir, *ignore_dir);
1229                     if (d != NULL && IS_PATH_SEP (d[-1])
1230                         && (d[ilen] == '\0' || IS_PATH_SEP (d[ilen])))
1231                         return TRUE;
1232                 }
1233                 break;
1234             case 2:            /* dir is relative, ignore_dir is absolute */
1235                 /* FIXME: skip this case */
1236                 break;
1237             default:           /* this cannot occurs */
1238                 return FALSE;
1239             }
1240         }
1241     }
1242 
1243     return FALSE;
1244 }
1245 
1246 /* --------------------------------------------------------------------------------------------- */
1247 
1248 static void
1249 find_rotate_dash (const WDialog * h, gboolean show)
     /* [previous][next][first][last][top][bottom][index][help]  */
1250 {
1251     static size_t pos = 0;
1252     static const char rotating_dash[4] = "|/-\\";
1253     const Widget *w = CONST_WIDGET (h);
1254     const int *colors;
1255 
1256     colors = widget_get_colors (w);
1257     tty_setcolor (colors[DLG_COLOR_NORMAL]);
1258     widget_gotoyx (h, w->lines - 7, w->cols - 4);
1259     tty_print_char (show ? rotating_dash[pos] : ' ');
1260     pos = (pos + 1) % sizeof (rotating_dash);
1261     mc_refresh ();
1262 }
1263 
1264 /* --------------------------------------------------------------------------------------------- */
1265 
1266 static int
1267 do_search (WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
1268 {
1269     static struct vfs_dirent *dp = NULL;
1270     static DIR *dirp = NULL;
1271     static char *directory = NULL;
1272     struct stat tmp_stat;
1273     gsize bytes_found;
1274     unsigned short count;
1275 
1276     if (h == NULL)
1277     {                           /* someone forces me to close dirp */
1278         if (dirp != NULL)
1279         {
1280             mc_closedir (dirp);
1281             dirp = NULL;
1282         }
1283         MC_PTR_FREE (directory);
1284         dp = NULL;
1285         return 1;
1286     }
1287 
1288     for (count = 0; count < 32; count++)
1289     {
1290         while (dp == NULL)
1291         {
1292             if (dirp != NULL)
1293             {
1294                 mc_closedir (dirp);
1295                 dirp = NULL;
1296             }
1297 
1298             while (dirp == NULL)
1299             {
1300                 vfs_path_t *tmp_vpath = NULL;
1301 
1302                 tty_setcolor (REVERSE_COLOR);
1303 
1304                 while (TRUE)
1305                 {
1306                     tmp_vpath = pop_directory ();
1307                     if (tmp_vpath == NULL)
1308                     {
1309                         running = FALSE;
1310                         if (ignore_count == 0)
1311                             status_update (_("Finished"));
1312                         else
1313                         {
1314                             char msg[BUF_SMALL];
1315 
1316                             g_snprintf (msg, sizeof (msg),
1317                                         ngettext ("Finished (ignored %zu directory)",
1318                                                   "Finished (ignored %zu directories)",
1319                                                   ignore_count), ignore_count);
1320                             status_update (msg);
1321                         }
1322                         if (verbose)
1323                             find_rotate_dash (h, FALSE);
1324                         stop_idle (h);
1325                         return 0;
1326                     }
1327 
1328                     /* handle absolute ignore dirs here */
1329                     {
1330                         gboolean ok;
1331 
1332                         ok = find_ignore_dir_search (vfs_path_as_str (tmp_vpath));
1333                         if (!ok)
1334                             break;
1335                     }
1336 
1337                     vfs_path_free (tmp_vpath, TRUE);
1338                     ignore_count++;
1339                 }
1340 
1341                 g_free (directory);
1342                 directory = g_strdup (vfs_path_as_str (tmp_vpath));
1343 
1344                 if (verbose)
1345                 {
1346                     char buffer[BUF_MEDIUM];
1347 
1348                     g_snprintf (buffer, sizeof (buffer), _("Searching %s"), directory);
1349                     status_update (str_trunc (directory, WIDGET (h)->cols - 8));
1350                 }
1351 
1352                 dirp = mc_opendir (tmp_vpath);
1353                 vfs_path_free (tmp_vpath, TRUE);
1354             }                   /* while (!dirp) */
1355 
1356             /* skip invalid filenames */
1357             while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1358                 ;
1359         }                       /* while (!dp) */
1360 
1361         if (DIR_IS_DOT (dp->d_name) || DIR_IS_DOTDOT (dp->d_name))
1362         {
1363             /* skip invalid filenames */
1364             while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1365                 ;
1366 
1367             return 1;
1368         }
1369 
1370         if (!(options.skip_hidden && (dp->d_name[0] == '.')))
1371         {
1372             gboolean search_ok;
1373 
1374             if (options.find_recurs && (directory != NULL))
1375             {                   /* Can directory be NULL ? */
1376                 /* handle relative ignore dirs here */
1377                 if (options.ignore_dirs_enable && find_ignore_dir_search (dp->d_name))
1378                     ignore_count++;
1379                 else
1380                 {
1381                     vfs_path_t *tmp_vpath;
1382                     int stat_res;
1383 
1384                     tmp_vpath = vfs_path_build_filename (directory, dp->d_name, (char *) NULL);
1385 
1386                     if (options.follow_symlinks)
1387                         stat_res = mc_stat (tmp_vpath, &tmp_stat);
1388                     else
1389                         stat_res = mc_lstat (tmp_vpath, &tmp_stat);
1390 
1391                     if (stat_res == 0 && S_ISDIR (tmp_stat.st_mode))
1392                         push_directory (tmp_vpath);
1393                     else
1394                         vfs_path_free (tmp_vpath, TRUE);
1395                 }
1396             }
1397 
1398             search_ok = mc_search_run (search_file_handle, dp->d_name,
1399                                        0, strlen (dp->d_name), &bytes_found);
1400 
1401             if (search_ok)
1402             {
1403                 if (content_pattern == NULL)
1404                     find_add_match (directory, dp->d_name, 0, 0);
1405                 else if (search_content (h, directory, dp->d_name))
1406                     return 1;
1407             }
1408         }
1409 
1410         /* skip invalid filenames */
1411         while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1412             ;
1413     }                           /* for */
1414 
1415     if (verbose)
1416         find_rotate_dash (h, TRUE);
1417 
1418     return 1;
1419 }
1420 
1421 /* --------------------------------------------------------------------------------------------- */
1422 
1423 static void
1424 init_find_vars (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1425 {
1426     MC_PTR_FREE (old_dir);
1427     matches = 0;
1428     ignore_count = 0;
1429 
1430     /* Remove all the items from the stack */
1431     clear_stack ();
1432 
1433     g_strfreev (find_ignore_dirs);
1434     find_ignore_dirs = NULL;
1435 }
1436 
1437 /* --------------------------------------------------------------------------------------------- */
1438 
1439 static void
1440 find_do_view_edit (gboolean unparsed_view, gboolean edit, char *dir, char *file, off_t search_start,
     /* [previous][next][first][last][top][bottom][index][help]  */
1441                    off_t search_end)
1442 {
1443     const char *filename = NULL;
1444     int line;
1445     vfs_path_t *fullname_vpath;
1446 
1447     if (content_pattern != NULL)
1448     {
1449         filename = strchr (file + 4, ':') + 1;
1450         line = atoi (file + 4);
1451     }
1452     else
1453     {
1454         filename = file + 4;
1455         line = 0;
1456     }
1457 
1458     fullname_vpath = vfs_path_build_filename (dir, filename, (char *) NULL);
1459     if (edit)
1460         edit_file_at_line (fullname_vpath, use_internal_edit, line);
1461     else
1462         view_file_at_line (fullname_vpath, unparsed_view, use_internal_view, line, search_start,
1463                            search_end);
1464     vfs_path_free (fullname_vpath, TRUE);
1465 }
1466 
1467 /* --------------------------------------------------------------------------------------------- */
1468 
1469 static cb_ret_t
1470 view_edit_currently_selected_file (gboolean unparsed_view, gboolean edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1471 {
1472     char *text = NULL;
1473     find_match_location_t *location;
1474 
1475     listbox_get_current (find_list, &text, (void **) &location);
1476 
1477     if ((text == NULL) || (location == NULL) || (location->dir == NULL))
1478         return MSG_NOT_HANDLED;
1479 
1480     find_do_view_edit (unparsed_view, edit, location->dir, text, location->start, location->end);
1481     return MSG_HANDLED;
1482 }
1483 
1484 /* --------------------------------------------------------------------------------------------- */
1485 
1486 static void
1487 find_calc_button_locations (const WDialog * h, gboolean all_buttons)
     /* [previous][next][first][last][top][bottom][index][help]  */
1488 {
1489     const int cols = CONST_WIDGET (h)->cols;
1490 
1491     int l1, l2;
1492 
1493     l1 = fbuts[0].len + fbuts[1].len + fbuts[is_start ? 3 : 2].len + fbuts[4].len + 3;
1494     l2 = fbuts[5].len + fbuts[6].len + fbuts[7].len + 2;
1495 
1496     fbuts[0].x = (cols - l1) / 2;
1497     fbuts[1].x = fbuts[0].x + fbuts[0].len + 1;
1498     fbuts[2].x = fbuts[1].x + fbuts[1].len + 1;
1499     fbuts[3].x = fbuts[2].x;
1500     fbuts[4].x = fbuts[2].x + fbuts[is_start ? 3 : 2].len + 1;
1501 
1502     if (all_buttons)
1503     {
1504         fbuts[5].x = (cols - l2) / 2;
1505         fbuts[6].x = fbuts[5].x + fbuts[5].len + 1;
1506         fbuts[7].x = fbuts[6].x + fbuts[6].len + 1;
1507     }
1508 }
1509 
1510 /* --------------------------------------------------------------------------------------------- */
1511 
1512 static void
1513 find_adjust_header (WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
1514 {
1515     char title[BUF_MEDIUM];
1516     int title_len;
1517 
1518     if (content_pattern != NULL)
1519         g_snprintf (title, sizeof (title), _("Find File: \"%s\". Content: \"%s\""), find_pattern,
1520                     content_pattern);
1521     else
1522         g_snprintf (title, sizeof (title), _("Find File: \"%s\""), find_pattern);
1523 
1524     title_len = str_term_width1 (title);
1525     if (title_len > WIDGET (h)->cols - 6)
1526     {
1527         /* title is too wide, truncate it */
1528         title_len = WIDGET (h)->cols - 6;
1529         title_len = str_column_to_pos (title, title_len);
1530         title_len -= 3;         /* reserve space for three dots */
1531         title_len = str_offset_to_pos (title, title_len);
1532         /* mark that title is truncated */
1533         memmove (title + title_len, "...", 4);
1534     }
1535 
1536     frame_set_title (FRAME (h->bg), title);
1537 }
1538 
1539 /* --------------------------------------------------------------------------------------------- */
1540 
1541 static void
1542 find_relocate_buttons (const WDialog * h, gboolean all_buttons)
     /* [previous][next][first][last][top][bottom][index][help]  */
1543 {
1544     size_t i;
1545 
1546     find_calc_button_locations (h, all_buttons);
1547 
1548     for (i = 0; i < fbuts_num; i++)
1549         fbuts[i].button->x = CONST_WIDGET (h)->x + fbuts[i].x;
1550 }
1551 
1552 /* --------------------------------------------------------------------------------------------- */
1553 
1554 static cb_ret_t
1555 find_resize (WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
1556 {
1557     Widget *w = WIDGET (h);
1558     WRect r;
1559 
1560     rect_init (&r, w->y, w->x, LINES - 4, COLS - 16);
1561     dlg_default_callback (w, NULL, MSG_RESIZE, 0, &r);
1562     find_adjust_header (h);
1563     find_relocate_buttons (h, TRUE);
1564 
1565     return MSG_HANDLED;
1566 }
1567 
1568 /* --------------------------------------------------------------------------------------------- */
1569 
1570 static cb_ret_t
1571 find_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
1572 {
1573     WDialog *h = DIALOG (w);
1574 
1575     switch (msg)
1576     {
1577     case MSG_INIT:
1578         group_default_callback (w, NULL, MSG_INIT, 0, NULL);
1579         find_adjust_header (h);
1580         return MSG_HANDLED;
1581 
1582     case MSG_KEY:
1583         if (parm == KEY_F (3) || parm == KEY_F (13))
1584         {
1585             gboolean unparsed_view = (parm == KEY_F (13));
1586 
1587             return view_edit_currently_selected_file (unparsed_view, FALSE);
1588         }
1589         if (parm == KEY_F (4))
1590             return view_edit_currently_selected_file (FALSE, TRUE);
1591         return MSG_NOT_HANDLED;
1592 
1593     case MSG_RESIZE:
1594         return find_resize (h);
1595 
1596     case MSG_IDLE:
1597         do_search (h);
1598         return MSG_HANDLED;
1599 
1600     default:
1601         return dlg_default_callback (w, sender, msg, parm, data);
1602     }
1603 }
1604 
1605 /* --------------------------------------------------------------------------------------------- */
1606 /** Handles the Stop/Start button in the find window */
1607 
1608 static int
1609 start_stop (WButton * button, int action)
     /* [previous][next][first][last][top][bottom][index][help]  */
1610 {
1611     Widget *w = WIDGET (button);
1612 
1613     (void) action;
1614 
1615     running = is_start;
1616     widget_idle (WIDGET (find_dlg), running);
1617     is_start = !is_start;
1618 
1619     status_update (is_start ? _("Stopped") : _("Searching"));
1620     button_set_text (button, fbuts[is_start ? 3 : 2].text);
1621 
1622     find_relocate_buttons (DIALOG (w->owner), FALSE);
1623     widget_draw (WIDGET (w->owner));
1624 
1625     return 0;
1626 }
1627 
1628 /* --------------------------------------------------------------------------------------------- */
1629 /** Handle view command, when invoked as a button */
1630 
1631 static int
1632 find_do_view_file (WButton * button, int action)
     /* [previous][next][first][last][top][bottom][index][help]  */
1633 {
1634     (void) button;
1635     (void) action;
1636 
1637     view_edit_currently_selected_file (FALSE, FALSE);
1638     return 0;
1639 }
1640 
1641 /* --------------------------------------------------------------------------------------------- */
1642 /** Handle edit command, when invoked as a button */
1643 
1644 static int
1645 find_do_edit_file (WButton * button, int action)
     /* [previous][next][first][last][top][bottom][index][help]  */
1646 {
1647     (void) button;
1648     (void) action;
1649 
1650     view_edit_currently_selected_file (FALSE, TRUE);
1651     return 0;
1652 }
1653 
1654 /* --------------------------------------------------------------------------------------------- */
1655 
1656 static void
1657 setup_gui (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1658 {
1659     WGroup *g;
1660     size_t i;
1661     int lines, cols;
1662     int y;
1663 
1664     static gboolean i18n_flag = FALSE;
1665 
1666     if (!i18n_flag)
1667     {
1668         for (i = 0; i < fbuts_num; i++)
1669         {
1670 #ifdef ENABLE_NLS
1671             fbuts[i].text = _(fbuts[i].text);
1672 #endif /* ENABLE_NLS */
1673             fbuts[i].len = str_term_width1 (fbuts[i].text) + 3;
1674             if (fbuts[i].flags == DEFPUSH_BUTTON)
1675                 fbuts[i].len += 2;
1676         }
1677 
1678         i18n_flag = TRUE;
1679     }
1680 
1681     lines = LINES - 4;
1682     cols = COLS - 16;
1683 
1684     find_dlg =
1685         dlg_create (TRUE, 0, 0, lines, cols, WPOS_CENTER, FALSE, dialog_colors, find_callback, NULL,
1686                     "[Find File]", NULL);
1687     g = GROUP (find_dlg);
1688 
1689     find_calc_button_locations (find_dlg, TRUE);
1690 
1691     y = 2;
1692     find_list = listbox_new (y, 2, lines - 10, cols - 4, FALSE, NULL);
1693     group_add_widget_autopos (g, find_list, WPOS_KEEP_ALL, NULL);
1694     y += WIDGET (find_list)->lines;
1695 
1696     group_add_widget_autopos (g, hline_new (y++, -1, -1), WPOS_KEEP_BOTTOM, NULL);
1697 
1698     found_num_label = label_new (y++, 4, "");
1699     group_add_widget_autopos (g, found_num_label, WPOS_KEEP_BOTTOM, NULL);
1700 
1701     status_label = label_new (y++, 4, _("Searching"));
1702     group_add_widget_autopos (g, status_label, WPOS_KEEP_BOTTOM, NULL);
1703 
1704     group_add_widget_autopos (g, hline_new (y++, -1, -1), WPOS_KEEP_BOTTOM, NULL);
1705 
1706     for (i = 0; i < fbuts_num; i++)
1707     {
1708         if (i == 3)
1709             fbuts[3].button = fbuts[2].button;
1710         else
1711         {
1712             fbuts[i].button =
1713                 WIDGET (button_new
1714                         (y, fbuts[i].x, fbuts[i].ret_cmd, fbuts[i].flags, fbuts[i].text,
1715                          fbuts[i].callback));
1716             group_add_widget_autopos (g, fbuts[i].button, WPOS_KEEP_BOTTOM, NULL);
1717         }
1718 
1719         if (i == quit_button)
1720             y++;
1721     }
1722 
1723     widget_select (WIDGET (find_list));
1724 }
1725 
1726 /* --------------------------------------------------------------------------------------------- */
1727 
1728 static int
1729 run_process (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1730 {
1731     int ret;
1732 
1733     search_content_handle = mc_search_new (content_pattern, NULL);
1734     if (search_content_handle)
1735     {
1736         search_content_handle->search_type =
1737             options.content_regexp ? MC_SEARCH_T_REGEX : MC_SEARCH_T_NORMAL;
1738         search_content_handle->is_case_sensitive = options.content_case_sens;
1739         search_content_handle->whole_words = options.content_whole_words;
1740 #ifdef HAVE_CHARSET
1741         search_content_handle->is_all_charsets = options.content_all_charsets;
1742 #endif
1743     }
1744     search_file_handle = mc_search_new (find_pattern, NULL);
1745     search_file_handle->search_type = options.file_pattern ? MC_SEARCH_T_GLOB : MC_SEARCH_T_REGEX;
1746     search_file_handle->is_case_sensitive = options.file_case_sens;
1747 #ifdef HAVE_CHARSET
1748     search_file_handle->is_all_charsets = options.file_all_charsets;
1749 #endif
1750     search_file_handle->is_entire_line = options.file_pattern;
1751 
1752     resuming = FALSE;
1753 
1754     widget_idle (WIDGET (find_dlg), TRUE);
1755     ret = dlg_run (find_dlg);
1756 
1757     mc_search_free (search_file_handle);
1758     search_file_handle = NULL;
1759     mc_search_free (search_content_handle);
1760     search_content_handle = NULL;
1761 
1762     return ret;
1763 }
1764 
1765 /* --------------------------------------------------------------------------------------------- */
1766 
1767 static void
1768 kill_gui (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1769 {
1770     widget_idle (WIDGET (find_dlg), FALSE);
1771     dlg_destroy (find_dlg);
1772 }
1773 
1774 /* --------------------------------------------------------------------------------------------- */
1775 
1776 static int
1777 do_find (WPanel * panel, const char *start_dir, ssize_t start_dir_len, const char *ignore_dirs,
     /* [previous][next][first][last][top][bottom][index][help]  */
1778          char **dirname, char **filename)
1779 {
1780     int return_value = 0;
1781     char *dir_tmp = NULL, *file_tmp = NULL;
1782 
1783     setup_gui ();
1784 
1785     init_find_vars ();
1786     parse_ignore_dirs (ignore_dirs);
1787     push_directory (vfs_path_from_str (start_dir));
1788 
1789     return_value = run_process ();
1790 
1791     /* Clear variables */
1792     init_find_vars ();
1793 
1794     get_list_info (&file_tmp, &dir_tmp, NULL, NULL);
1795 
1796     if (dir_tmp)
1797         *dirname = g_strdup (dir_tmp);
1798     if (file_tmp)
1799         *filename = g_strdup (file_tmp);
1800 
1801     if (return_value == B_PANELIZE && *filename)
1802     {
1803         int i;
1804         struct stat st;
1805         GList *entry;
1806         dir_list *list = &panel->dir;
1807         char *name = NULL;
1808 
1809         panel_clean_dir (panel);
1810         dir_list_init (list);
1811 
1812         for (i = 0, entry = listbox_get_first_link (find_list); entry != NULL;
1813              i++, entry = g_list_next (entry))
1814         {
1815             const char *lc_filename = NULL;
1816             WLEntry *le = LENTRY (entry->data);
1817             find_match_location_t *location = le->data;
1818             char *p;
1819             gboolean link_to_dir, stale_link;
1820 
1821             if ((le->text == NULL) || (location == NULL) || (location->dir == NULL))
1822                 continue;
1823 
1824             if (!content_is_empty)
1825                 lc_filename = strchr (le->text + 4, ':') + 1;
1826             else
1827                 lc_filename = le->text + 4;
1828 
1829             name = mc_build_filename (location->dir, lc_filename, (char *) NULL);
1830             /* skip initial start dir */
1831             if (start_dir_len < 0)
1832                 p = name;
1833             else
1834             {
1835                 p = name + (size_t) start_dir_len;
1836                 if (IS_PATH_SEP (*p))
1837                     p++;
1838             }
1839 
1840             if (!handle_path (p, &st, &link_to_dir, &stale_link))
1841             {
1842                 g_free (name);
1843                 continue;
1844             }
1845             /* Need to grow the *list? */
1846             if (list->len == list->size && !dir_list_grow (list, DIR_LIST_RESIZE_STEP))
1847             {
1848                 g_free (name);
1849                 break;
1850             }
1851 
1852             /* don't add files more than once to the panel */
1853             if (!content_is_empty && list->len != 0
1854                 && strcmp (list->list[list->len - 1].fname->str, p) == 0)
1855             {
1856                 g_free (name);
1857                 continue;
1858             }
1859 
1860             list->list[list->len].fname = g_string_new (p);
1861             list->list[list->len].f.marked = 0;
1862             list->list[list->len].f.link_to_dir = link_to_dir ? 1 : 0;
1863             list->list[list->len].f.stale_link = stale_link ? 1 : 0;
1864             list->list[list->len].f.dir_size_computed = 0;
1865             list->list[list->len].st = st;
1866             list->list[list->len].sort_key = NULL;
1867             list->list[list->len].second_sort_key = NULL;
1868             list->len++;
1869             g_free (name);
1870             if ((list->len & 15) == 0)
1871                 rotate_dash (TRUE);
1872         }
1873 
1874         panel->is_panelized = TRUE;
1875         panelize_absolutize_if_needed (panel);
1876         panelize_save_panel (panel);
1877     }
1878 
1879     kill_gui ();
1880     do_search (NULL);           /* force do_search to release resources */
1881     MC_PTR_FREE (old_dir);
1882     rotate_dash (FALSE);
1883 
1884     return return_value;
1885 }
1886 
1887 /* --------------------------------------------------------------------------------------------- */
1888 /*** public functions ****************************************************************************/
1889 /* --------------------------------------------------------------------------------------------- */
1890 
1891 void
1892 find_cmd (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
1893 {
1894     char *start_dir = NULL, *ignore_dirs = NULL;
1895     ssize_t start_dir_len;
1896 
1897     find_pattern = NULL;
1898     content_pattern = NULL;
1899 
1900     while (find_parameters (panel, &start_dir, &start_dir_len,
1901                             &ignore_dirs, &find_pattern, &content_pattern))
1902     {
1903         char *filename = NULL, *dirname = NULL;
1904         int v = B_CANCEL;
1905 
1906         content_is_empty = content_pattern == NULL;
1907 
1908         if (find_pattern[0] != '\0')
1909         {
1910             last_refresh = 0;
1911 
1912             is_start = FALSE;
1913 
1914             if (!content_is_empty && !str_is_valid_string (content_pattern))
1915                 MC_PTR_FREE (content_pattern);
1916 
1917             v = do_find (panel, start_dir, start_dir_len, ignore_dirs, &dirname, &filename);
1918         }
1919 
1920         g_free (start_dir);
1921         g_free (ignore_dirs);
1922         MC_PTR_FREE (find_pattern);
1923 
1924         if (v == B_ENTER)
1925         {
1926             if (dirname != NULL)
1927             {
1928                 vfs_path_t *dirname_vpath;
1929 
1930                 dirname_vpath = vfs_path_from_str (dirname);
1931                 panel_cd (panel, dirname_vpath, cd_exact);
1932                 vfs_path_free (dirname_vpath, TRUE);
1933                 if (filename != NULL)
1934                     try_to_select (panel,
1935                                    filename + (content_pattern != NULL
1936                                                ? strchr (filename + 4, ':') - filename + 1 : 4));
1937             }
1938             else if (filename != NULL)
1939             {
1940                 vfs_path_t *filename_vpath;
1941 
1942                 filename_vpath = vfs_path_from_str (filename);
1943                 panel_cd (panel, filename_vpath, cd_exact);
1944                 vfs_path_free (filename_vpath, TRUE);
1945             }
1946         }
1947 
1948         MC_PTR_FREE (content_pattern);
1949         g_free (dirname);
1950         g_free (filename);
1951 
1952         if (v == B_ENTER || v == B_CANCEL)
1953             break;
1954 
1955         if (v == B_PANELIZE)
1956         {
1957             panel_re_sort (panel);
1958             try_to_select (panel, NULL);
1959             break;
1960         }
1961     }
1962 }
1963 
1964 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */