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-2022
   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 /*** 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->str))
 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
 515             && !find_check_regexp (in_with->buffer->str))
 516         {
 517             /* Don't stop the dialog */
 518             widget_set_state (w, WST_ACTIVE, TRUE);
 519             message (D_ERROR, MSG_ERROR, _("Malformed regular expression"));
 520             widget_select (WIDGET (in_with));
 521             return MSG_HANDLED;
 522         }
 523 
 524         return MSG_HANDLED;
 525 
 526     case MSG_POST_KEY:
 527         if (GROUP (h)->current->data == in_name)
 528             find_toggle_enable_params ();
 529         else if (GROUP (h)->current->data == in_with)
 530         {
 531             content_is_empty = input_is_empty (in_with);
 532             find_toggle_enable_content ();
 533         }
 534         return MSG_HANDLED;
 535 
 536     case MSG_DRAW:
 537         if (first_draw)
 538         {
 539             find_toggle_enable_ignore_dirs ();
 540             find_toggle_enable_params ();
 541             find_toggle_enable_content ();
 542         }
 543 
 544         first_draw = FALSE;
 545         MC_FALLTHROUGH;         /* to call MSG_DRAW default handler */
 546 
 547     default:
 548         return dlg_default_callback (w, sender, msg, parm, data);
 549     }
 550 }
 551 
 552 /* --------------------------------------------------------------------------------------------- */
 553 /**
 554  * find_parameters: gets information from the user
 555  *
 556  * If the return value is TRUE, then the following holds:
 557  *
 558  * start_dir, ignore_dirs, pattern and content contain the information provided by the user.
 559  * They are newly allocated strings and must be freed when uneeded.
 560  *
 561  * start_dir_len is -1 when user entered an absolute path, otherwise it is a length
 562  * of start_dir (which is absolute). It is used to get a relative pats of find results.
 563  */
 564 
 565 static gboolean
 566 find_parameters (WPanel * panel, char **start_dir, ssize_t * start_dir_len,
     /* [previous][next][first][last][top][bottom][index][help]  */
 567                  char **ignore_dirs, char **pattern, char **content)
 568 {
 569     WGroup *g;
 570 
 571     /* Size of the find parameters window */
 572 #ifdef HAVE_CHARSET
 573     const int lines = 19;
 574 #else
 575     const int lines = 18;
 576 #endif
 577     int cols = 68;
 578 
 579     gboolean return_value;
 580 
 581     /* file name */
 582     const char *file_name_label = N_("File name:");
 583     const char *file_recurs_label = N_("&Find recursively");
 584     const char *file_follow_symlinks = N_("Follow s&ymlinks");
 585     const char *file_pattern_label = N_("&Using shell patterns");
 586 #ifdef HAVE_CHARSET
 587     const char *file_all_charsets_label = N_("&All charsets");
 588 #endif
 589     const char *file_case_label = N_("Cas&e sensitive");
 590     const char *file_skip_hidden_label = N_("S&kip hidden");
 591 
 592     /* file content */
 593     const char *content_content_label = N_("Content:");
 594     const char *content_use_label = N_("Sea&rch for content");
 595     const char *content_regexp_label = N_("Re&gular expression");
 596     const char *content_case_label = N_("Case sens&itive");
 597 #ifdef HAVE_CHARSET
 598     const char *content_all_charsets_label = N_("A&ll charsets");
 599 #endif
 600     const char *content_whole_words_label = N_("&Whole words");
 601     const char *content_first_hit_label = N_("Fir&st hit");
 602 
 603     const char *buts[] = { N_("&Tree"), N_("&OK"), N_("&Cancel") };
 604 
 605     /* button lengths */
 606     int b0, b1, b2, b12;
 607     int y1, y2, x1, x2;
 608     /* column width */
 609     int cw;
 610 
 611 #ifdef ENABLE_NLS
 612     {
 613         size_t i;
 614 
 615         file_name_label = _(file_name_label);
 616         file_recurs_label = _(file_recurs_label);
 617         file_follow_symlinks = _(file_follow_symlinks);
 618         file_pattern_label = _(file_pattern_label);
 619 #ifdef HAVE_CHARSET
 620         file_all_charsets_label = _(file_all_charsets_label);
 621 #endif
 622         file_case_label = _(file_case_label);
 623         file_skip_hidden_label = _(file_skip_hidden_label);
 624 
 625         /* file content */
 626         content_content_label = _(content_content_label);
 627         content_use_label = _(content_use_label);
 628         content_regexp_label = _(content_regexp_label);
 629         content_case_label = _(content_case_label);
 630 #ifdef HAVE_CHARSET
 631         content_all_charsets_label = _(content_all_charsets_label);
 632 #endif
 633         content_whole_words_label = _(content_whole_words_label);
 634         content_first_hit_label = _(content_first_hit_label);
 635 
 636         for (i = 0; i < G_N_ELEMENTS (buts); i++)
 637             buts[i] = _(buts[i]);
 638     }
 639 #endif /* ENABLE_NLS */
 640 
 641     /* caclulate dialog width */
 642 
 643     /* widget widths */
 644     cw = str_term_width1 (file_name_label);
 645     cw = max (cw, str_term_width1 (file_recurs_label) + 4);
 646     cw = max (cw, str_term_width1 (file_follow_symlinks) + 4);
 647     cw = max (cw, str_term_width1 (file_pattern_label) + 4);
 648 #ifdef HAVE_CHARSET
 649     cw = max (cw, str_term_width1 (file_all_charsets_label) + 4);
 650 #endif
 651     cw = max (cw, str_term_width1 (file_case_label) + 4);
 652     cw = max (cw, str_term_width1 (file_skip_hidden_label) + 4);
 653 
 654     cw = max (cw, str_term_width1 (content_content_label) + 4);
 655     cw = max (cw, str_term_width1 (content_use_label) + 4);
 656     cw = max (cw, str_term_width1 (content_regexp_label) + 4);
 657     cw = max (cw, str_term_width1 (content_case_label) + 4);
 658 #ifdef HAVE_CHARSET
 659     cw = max (cw, str_term_width1 (content_all_charsets_label) + 4);
 660 #endif
 661     cw = max (cw, str_term_width1 (content_whole_words_label) + 4);
 662     cw = max (cw, str_term_width1 (content_first_hit_label) + 4);
 663 
 664     /* button width */
 665     b0 = str_term_width1 (buts[0]) + 3;
 666     b1 = str_term_width1 (buts[1]) + 5; /* default button */
 667     b2 = str_term_width1 (buts[2]) + 3;
 668     b12 = b1 + b2 + 1;
 669 
 670     cols = max (cols, max (b12, cw * 2 + 1) + 6);
 671 
 672     find_load_options ();
 673 
 674     if (in_start_dir == NULL)
 675         in_start_dir = g_strdup (".");
 676 
 677     find_dlg =
 678         dlg_create (TRUE, 0, 0, lines, cols, WPOS_CENTER, FALSE, dialog_colors, find_parm_callback,
 679                     NULL, "[Find File]", _("Find File"));
 680     g = GROUP (find_dlg);
 681 
 682     x1 = 3;
 683     x2 = cols / 2 + 1;
 684     cw = (cols - 7) / 2;
 685     y1 = 2;
 686 
 687     group_add_widget (g, label_new (y1++, x1, _("Start at:")));
 688     in_start =
 689         input_new (y1, x1, input_colors, cols - b0 - 7, in_start_dir, "start",
 690                    INPUT_COMPLETE_CD | INPUT_COMPLETE_FILENAMES);
 691     group_add_widget (g, in_start);
 692 
 693     group_add_widget (g, button_new (y1++, cols - b0 - 3, B_TREE, NORMAL_BUTTON, buts[0], NULL));
 694 
 695     ignore_dirs_cbox =
 696         check_new (y1++, x1, options.ignore_dirs_enable, _("Ena&ble ignore directories:"));
 697     group_add_widget (g, ignore_dirs_cbox);
 698 
 699     in_ignore =
 700         input_new (y1++, x1, input_colors, cols - 6,
 701                    options.ignore_dirs != NULL ? options.ignore_dirs : "", "ignoredirs",
 702                    INPUT_COMPLETE_CD | INPUT_COMPLETE_FILENAMES);
 703     group_add_widget (g, in_ignore);
 704 
 705     group_add_widget (g, hline_new (y1++, -1, -1));
 706 
 707     y2 = y1;
 708 
 709     /* Start 1st column */
 710     group_add_widget (g, label_new (y1++, x1, file_name_label));
 711     in_name =
 712         input_new (y1++, x1, input_colors, cw, INPUT_LAST_TEXT, "name",
 713                    INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_CD);
 714     group_add_widget (g, in_name);
 715 
 716     /* Start 2nd column */
 717     content_label = label_new (y2++, x2, content_content_label);
 718     group_add_widget (g, content_label);
 719     in_with =
 720         input_new (y2++, x2, input_colors, cw, content_is_empty ? "" : INPUT_LAST_TEXT,
 721                    MC_HISTORY_SHARED_SEARCH, INPUT_COMPLETE_NONE);
 722     in_with->label = content_label;
 723     group_add_widget (g, in_with);
 724 
 725     /* Continue 1st column */
 726     recursively_cbox = check_new (y1++, x1, options.find_recurs, file_recurs_label);
 727     group_add_widget (g, recursively_cbox);
 728 
 729     follow_sym_cbox = check_new (y1++, x1, options.follow_symlinks, file_follow_symlinks);
 730     group_add_widget (g, follow_sym_cbox);
 731 
 732     file_pattern_cbox = check_new (y1++, x1, options.file_pattern, file_pattern_label);
 733     group_add_widget (g, file_pattern_cbox);
 734 
 735     file_case_sens_cbox = check_new (y1++, x1, options.file_case_sens, file_case_label);
 736     group_add_widget (g, file_case_sens_cbox);
 737 
 738 #ifdef HAVE_CHARSET
 739     file_all_charsets_cbox =
 740         check_new (y1++, x1, options.file_all_charsets, file_all_charsets_label);
 741     group_add_widget (g, file_all_charsets_cbox);
 742 #endif
 743 
 744     skip_hidden_cbox = check_new (y1++, x1, options.skip_hidden, file_skip_hidden_label);
 745     group_add_widget (g, skip_hidden_cbox);
 746 
 747     /* Continue 2nd column */
 748     content_whole_words_cbox =
 749         check_new (y2++, x2, options.content_whole_words, content_whole_words_label);
 750     group_add_widget (g, content_whole_words_cbox);
 751 
 752     content_regexp_cbox = check_new (y2++, x2, options.content_regexp, content_regexp_label);
 753     group_add_widget (g, content_regexp_cbox);
 754 
 755     content_case_sens_cbox = check_new (y2++, x2, options.content_case_sens, content_case_label);
 756     group_add_widget (g, content_case_sens_cbox);
 757 
 758 #ifdef HAVE_CHARSET
 759     content_all_charsets_cbox =
 760         check_new (y2++, x2, options.content_all_charsets, content_all_charsets_label);
 761     group_add_widget (g, content_all_charsets_cbox);
 762 #endif
 763 
 764     content_first_hit_cbox =
 765         check_new (y2++, x2, options.content_first_hit, content_first_hit_label);
 766     group_add_widget (g, content_first_hit_cbox);
 767 
 768     /* buttons */
 769     y1 = max (y1, y2);
 770     x1 = (cols - b12) / 2;
 771     group_add_widget (g, hline_new (y1++, -1, -1));
 772     group_add_widget (g, button_new (y1, x1, B_ENTER, DEFPUSH_BUTTON, buts[1], NULL));
 773     group_add_widget (g, button_new (y1, x1 + b1 + 1, B_CANCEL, NORMAL_BUTTON, buts[2], NULL));
 774 
 775   find_par_start:
 776     widget_select (WIDGET (in_name));
 777 
 778     switch (dlg_run (find_dlg))
 779     {
 780     case B_CANCEL:
 781         return_value = FALSE;
 782         break;
 783 
 784     case B_TREE:
 785         {
 786             const char *temp_dir;
 787 
 788             if (input_is_empty (in_start) || DIR_IS_DOT (in_start->buffer->str))
 789                 temp_dir = vfs_path_as_str (panel->cwd_vpath);
 790             else
 791                 temp_dir = in_start->buffer->str;
 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 = g_strdup (temp_dir);
 798 
 799             input_assign_text (in_start, in_start_dir);
 800 
 801             /* Warning: Dreadful goto */
 802             goto find_par_start;
 803         }
 804 
 805     default:
 806         {
 807             char *s;
 808 
 809 #ifdef HAVE_CHARSET
 810             options.file_all_charsets = file_all_charsets_cbox->state;
 811             options.content_all_charsets = content_all_charsets_cbox->state;
 812 #endif
 813             options.content_case_sens = content_case_sens_cbox->state;
 814             options.content_regexp = content_regexp_cbox->state;
 815             options.content_first_hit = content_first_hit_cbox->state;
 816             options.content_whole_words = content_whole_words_cbox->state;
 817             options.find_recurs = recursively_cbox->state;
 818             options.follow_symlinks = follow_sym_cbox->state;
 819             options.file_pattern = file_pattern_cbox->state;
 820             options.file_case_sens = file_case_sens_cbox->state;
 821             options.skip_hidden = skip_hidden_cbox->state;
 822             options.ignore_dirs_enable = ignore_dirs_cbox->state;
 823             g_free (options.ignore_dirs);
 824             options.ignore_dirs = input_get_text (in_ignore);
 825 
 826             *content = input_get_text (in_with);
 827             *pattern = input_get_text (in_name);
 828             if (*pattern == NULL)
 829                 *pattern = g_strdup (options.file_pattern ? "*" : ".*");
 830             *start_dir = !input_is_empty (in_start) ? in_start->buffer->str : (char *) ".";
 831             if (in_start_dir != INPUT_LAST_TEXT)
 832                 g_free (in_start_dir);
 833             in_start_dir = g_strdup (*start_dir);
 834 
 835             s = tilde_expand (*start_dir);
 836             canonicalize_pathname (s);
 837 
 838             if (DIR_IS_DOT (s))
 839             {
 840                 *start_dir = g_strdup (vfs_path_as_str (panel->cwd_vpath));
 841                 /* FIXME: is panel->cwd_vpath canonicalized? */
 842                 /* relative paths will be used in panelization */
 843                 *start_dir_len = (ssize_t) strlen (*start_dir);
 844                 g_free (s);
 845             }
 846             else if (g_path_is_absolute (s))
 847             {
 848                 *start_dir = s;
 849                 *start_dir_len = -1;
 850             }
 851             else
 852             {
 853                 /* relative paths will be used in panelization */
 854                 *start_dir =
 855                     mc_build_filename (vfs_path_as_str (panel->cwd_vpath), s, (char *) NULL);
 856                 *start_dir_len = (ssize_t) strlen (vfs_path_as_str (panel->cwd_vpath));
 857                 g_free (s);
 858             }
 859 
 860             if (!options.ignore_dirs_enable || input_is_empty (in_ignore)
 861                 || DIR_IS_DOT (in_ignore->buffer->str))
 862                 *ignore_dirs = NULL;
 863             else
 864                 *ignore_dirs = input_get_text (in_ignore);
 865 
 866             find_save_options ();
 867 
 868             return_value = TRUE;
 869         }
 870     }
 871 
 872     widget_destroy (WIDGET (find_dlg));
 873 
 874     return return_value;
 875 }
 876 
 877 /* --------------------------------------------------------------------------------------------- */
 878 
 879 static inline void
 880 push_directory (vfs_path_t * dir)
     /* [previous][next][first][last][top][bottom][index][help]  */
 881 {
 882     g_queue_push_head (&dir_queue, (void *) dir);
 883 }
 884 
 885 /* --------------------------------------------------------------------------------------------- */
 886 
 887 static inline vfs_path_t *
 888 pop_directory (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 889 {
 890     return (vfs_path_t *) g_queue_pop_head (&dir_queue);
 891 }
 892 
 893 /* --------------------------------------------------------------------------------------------- */
 894 
 895 static void
 896 queue_dir_free (gpointer data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 897 {
 898     vfs_path_free ((vfs_path_t *) data, TRUE);
 899 }
 900 
 901 /* --------------------------------------------------------------------------------------------- */
 902 /** Remove all the items from the stack */
 903 
 904 static void
 905 clear_stack (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 906 {
 907     g_queue_clear_full (&dir_queue, queue_dir_free);
 908 }
 909 
 910 /* --------------------------------------------------------------------------------------------- */
 911 
 912 static void
 913 insert_file (const char *dir, const char *file, gsize start, gsize end)
     /* [previous][next][first][last][top][bottom][index][help]  */
 914 {
 915     char *tmp_name = NULL;
 916     static char *dirname = NULL;
 917     find_match_location_t *location;
 918 
 919     while (IS_PATH_SEP (dir[0]) && IS_PATH_SEP (dir[1]))
 920         dir++;
 921 
 922     if (old_dir != NULL)
 923     {
 924         if (strcmp (old_dir, dir) != 0)
 925         {
 926             g_free (old_dir);
 927             old_dir = g_strdup (dir);
 928             dirname = add_to_list (dir, NULL);
 929         }
 930     }
 931     else
 932     {
 933         old_dir = g_strdup (dir);
 934         dirname = add_to_list (dir, NULL);
 935     }
 936 
 937     tmp_name = g_strdup_printf ("    %s", file);
 938     location = g_malloc (sizeof (*location));
 939     location->dir = dirname;
 940     location->start = start;
 941     location->end = end;
 942     add_to_list (tmp_name, location);
 943     g_free (tmp_name);
 944 }
 945 
 946 /* --------------------------------------------------------------------------------------------- */
 947 
 948 static void
 949 find_add_match (const char *dir, const char *file, gsize start, gsize end)
     /* [previous][next][first][last][top][bottom][index][help]  */
 950 {
 951     insert_file (dir, file, start, end);
 952 
 953     /* Don't scroll */
 954     if (matches == 0)
 955         listbox_select_first (find_list);
 956     widget_draw (WIDGET (find_list));
 957 
 958     matches++;
 959     found_num_update ();
 960 }
 961 
 962 /* --------------------------------------------------------------------------------------------- */
 963 
 964 static FindProgressStatus
 965 check_find_events (WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
 966 {
 967     Gpm_Event event;
 968     int c;
 969 
 970     event.x = -1;
 971     c = tty_get_event (&event, GROUP (h)->mouse_status == MOU_REPEAT, FALSE);
 972     if (c != EV_NONE)
 973     {
 974         dlg_process_event (h, c, &event);
 975         if (h->ret_value == B_ENTER
 976             || h->ret_value == B_CANCEL || h->ret_value == B_AGAIN || h->ret_value == B_PANELIZE)
 977         {
 978             /* dialog terminated */
 979             return FIND_ABORT;
 980         }
 981         if (!widget_get_state (WIDGET (h), WST_IDLE))
 982         {
 983             /* searching suspended */
 984             return FIND_SUSPEND;
 985         }
 986     }
 987 
 988     return FIND_CONT;
 989 }
 990 
 991 /* --------------------------------------------------------------------------------------------- */
 992 /**
 993  * search_content:
 994  *
 995  * Search the content_pattern string in the DIRECTORY/FILE.
 996  * It will add the found entries to the find listbox.
 997  *
 998  * returns FALSE if do_search should look for another file
 999  *         TRUE if do_search should exit and proceed to the event handler
1000  */
1001 
1002 static gboolean
1003 search_content (WDialog * h, const char *directory, const char *filename)
     /* [previous][next][first][last][top][bottom][index][help]  */
1004 {
1005     struct stat s;
1006     char buffer[BUF_4K] = "";   /* raw input buffer */
1007     int file_fd;
1008     gboolean ret_val = FALSE;
1009     vfs_path_t *vpath;
1010     gint64 tv;
1011     gboolean status_updated = FALSE;
1012 
1013     vpath = vfs_path_build_filename (directory, filename, (char *) NULL);
1014 
1015     if (mc_stat (vpath, &s) != 0 || !S_ISREG (s.st_mode))
1016     {
1017         vfs_path_free (vpath, TRUE);
1018         return FALSE;
1019     }
1020 
1021     file_fd = mc_open (vpath, O_RDONLY);
1022     vfs_path_free (vpath, TRUE);
1023 
1024     if (file_fd == -1)
1025         return FALSE;
1026 
1027     /* get time elapsed from last refresh */
1028     tv = g_get_real_time ();
1029 
1030     if (s.st_size >= MIN_REFRESH_FILE_SIZE || (tv - last_refresh) > MAX_REFRESH_INTERVAL)
1031     {
1032         g_snprintf (buffer, sizeof (buffer), _("Grepping in %s"), filename);
1033         status_update (str_trunc (buffer, WIDGET (h)->rect.cols - 8));
1034         mc_refresh ();
1035         last_refresh = tv;
1036         status_updated = TRUE;
1037     }
1038 
1039     tty_enable_interrupt_key ();
1040     tty_got_interrupt ();
1041 
1042     {
1043         int line = 1;
1044         int pos = 0;
1045         int n_read = 0;
1046         off_t off = 0;          /* file_fd's offset corresponding to strbuf[0] */
1047         gboolean found = FALSE;
1048         gsize found_len;
1049         gsize found_start;
1050         char result[BUF_MEDIUM];
1051         char *strbuf = NULL;    /* buffer for fetched string */
1052         int strbuf_size = 0;
1053         int i = -1;             /* compensate for a newline we'll add when we first enter the loop */
1054 
1055         if (resuming)
1056         {
1057             /* We've been previously suspended, start from the previous position */
1058             resuming = FALSE;
1059             line = last_line;
1060             pos = last_pos;
1061             off = last_off;
1062             i = last_i;
1063         }
1064 
1065         while (!ret_val)
1066         {
1067             char ch = '\0';
1068 
1069             off += i + 1;       /* the previous line, plus a newline character */
1070             i = 0;
1071 
1072             /* read to buffer and get line from there */
1073             while (TRUE)
1074             {
1075                 if (pos >= n_read)
1076                 {
1077                     pos = 0;
1078                     n_read = mc_read (file_fd, buffer, sizeof (buffer));
1079                     if (n_read <= 0)
1080                         break;
1081                 }
1082 
1083                 ch = buffer[pos++];
1084                 if (ch == '\0')
1085                 {
1086                     /* skip possible leading zero(s) */
1087                     if (i == 0)
1088                     {
1089                         off++;
1090                         continue;
1091                     }
1092                     break;
1093                 }
1094 
1095                 if (i >= strbuf_size - 1)
1096                 {
1097                     strbuf_size += 128;
1098                     strbuf = g_realloc (strbuf, strbuf_size);
1099                 }
1100 
1101                 /* Strip newline */
1102                 if (ch == '\n')
1103                     break;
1104 
1105                 strbuf[i++] = ch;
1106             }
1107 
1108             if (i == 0)
1109             {
1110                 if (ch == '\0')
1111                     break;
1112 
1113                 /* if (ch == '\n'): do not search in empty strings */
1114                 goto skip_search;
1115             }
1116 
1117             strbuf[i] = '\0';
1118 
1119             if (!found          /* Search in binary line once */
1120                 && mc_search_run (search_content_handle, (const void *) strbuf, 0, i, &found_len))
1121             {
1122                 if (!status_updated)
1123                 {
1124                     /* if we add results for a file, we have to ensure that
1125                        name of this file is shown in status bar */
1126                     g_snprintf (result, sizeof (result), _("Grepping in %s"), filename);
1127                     status_update (str_trunc (result, WIDGET (h)->rect.cols - 8));
1128                     mc_refresh ();
1129                     last_refresh = tv;
1130                     status_updated = TRUE;
1131                 }
1132 
1133                 g_snprintf (result, sizeof (result), "%d:%s", line, filename);
1134                 found_start = off + search_content_handle->normal_offset + 1;   /* off by one: ticket 3280 */
1135                 find_add_match (directory, result, found_start, found_start + found_len);
1136                 found = TRUE;
1137             }
1138 
1139             if (found && options.content_first_hit)
1140                 break;
1141 
1142             if (ch == '\n')
1143             {
1144               skip_search:
1145                 found = FALSE;
1146                 line++;
1147             }
1148 
1149             if ((line & 0xff) == 0)
1150             {
1151                 FindProgressStatus res;
1152 
1153                 res = check_find_events (h);
1154                 switch (res)
1155                 {
1156                 case FIND_ABORT:
1157                     stop_idle (h);
1158                     ret_val = TRUE;
1159                     break;
1160                 case FIND_SUSPEND:
1161                     resuming = TRUE;
1162                     last_line = line;
1163                     last_pos = pos;
1164                     last_off = off;
1165                     last_i = i;
1166                     ret_val = TRUE;
1167                     break;
1168                 default:
1169                     break;
1170                 }
1171             }
1172         }
1173 
1174         g_free (strbuf);
1175     }
1176 
1177     tty_disable_interrupt_key ();
1178     mc_close (file_fd);
1179     return ret_val;
1180 }
1181 
1182 /* --------------------------------------------------------------------------------------------- */
1183 
1184 /**
1185   If dir is absolute, this means we're within dir and searching file here.
1186   If dir is relative, this means we're going to add dir to the directory stack.
1187 **/
1188 static gboolean
1189 find_ignore_dir_search (const char *dir)
     /* [previous][next][first][last][top][bottom][index][help]  */
1190 {
1191     if (find_ignore_dirs != NULL)
1192     {
1193         const size_t dlen = strlen (dir);
1194         const unsigned char dabs = g_path_is_absolute (dir) ? 1 : 0;
1195 
1196         char **ignore_dir;
1197 
1198         for (ignore_dir = find_ignore_dirs; *ignore_dir != NULL; ignore_dir++)
1199         {
1200             const size_t ilen = strlen (*ignore_dir);
1201             const unsigned char iabs = g_path_is_absolute (*ignore_dir) ? 2 : 0;
1202 
1203             /* ignore dir is too long -- skip it */
1204             if (dlen < ilen)
1205                 continue;
1206 
1207             /* handle absolute and relative paths */
1208             switch (iabs | dabs)
1209             {
1210             case 0:            /* both paths are relative */
1211             case 3:            /* both paths are abolute */
1212                 /* if ignore dir is not a path  of dir -- skip it */
1213                 if (strncmp (dir, *ignore_dir, ilen) == 0)
1214                 {
1215                     /* be sure that ignore dir is not a part of dir like:
1216                        ignore dir is "h", dir is "home" */
1217                     if (dir[ilen] == '\0' || IS_PATH_SEP (dir[ilen]))
1218                         return TRUE;
1219                 }
1220                 break;
1221             case 1:            /* dir is absolute, ignore_dir is relative */
1222                 {
1223                     char *d;
1224 
1225                     d = strstr (dir, *ignore_dir);
1226                     if (d != NULL && IS_PATH_SEP (d[-1])
1227                         && (d[ilen] == '\0' || IS_PATH_SEP (d[ilen])))
1228                         return TRUE;
1229                 }
1230                 break;
1231             case 2:            /* dir is relative, ignore_dir is absolute */
1232                 /* FIXME: skip this case */
1233                 break;
1234             default:           /* this cannot occurs */
1235                 return FALSE;
1236             }
1237         }
1238     }
1239 
1240     return FALSE;
1241 }
1242 
1243 /* --------------------------------------------------------------------------------------------- */
1244 
1245 static void
1246 find_rotate_dash (const WDialog * h, gboolean show)
     /* [previous][next][first][last][top][bottom][index][help]  */
1247 {
1248     static size_t pos = 0;
1249     static const char rotating_dash[4] = "|/-\\";
1250     const Widget *w = CONST_WIDGET (h);
1251     const int *colors;
1252 
1253     colors = widget_get_colors (w);
1254     tty_setcolor (colors[DLG_COLOR_NORMAL]);
1255     widget_gotoyx (h, w->rect.lines - 7, w->rect.cols - 4);
1256     tty_print_char (show ? rotating_dash[pos] : ' ');
1257     pos = (pos + 1) % sizeof (rotating_dash);
1258     mc_refresh ();
1259 }
1260 
1261 /* --------------------------------------------------------------------------------------------- */
1262 
1263 static int
1264 do_search (WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
1265 {
1266     static struct vfs_dirent *dp = NULL;
1267     static DIR *dirp = NULL;
1268     static char *directory = NULL;
1269     struct stat tmp_stat;
1270     gsize bytes_found;
1271     unsigned short count;
1272 
1273     if (h == NULL)
1274     {                           /* someone forces me to close dirp */
1275         if (dirp != NULL)
1276         {
1277             mc_closedir (dirp);
1278             dirp = NULL;
1279         }
1280         MC_PTR_FREE (directory);
1281         dp = NULL;
1282         return 1;
1283     }
1284 
1285     for (count = 0; count < 32; count++)
1286     {
1287         while (dp == NULL)
1288         {
1289             if (dirp != NULL)
1290             {
1291                 mc_closedir (dirp);
1292                 dirp = NULL;
1293             }
1294 
1295             while (dirp == NULL)
1296             {
1297                 vfs_path_t *tmp_vpath = NULL;
1298 
1299                 tty_setcolor (REVERSE_COLOR);
1300 
1301                 while (TRUE)
1302                 {
1303                     tmp_vpath = pop_directory ();
1304                     if (tmp_vpath == NULL)
1305                     {
1306                         running = FALSE;
1307                         if (ignore_count == 0)
1308                             status_update (_("Finished"));
1309                         else
1310                         {
1311                             char msg[BUF_SMALL];
1312 
1313                             g_snprintf (msg, sizeof (msg),
1314                                         ngettext ("Finished (ignored %zu directory)",
1315                                                   "Finished (ignored %zu directories)",
1316                                                   ignore_count), ignore_count);
1317                             status_update (msg);
1318                         }
1319                         if (verbose)
1320                             find_rotate_dash (h, FALSE);
1321                         stop_idle (h);
1322                         return 0;
1323                     }
1324 
1325                     /* handle absolute ignore dirs here */
1326                     {
1327                         gboolean ok;
1328 
1329                         ok = find_ignore_dir_search (vfs_path_as_str (tmp_vpath));
1330                         if (!ok)
1331                             break;
1332                     }
1333 
1334                     vfs_path_free (tmp_vpath, TRUE);
1335                     ignore_count++;
1336                 }
1337 
1338                 g_free (directory);
1339                 directory = g_strdup (vfs_path_as_str (tmp_vpath));
1340 
1341                 if (verbose)
1342                 {
1343                     char buffer[BUF_MEDIUM];
1344 
1345                     g_snprintf (buffer, sizeof (buffer), _("Searching %s"), directory);
1346                     status_update (str_trunc (directory, WIDGET (h)->rect.cols - 8));
1347                 }
1348 
1349                 dirp = mc_opendir (tmp_vpath);
1350                 vfs_path_free (tmp_vpath, TRUE);
1351             }                   /* while (!dirp) */
1352 
1353             /* skip invalid filenames */
1354             while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1355                 ;
1356         }                       /* while (!dp) */
1357 
1358         if (DIR_IS_DOT (dp->d_name) || DIR_IS_DOTDOT (dp->d_name))
1359         {
1360             /* skip invalid filenames */
1361             while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1362                 ;
1363 
1364             return 1;
1365         }
1366 
1367         if (!(options.skip_hidden && (dp->d_name[0] == '.')))
1368         {
1369             gboolean search_ok;
1370 
1371             if (options.find_recurs && (directory != NULL))
1372             {                   /* Can directory be NULL ? */
1373                 /* handle relative ignore dirs here */
1374                 if (options.ignore_dirs_enable && find_ignore_dir_search (dp->d_name))
1375                     ignore_count++;
1376                 else
1377                 {
1378                     vfs_path_t *tmp_vpath;
1379                     int stat_res;
1380 
1381                     tmp_vpath = vfs_path_build_filename (directory, dp->d_name, (char *) NULL);
1382 
1383                     if (options.follow_symlinks)
1384                         stat_res = mc_stat (tmp_vpath, &tmp_stat);
1385                     else
1386                         stat_res = mc_lstat (tmp_vpath, &tmp_stat);
1387 
1388                     if (stat_res == 0 && S_ISDIR (tmp_stat.st_mode))
1389                         push_directory (tmp_vpath);
1390                     else
1391                         vfs_path_free (tmp_vpath, TRUE);
1392                 }
1393             }
1394 
1395             search_ok = mc_search_run (search_file_handle, dp->d_name,
1396                                        0, strlen (dp->d_name), &bytes_found);
1397 
1398             if (search_ok)
1399             {
1400                 if (content_pattern == NULL)
1401                     find_add_match (directory, dp->d_name, 0, 0);
1402                 else if (search_content (h, directory, dp->d_name))
1403                     return 1;
1404             }
1405         }
1406 
1407         /* skip invalid filenames */
1408         while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1409             ;
1410     }                           /* for */
1411 
1412     if (verbose)
1413         find_rotate_dash (h, TRUE);
1414 
1415     return 1;
1416 }
1417 
1418 /* --------------------------------------------------------------------------------------------- */
1419 
1420 static void
1421 init_find_vars (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1422 {
1423     MC_PTR_FREE (old_dir);
1424     matches = 0;
1425     ignore_count = 0;
1426 
1427     /* Remove all the items from the stack */
1428     clear_stack ();
1429 
1430     g_strfreev (find_ignore_dirs);
1431     find_ignore_dirs = NULL;
1432 }
1433 
1434 /* --------------------------------------------------------------------------------------------- */
1435 
1436 static void
1437 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]  */
1438                    off_t search_end)
1439 {
1440     const char *filename = NULL;
1441     int line;
1442     vfs_path_t *fullname_vpath;
1443 
1444     if (content_pattern != NULL)
1445     {
1446         filename = strchr (file + 4, ':') + 1;
1447         line = atoi (file + 4);
1448     }
1449     else
1450     {
1451         filename = file + 4;
1452         line = 0;
1453     }
1454 
1455     fullname_vpath = vfs_path_build_filename (dir, filename, (char *) NULL);
1456     if (edit)
1457         edit_file_at_line (fullname_vpath, use_internal_edit, line);
1458     else
1459         view_file_at_line (fullname_vpath, unparsed_view, use_internal_view, line, search_start,
1460                            search_end);
1461     vfs_path_free (fullname_vpath, TRUE);
1462 }
1463 
1464 /* --------------------------------------------------------------------------------------------- */
1465 
1466 static cb_ret_t
1467 view_edit_currently_selected_file (gboolean unparsed_view, gboolean edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1468 {
1469     char *text = NULL;
1470     find_match_location_t *location;
1471 
1472     listbox_get_current (find_list, &text, (void **) &location);
1473 
1474     if ((text == NULL) || (location == NULL) || (location->dir == NULL))
1475         return MSG_NOT_HANDLED;
1476 
1477     find_do_view_edit (unparsed_view, edit, location->dir, text, location->start, location->end);
1478     return MSG_HANDLED;
1479 }
1480 
1481 /* --------------------------------------------------------------------------------------------- */
1482 
1483 static void
1484 find_calc_button_locations (const WDialog * h, gboolean all_buttons)
     /* [previous][next][first][last][top][bottom][index][help]  */
1485 {
1486     const int cols = CONST_WIDGET (h)->rect.cols;
1487 
1488     int l1, l2;
1489 
1490     l1 = fbuts[0].len + fbuts[1].len + fbuts[is_start ? 3 : 2].len + fbuts[4].len + 3;
1491     l2 = fbuts[5].len + fbuts[6].len + fbuts[7].len + 2;
1492 
1493     fbuts[0].x = (cols - l1) / 2;
1494     fbuts[1].x = fbuts[0].x + fbuts[0].len + 1;
1495     fbuts[2].x = fbuts[1].x + fbuts[1].len + 1;
1496     fbuts[3].x = fbuts[2].x;
1497     fbuts[4].x = fbuts[2].x + fbuts[is_start ? 3 : 2].len + 1;
1498 
1499     if (all_buttons)
1500     {
1501         fbuts[5].x = (cols - l2) / 2;
1502         fbuts[6].x = fbuts[5].x + fbuts[5].len + 1;
1503         fbuts[7].x = fbuts[6].x + fbuts[6].len + 1;
1504     }
1505 }
1506 
1507 /* --------------------------------------------------------------------------------------------- */
1508 
1509 static void
1510 find_adjust_header (WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
1511 {
1512     char title[BUF_MEDIUM];
1513     int title_len;
1514 
1515     if (content_pattern != NULL)
1516         g_snprintf (title, sizeof (title), _("Find File: \"%s\". Content: \"%s\""), find_pattern,
1517                     content_pattern);
1518     else
1519         g_snprintf (title, sizeof (title), _("Find File: \"%s\""), find_pattern);
1520 
1521     title_len = str_term_width1 (title);
1522     if (title_len > WIDGET (h)->rect.cols - 6)
1523     {
1524         /* title is too wide, truncate it */
1525         title_len = WIDGET (h)->rect.cols - 6;
1526         title_len = str_column_to_pos (title, title_len);
1527         title_len -= 3;         /* reserve space for three dots */
1528         title_len = str_offset_to_pos (title, title_len);
1529         /* mark that title is truncated */
1530         memmove (title + title_len, "...", 4);
1531     }
1532 
1533     frame_set_title (FRAME (h->bg), title);
1534 }
1535 
1536 /* --------------------------------------------------------------------------------------------- */
1537 
1538 static void
1539 find_relocate_buttons (const WDialog * h, gboolean all_buttons)
     /* [previous][next][first][last][top][bottom][index][help]  */
1540 {
1541     size_t i;
1542 
1543     find_calc_button_locations (h, all_buttons);
1544 
1545     for (i = 0; i < fbuts_num; i++)
1546         fbuts[i].button->rect.x = CONST_WIDGET (h)->rect.x + fbuts[i].x;
1547 }
1548 
1549 /* --------------------------------------------------------------------------------------------- */
1550 
1551 static cb_ret_t
1552 find_resize (WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
1553 {
1554     Widget *w = WIDGET (h);
1555     WRect r = w->rect;
1556 
1557     r.lines = LINES - 4;
1558     r.cols = COLS - 16;
1559     dlg_default_callback (w, NULL, MSG_RESIZE, 0, &r);
1560     find_adjust_header (h);
1561     find_relocate_buttons (h, TRUE);
1562 
1563     return MSG_HANDLED;
1564 }
1565 
1566 /* --------------------------------------------------------------------------------------------- */
1567 
1568 static cb_ret_t
1569 find_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
1570 {
1571     WDialog *h = DIALOG (w);
1572 
1573     switch (msg)
1574     {
1575     case MSG_INIT:
1576         group_default_callback (w, NULL, MSG_INIT, 0, NULL);
1577         find_adjust_header (h);
1578         return MSG_HANDLED;
1579 
1580     case MSG_KEY:
1581         if (parm == KEY_F (3) || parm == KEY_F (13))
1582         {
1583             gboolean unparsed_view = (parm == KEY_F (13));
1584 
1585             return view_edit_currently_selected_file (unparsed_view, FALSE);
1586         }
1587         if (parm == KEY_F (4))
1588             return view_edit_currently_selected_file (FALSE, TRUE);
1589         return MSG_NOT_HANDLED;
1590 
1591     case MSG_RESIZE:
1592         return find_resize (h);
1593 
1594     case MSG_IDLE:
1595         do_search (h);
1596         return MSG_HANDLED;
1597 
1598     default:
1599         return dlg_default_callback (w, sender, msg, parm, data);
1600     }
1601 }
1602 
1603 /* --------------------------------------------------------------------------------------------- */
1604 /** Handles the Stop/Start button in the find window */
1605 
1606 static int
1607 start_stop (WButton * button, int action)
     /* [previous][next][first][last][top][bottom][index][help]  */
1608 {
1609     Widget *w = WIDGET (button);
1610 
1611     (void) action;
1612 
1613     running = is_start;
1614     widget_idle (WIDGET (find_dlg), running);
1615     is_start = !is_start;
1616 
1617     status_update (is_start ? _("Stopped") : _("Searching"));
1618     button_set_text (button, fbuts[is_start ? 3 : 2].text);
1619 
1620     find_relocate_buttons (DIALOG (w->owner), FALSE);
1621     widget_draw (WIDGET (w->owner));
1622 
1623     return 0;
1624 }
1625 
1626 /* --------------------------------------------------------------------------------------------- */
1627 /** Handle view command, when invoked as a button */
1628 
1629 static int
1630 find_do_view_file (WButton * button, int action)
     /* [previous][next][first][last][top][bottom][index][help]  */
1631 {
1632     (void) button;
1633     (void) action;
1634 
1635     view_edit_currently_selected_file (FALSE, FALSE);
1636     return 0;
1637 }
1638 
1639 /* --------------------------------------------------------------------------------------------- */
1640 /** Handle edit command, when invoked as a button */
1641 
1642 static int
1643 find_do_edit_file (WButton * button, int action)
     /* [previous][next][first][last][top][bottom][index][help]  */
1644 {
1645     (void) button;
1646     (void) action;
1647 
1648     view_edit_currently_selected_file (FALSE, TRUE);
1649     return 0;
1650 }
1651 
1652 /* --------------------------------------------------------------------------------------------- */
1653 
1654 static void
1655 setup_gui (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1656 {
1657     WGroup *g;
1658     size_t i;
1659     int lines, cols;
1660     int y;
1661 
1662     static gboolean i18n_flag = FALSE;
1663 
1664     if (!i18n_flag)
1665     {
1666         for (i = 0; i < fbuts_num; i++)
1667         {
1668 #ifdef ENABLE_NLS
1669             fbuts[i].text = _(fbuts[i].text);
1670 #endif /* ENABLE_NLS */
1671             fbuts[i].len = str_term_width1 (fbuts[i].text) + 3;
1672             if (fbuts[i].flags == DEFPUSH_BUTTON)
1673                 fbuts[i].len += 2;
1674         }
1675 
1676         i18n_flag = TRUE;
1677     }
1678 
1679     lines = LINES - 4;
1680     cols = COLS - 16;
1681 
1682     find_dlg =
1683         dlg_create (TRUE, 0, 0, lines, cols, WPOS_CENTER, FALSE, dialog_colors, find_callback, NULL,
1684                     "[Find File]", NULL);
1685     g = GROUP (find_dlg);
1686 
1687     find_calc_button_locations (find_dlg, TRUE);
1688 
1689     y = 2;
1690     find_list = listbox_new (y, 2, lines - 10, cols - 4, FALSE, NULL);
1691     group_add_widget_autopos (g, find_list, WPOS_KEEP_ALL, NULL);
1692     y += WIDGET (find_list)->rect.lines;
1693 
1694     group_add_widget_autopos (g, hline_new (y++, -1, -1), WPOS_KEEP_BOTTOM, NULL);
1695 
1696     found_num_label = label_new (y++, 4, "");
1697     group_add_widget_autopos (g, found_num_label, WPOS_KEEP_BOTTOM, NULL);
1698 
1699     status_label = label_new (y++, 4, _("Searching"));
1700     group_add_widget_autopos (g, status_label, WPOS_KEEP_BOTTOM, NULL);
1701 
1702     group_add_widget_autopos (g, hline_new (y++, -1, -1), WPOS_KEEP_BOTTOM, NULL);
1703 
1704     for (i = 0; i < fbuts_num; i++)
1705     {
1706         if (i == 3)
1707             fbuts[3].button = fbuts[2].button;
1708         else
1709         {
1710             fbuts[i].button =
1711                 WIDGET (button_new
1712                         (y, fbuts[i].x, fbuts[i].ret_cmd, fbuts[i].flags, fbuts[i].text,
1713                          fbuts[i].callback));
1714             group_add_widget_autopos (g, fbuts[i].button, WPOS_KEEP_BOTTOM, NULL);
1715         }
1716 
1717         if (i == quit_button)
1718             y++;
1719     }
1720 
1721     widget_select (WIDGET (find_list));
1722 }
1723 
1724 /* --------------------------------------------------------------------------------------------- */
1725 
1726 static int
1727 run_process (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1728 {
1729     int ret;
1730 
1731     search_content_handle = mc_search_new (content_pattern, NULL);
1732     if (search_content_handle)
1733     {
1734         search_content_handle->search_type =
1735             options.content_regexp ? MC_SEARCH_T_REGEX : MC_SEARCH_T_NORMAL;
1736         search_content_handle->is_case_sensitive = options.content_case_sens;
1737         search_content_handle->whole_words = options.content_whole_words;
1738 #ifdef HAVE_CHARSET
1739         search_content_handle->is_all_charsets = options.content_all_charsets;
1740 #endif
1741     }
1742     search_file_handle = mc_search_new (find_pattern, NULL);
1743     search_file_handle->search_type = options.file_pattern ? MC_SEARCH_T_GLOB : MC_SEARCH_T_REGEX;
1744     search_file_handle->is_case_sensitive = options.file_case_sens;
1745 #ifdef HAVE_CHARSET
1746     search_file_handle->is_all_charsets = options.file_all_charsets;
1747 #endif
1748     search_file_handle->is_entire_line = options.file_pattern;
1749 
1750     resuming = FALSE;
1751 
1752     widget_idle (WIDGET (find_dlg), TRUE);
1753     ret = dlg_run (find_dlg);
1754 
1755     mc_search_free (search_file_handle);
1756     search_file_handle = NULL;
1757     mc_search_free (search_content_handle);
1758     search_content_handle = NULL;
1759 
1760     return ret;
1761 }
1762 
1763 /* --------------------------------------------------------------------------------------------- */
1764 
1765 static void
1766 kill_gui (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1767 {
1768     Widget *w = WIDGET (find_dlg);
1769 
1770     widget_idle (w, FALSE);
1771     widget_destroy (w);
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]  */