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. add_to_list_take
  7. stop_idle
  8. status_update
  9. found_num_update
  10. get_list_info
  11. find_check_regexp
  12. find_toggle_enable_ignore_dirs
  13. find_toggle_enable_params
  14. find_toggle_enable_content
  15. find_parm_callback
  16. find_parameters
  17. push_directory
  18. pop_directory
  19. queue_dir_free
  20. clear_stack
  21. insert_file
  22. find_add_match
  23. check_find_events
  24. search_content
  25. find_ignore_dir_search
  26. find_rotate_dash
  27. do_search
  28. init_find_vars
  29. find_do_view_edit
  30. view_edit_currently_selected_file
  31. find_calc_button_locations
  32. find_adjust_header
  33. find_relocate_buttons
  34. find_resize
  35. find_callback
  36. start_stop
  37. find_do_view_file
  38. find_do_edit_file
  39. setup_gui
  40. run_process
  41. kill_gui
  42. do_find
  43. find_cmd

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

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