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

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