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 (input_get_ctext (in_name)))
 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 (input_get_ctext (in_with)))
 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 *start_cstr;
 787             const char *temp_dir;
 788 
 789             start_cstr = input_get_ctext (in_start);
 790 
 791             if (input_is_empty (in_start) || DIR_IS_DOT (start_cstr))
 792                 temp_dir = vfs_path_as_str (panel->cwd_vpath);
 793             else
 794                 temp_dir = start_cstr;
 795 
 796             if (in_start_dir != INPUT_LAST_TEXT)
 797                 g_free (in_start_dir);
 798             in_start_dir = tree_box (temp_dir);
 799             if (in_start_dir == NULL)
 800                 in_start_dir = g_strdup (temp_dir);
 801 
 802             input_assign_text (in_start, in_start_dir);
 803 
 804             /* Warning: Dreadful goto */
 805             goto find_par_start;
 806         }
 807 
 808     default:
 809         {
 810             char *s;
 811 
 812 #ifdef HAVE_CHARSET
 813             options.file_all_charsets = file_all_charsets_cbox->state;
 814             options.content_all_charsets = content_all_charsets_cbox->state;
 815 #endif
 816             options.content_case_sens = content_case_sens_cbox->state;
 817             options.content_regexp = content_regexp_cbox->state;
 818             options.content_first_hit = content_first_hit_cbox->state;
 819             options.content_whole_words = content_whole_words_cbox->state;
 820             options.find_recurs = recursively_cbox->state;
 821             options.follow_symlinks = follow_sym_cbox->state;
 822             options.file_pattern = file_pattern_cbox->state;
 823             options.file_case_sens = file_case_sens_cbox->state;
 824             options.skip_hidden = skip_hidden_cbox->state;
 825             options.ignore_dirs_enable = ignore_dirs_cbox->state;
 826             g_free (options.ignore_dirs);
 827             options.ignore_dirs = input_get_text (in_ignore);
 828 
 829             *content = !input_is_empty (in_with) ? input_get_text (in_with) : NULL;
 830             if (input_is_empty (in_name))
 831                 *pattern = g_strdup (options.file_pattern ? "*" : ".*");
 832             else
 833                 *pattern = input_get_text (in_name);
 834             *start_dir = (char *) (!input_is_empty (in_start) ? input_get_ctext (in_start) : ".");
 835             if (in_start_dir != INPUT_LAST_TEXT)
 836                 g_free (in_start_dir);
 837             in_start_dir = g_strdup (*start_dir);
 838 
 839             s = tilde_expand (*start_dir);
 840             canonicalize_pathname (s);
 841 
 842             if (DIR_IS_DOT (s))
 843             {
 844                 *start_dir = g_strdup (vfs_path_as_str (panel->cwd_vpath));
 845                 /* FIXME: is panel->cwd_vpath canonicalized? */
 846                 /* relative paths will be used in panelization */
 847                 *start_dir_len = (ssize_t) strlen (*start_dir);
 848                 g_free (s);
 849             }
 850             else if (g_path_is_absolute (s))
 851             {
 852                 *start_dir = s;
 853                 *start_dir_len = -1;
 854             }
 855             else
 856             {
 857                 /* relative paths will be used in panelization */
 858                 *start_dir =
 859                     mc_build_filename (vfs_path_as_str (panel->cwd_vpath), s, (char *) NULL);
 860                 *start_dir_len = (ssize_t) strlen (vfs_path_as_str (panel->cwd_vpath));
 861                 g_free (s);
 862             }
 863 
 864             if (!options.ignore_dirs_enable || input_is_empty (in_ignore)
 865                 || DIR_IS_DOT (input_get_ctext (in_ignore)))
 866                 *ignore_dirs = NULL;
 867             else
 868                 *ignore_dirs = input_get_text (in_ignore);
 869 
 870             find_save_options ();
 871 
 872             return_value = TRUE;
 873         }
 874     }
 875 
 876     widget_destroy (WIDGET (find_dlg));
 877 
 878     return return_value;
 879 }
 880 
 881 /* --------------------------------------------------------------------------------------------- */
 882 
 883 static inline void
 884 push_directory (vfs_path_t * dir)
     /* [previous][next][first][last][top][bottom][index][help]  */
 885 {
 886     g_queue_push_head (&dir_queue, (void *) dir);
 887 }
 888 
 889 /* --------------------------------------------------------------------------------------------- */
 890 
 891 static inline vfs_path_t *
 892 pop_directory (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 893 {
 894     return (vfs_path_t *) g_queue_pop_head (&dir_queue);
 895 }
 896 
 897 /* --------------------------------------------------------------------------------------------- */
 898 
 899 static void
 900 queue_dir_free (gpointer data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 901 {
 902     vfs_path_free ((vfs_path_t *) data, TRUE);
 903 }
 904 
 905 /* --------------------------------------------------------------------------------------------- */
 906 /** Remove all the items from the stack */
 907 
 908 static void
 909 clear_stack (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 910 {
 911     g_queue_clear_full (&dir_queue, queue_dir_free);
 912 }
 913 
 914 /* --------------------------------------------------------------------------------------------- */
 915 
 916 static void
 917 insert_file (const char *dir, const char *file, gsize start, gsize end)
     /* [previous][next][first][last][top][bottom][index][help]  */
 918 {
 919     char *tmp_name = NULL;
 920     static char *dirname = NULL;
 921     find_match_location_t *location;
 922 
 923     while (IS_PATH_SEP (dir[0]) && IS_PATH_SEP (dir[1]))
 924         dir++;
 925 
 926     if (old_dir != NULL)
 927     {
 928         if (strcmp (old_dir, dir) != 0)
 929         {
 930             g_free (old_dir);
 931             old_dir = g_strdup (dir);
 932             dirname = add_to_list (dir, NULL);
 933         }
 934     }
 935     else
 936     {
 937         old_dir = g_strdup (dir);
 938         dirname = add_to_list (dir, NULL);
 939     }
 940 
 941     tmp_name = g_strdup_printf ("    %s", file);
 942     location = g_malloc (sizeof (*location));
 943     location->dir = dirname;
 944     location->start = start;
 945     location->end = end;
 946     add_to_list (tmp_name, location);
 947     g_free (tmp_name);
 948 }
 949 
 950 /* --------------------------------------------------------------------------------------------- */
 951 
 952 static void
 953 find_add_match (const char *dir, const char *file, gsize start, gsize end)
     /* [previous][next][first][last][top][bottom][index][help]  */
 954 {
 955     insert_file (dir, file, start, end);
 956 
 957     /* Don't scroll */
 958     if (matches == 0)
 959         listbox_select_first (find_list);
 960     widget_draw (WIDGET (find_list));
 961 
 962     matches++;
 963     found_num_update ();
 964 }
 965 
 966 /* --------------------------------------------------------------------------------------------- */
 967 
 968 static FindProgressStatus
 969 check_find_events (WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
 970 {
 971     Gpm_Event event;
 972     int c;
 973 
 974     event.x = -1;
 975     c = tty_get_event (&event, GROUP (h)->mouse_status == MOU_REPEAT, FALSE);
 976     if (c != EV_NONE)
 977     {
 978         dlg_process_event (h, c, &event);
 979         if (h->ret_value == B_ENTER
 980             || h->ret_value == B_CANCEL || h->ret_value == B_AGAIN || h->ret_value == B_PANELIZE)
 981         {
 982             /* dialog terminated */
 983             return FIND_ABORT;
 984         }
 985         if (!widget_get_state (WIDGET (h), WST_IDLE))
 986         {
 987             /* searching suspended */
 988             return FIND_SUSPEND;
 989         }
 990     }
 991 
 992     return FIND_CONT;
 993 }
 994 
 995 /* --------------------------------------------------------------------------------------------- */
 996 /**
 997  * search_content:
 998  *
 999  * Search the content_pattern string in the DIRECTORY/FILE.
1000  * It will add the found entries to the find listbox.
1001  *
1002  * returns FALSE if do_search should look for another file
1003  *         TRUE if do_search should exit and proceed to the event handler
1004  */
1005 
1006 static gboolean
1007 search_content (WDialog * h, const char *directory, const char *filename)
     /* [previous][next][first][last][top][bottom][index][help]  */
1008 {
1009     struct stat s;
1010     char buffer[BUF_4K] = "";   /* raw input buffer */
1011     int file_fd;
1012     gboolean ret_val = FALSE;
1013     vfs_path_t *vpath;
1014     gint64 tv;
1015     gboolean status_updated = FALSE;
1016 
1017     vpath = vfs_path_build_filename (directory, filename, (char *) NULL);
1018 
1019     if (mc_stat (vpath, &s) != 0 || !S_ISREG (s.st_mode))
1020     {
1021         vfs_path_free (vpath, TRUE);
1022         return FALSE;
1023     }
1024 
1025     file_fd = mc_open (vpath, O_RDONLY);
1026     vfs_path_free (vpath, TRUE);
1027 
1028     if (file_fd == -1)
1029         return FALSE;
1030 
1031     /* get time elapsed from last refresh */
1032     tv = g_get_real_time ();
1033 
1034     if (s.st_size >= MIN_REFRESH_FILE_SIZE || (tv - last_refresh) > MAX_REFRESH_INTERVAL)
1035     {
1036         g_snprintf (buffer, sizeof (buffer), _("Grepping in %s"), filename);
1037         status_update (str_trunc (buffer, WIDGET (h)->rect.cols - 8));
1038         mc_refresh ();
1039         last_refresh = tv;
1040         status_updated = TRUE;
1041     }
1042 
1043     tty_enable_interrupt_key ();
1044     tty_got_interrupt ();
1045 
1046     {
1047         int line = 1;
1048         int pos = 0;
1049         int n_read = 0;
1050         off_t off = 0;          /* file_fd's offset corresponding to strbuf[0] */
1051         gboolean found = FALSE;
1052         gsize found_len;
1053         gsize found_start;
1054         char result[BUF_MEDIUM];
1055         char *strbuf = NULL;    /* buffer for fetched string */
1056         int strbuf_size = 0;
1057         int i = -1;             /* compensate for a newline we'll add when we first enter the loop */
1058 
1059         if (resuming)
1060         {
1061             /* We've been previously suspended, start from the previous position */
1062             resuming = FALSE;
1063             line = last_line;
1064             pos = last_pos;
1065             off = last_off;
1066             i = last_i;
1067         }
1068 
1069         while (!ret_val)
1070         {
1071             char ch = '\0';
1072 
1073             off += i + 1;       /* the previous line, plus a newline character */
1074             i = 0;
1075 
1076             /* read to buffer and get line from there */
1077             while (TRUE)
1078             {
1079                 if (pos >= n_read)
1080                 {
1081                     pos = 0;
1082                     n_read = mc_read (file_fd, buffer, sizeof (buffer));
1083                     if (n_read <= 0)
1084                         break;
1085                 }
1086 
1087                 ch = buffer[pos++];
1088                 if (ch == '\0')
1089                 {
1090                     /* skip possible leading zero(s) */
1091                     if (i == 0)
1092                     {
1093                         off++;
1094                         continue;
1095                     }
1096                     break;
1097                 }
1098 
1099                 if (i >= strbuf_size - 1)
1100                 {
1101                     strbuf_size += 128;
1102                     strbuf = g_realloc (strbuf, strbuf_size);
1103                 }
1104 
1105                 /* Strip newline */
1106                 if (ch == '\n')
1107                     break;
1108 
1109                 strbuf[i++] = ch;
1110             }
1111 
1112             if (i == 0)
1113             {
1114                 if (ch == '\0')
1115                     break;
1116 
1117                 /* if (ch == '\n'): do not search in empty strings */
1118                 goto skip_search;
1119             }
1120 
1121             strbuf[i] = '\0';
1122 
1123             if (!found          /* Search in binary line once */
1124                 && mc_search_run (search_content_handle, (const void *) strbuf, 0, i, &found_len))
1125             {
1126                 if (!status_updated)
1127                 {
1128                     /* if we add results for a file, we have to ensure that
1129                        name of this file is shown in status bar */
1130                     g_snprintf (result, sizeof (result), _("Grepping in %s"), filename);
1131                     status_update (str_trunc (result, WIDGET (h)->rect.cols - 8));
1132                     mc_refresh ();
1133                     last_refresh = tv;
1134                     status_updated = TRUE;
1135                 }
1136 
1137                 g_snprintf (result, sizeof (result), "%d:%s", line, filename);
1138                 found_start = off + search_content_handle->normal_offset + 1;   /* off by one: ticket 3280 */
1139                 find_add_match (directory, result, found_start, found_start + found_len);
1140                 found = TRUE;
1141             }
1142 
1143             if (found && options.content_first_hit)
1144                 break;
1145 
1146             if (ch == '\n')
1147             {
1148               skip_search:
1149                 found = FALSE;
1150                 line++;
1151             }
1152 
1153             if ((line & 0xff) == 0)
1154             {
1155                 FindProgressStatus res;
1156 
1157                 res = check_find_events (h);
1158                 switch (res)
1159                 {
1160                 case FIND_ABORT:
1161                     stop_idle (h);
1162                     ret_val = TRUE;
1163                     break;
1164                 case FIND_SUSPEND:
1165                     resuming = TRUE;
1166                     last_line = line;
1167                     last_pos = pos;
1168                     last_off = off;
1169                     last_i = i;
1170                     ret_val = TRUE;
1171                     break;
1172                 default:
1173                     break;
1174                 }
1175             }
1176         }
1177 
1178         g_free (strbuf);
1179     }
1180 
1181     tty_disable_interrupt_key ();
1182     mc_close (file_fd);
1183     return ret_val;
1184 }
1185 
1186 /* --------------------------------------------------------------------------------------------- */
1187 
1188 /**
1189   If dir is absolute, this means we're within dir and searching file here.
1190   If dir is relative, this means we're going to add dir to the directory stack.
1191 **/
1192 static gboolean
1193 find_ignore_dir_search (const char *dir)
     /* [previous][next][first][last][top][bottom][index][help]  */
1194 {
1195     if (find_ignore_dirs != NULL)
1196     {
1197         const size_t dlen = strlen (dir);
1198         const unsigned char dabs = g_path_is_absolute (dir) ? 1 : 0;
1199 
1200         char **ignore_dir;
1201 
1202         for (ignore_dir = find_ignore_dirs; *ignore_dir != NULL; ignore_dir++)
1203         {
1204             const size_t ilen = strlen (*ignore_dir);
1205             const unsigned char iabs = g_path_is_absolute (*ignore_dir) ? 2 : 0;
1206 
1207             /* ignore dir is too long -- skip it */
1208             if (dlen < ilen)
1209                 continue;
1210 
1211             /* handle absolute and relative paths */
1212             switch (iabs | dabs)
1213             {
1214             case 0:            /* both paths are relative */
1215             case 3:            /* both paths are abolute */
1216                 /* if ignore dir is not a path  of dir -- skip it */
1217                 if (strncmp (dir, *ignore_dir, ilen) == 0)
1218                 {
1219                     /* be sure that ignore dir is not a part of dir like:
1220                        ignore dir is "h", dir is "home" */
1221                     if (dir[ilen] == '\0' || IS_PATH_SEP (dir[ilen]))
1222                         return TRUE;
1223                 }
1224                 break;
1225             case 1:            /* dir is absolute, ignore_dir is relative */
1226                 {
1227                     char *d;
1228 
1229                     d = strstr (dir, *ignore_dir);
1230                     if (d != NULL && IS_PATH_SEP (d[-1])
1231                         && (d[ilen] == '\0' || IS_PATH_SEP (d[ilen])))
1232                         return TRUE;
1233                 }
1234                 break;
1235             case 2:            /* dir is relative, ignore_dir is absolute */
1236                 /* FIXME: skip this case */
1237                 break;
1238             default:           /* this cannot occurs */
1239                 return FALSE;
1240             }
1241         }
1242     }
1243 
1244     return FALSE;
1245 }
1246 
1247 /* --------------------------------------------------------------------------------------------- */
1248 
1249 static void
1250 find_rotate_dash (const WDialog * h, gboolean show)
     /* [previous][next][first][last][top][bottom][index][help]  */
1251 {
1252     static size_t pos = 0;
1253     static const char rotating_dash[4] = "|/-\\";
1254     const Widget *w = CONST_WIDGET (h);
1255     const int *colors;
1256 
1257     colors = widget_get_colors (w);
1258     tty_setcolor (colors[DLG_COLOR_NORMAL]);
1259     widget_gotoyx (h, w->rect.lines - 7, w->rect.cols - 4);
1260     tty_print_char (show ? rotating_dash[pos] : ' ');
1261     pos = (pos + 1) % sizeof (rotating_dash);
1262     mc_refresh ();
1263 }
1264 
1265 /* --------------------------------------------------------------------------------------------- */
1266 
1267 static int
1268 do_search (WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
1269 {
1270     static struct vfs_dirent *dp = NULL;
1271     static DIR *dirp = NULL;
1272     static char *directory = NULL;
1273     struct stat tmp_stat;
1274     gsize bytes_found;
1275     unsigned short count;
1276 
1277     if (h == NULL)
1278     {                           /* someone forces me to close dirp */
1279         if (dirp != NULL)
1280         {
1281             mc_closedir (dirp);
1282             dirp = NULL;
1283         }
1284         MC_PTR_FREE (directory);
1285         dp = NULL;
1286         return 1;
1287     }
1288 
1289     for (count = 0; count < 32; count++)
1290     {
1291         while (dp == NULL)
1292         {
1293             if (dirp != NULL)
1294             {
1295                 mc_closedir (dirp);
1296                 dirp = NULL;
1297             }
1298 
1299             while (dirp == NULL)
1300             {
1301                 vfs_path_t *tmp_vpath = NULL;
1302 
1303                 tty_setcolor (REVERSE_COLOR);
1304 
1305                 while (TRUE)
1306                 {
1307                     tmp_vpath = pop_directory ();
1308                     if (tmp_vpath == NULL)
1309                     {
1310                         running = FALSE;
1311                         if (ignore_count == 0)
1312                             status_update (_("Finished"));
1313                         else
1314                         {
1315                             char msg[BUF_SMALL];
1316 
1317                             g_snprintf (msg, sizeof (msg),
1318                                         ngettext ("Finished (ignored %zu directory)",
1319                                                   "Finished (ignored %zu directories)",
1320                                                   ignore_count), ignore_count);
1321                             status_update (msg);
1322                         }
1323                         if (verbose)
1324                             find_rotate_dash (h, FALSE);
1325                         stop_idle (h);
1326                         return 0;
1327                     }
1328 
1329                     /* handle absolute ignore dirs here */
1330                     {
1331                         gboolean ok;
1332 
1333                         ok = find_ignore_dir_search (vfs_path_as_str (tmp_vpath));
1334                         if (!ok)
1335                             break;
1336                     }
1337 
1338                     vfs_path_free (tmp_vpath, TRUE);
1339                     ignore_count++;
1340                 }
1341 
1342                 g_free (directory);
1343                 directory = g_strdup (vfs_path_as_str (tmp_vpath));
1344 
1345                 if (verbose)
1346                 {
1347                     char buffer[BUF_MEDIUM];
1348 
1349                     g_snprintf (buffer, sizeof (buffer), _("Searching %s"), directory);
1350                     status_update (str_trunc (directory, WIDGET (h)->rect.cols - 8));
1351                 }
1352 
1353                 dirp = mc_opendir (tmp_vpath);
1354                 vfs_path_free (tmp_vpath, TRUE);
1355             }                   /* while (!dirp) */
1356 
1357             /* skip invalid filenames */
1358             while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1359                 ;
1360         }                       /* while (!dp) */
1361 
1362         if (DIR_IS_DOT (dp->d_name) || DIR_IS_DOTDOT (dp->d_name))
1363         {
1364             /* skip invalid filenames */
1365             while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1366                 ;
1367 
1368             return 1;
1369         }
1370 
1371         if (!(options.skip_hidden && (dp->d_name[0] == '.')))
1372         {
1373             gboolean search_ok;
1374 
1375             if (options.find_recurs && (directory != NULL))
1376             {                   /* Can directory be NULL ? */
1377                 /* handle relative ignore dirs here */
1378                 if (options.ignore_dirs_enable && find_ignore_dir_search (dp->d_name))
1379                     ignore_count++;
1380                 else
1381                 {
1382                     vfs_path_t *tmp_vpath;
1383                     int stat_res;
1384 
1385                     tmp_vpath = vfs_path_build_filename (directory, dp->d_name, (char *) NULL);
1386 
1387                     if (options.follow_symlinks)
1388                         stat_res = mc_stat (tmp_vpath, &tmp_stat);
1389                     else
1390                         stat_res = mc_lstat (tmp_vpath, &tmp_stat);
1391 
1392                     if (stat_res == 0 && S_ISDIR (tmp_stat.st_mode))
1393                         push_directory (tmp_vpath);
1394                     else
1395                         vfs_path_free (tmp_vpath, TRUE);
1396                 }
1397             }
1398 
1399             search_ok = mc_search_run (search_file_handle, dp->d_name,
1400                                        0, strlen (dp->d_name), &bytes_found);
1401 
1402             if (search_ok)
1403             {
1404                 if (content_pattern == NULL)
1405                     find_add_match (directory, dp->d_name, 0, 0);
1406                 else if (search_content (h, directory, dp->d_name))
1407                     return 1;
1408             }
1409         }
1410 
1411         /* skip invalid filenames */
1412         while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
1413             ;
1414     }                           /* for */
1415 
1416     if (verbose)
1417         find_rotate_dash (h, TRUE);
1418 
1419     return 1;
1420 }
1421 
1422 /* --------------------------------------------------------------------------------------------- */
1423 
1424 static void
1425 init_find_vars (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1426 {
1427     MC_PTR_FREE (old_dir);
1428     matches = 0;
1429     ignore_count = 0;
1430 
1431     /* Remove all the items from the stack */
1432     clear_stack ();
1433 
1434     g_strfreev (find_ignore_dirs);
1435     find_ignore_dirs = NULL;
1436 }
1437 
1438 /* --------------------------------------------------------------------------------------------- */
1439 
1440 static void
1441 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]  */
1442                    off_t search_end)
1443 {
1444     const char *filename = NULL;
1445     int line;
1446     vfs_path_t *fullname_vpath;
1447 
1448     if (content_pattern != NULL)
1449     {
1450         filename = strchr (file + 4, ':') + 1;
1451         line = atoi (file + 4);
1452     }
1453     else
1454     {
1455         filename = file + 4;
1456         line = 0;
1457     }
1458 
1459     fullname_vpath = vfs_path_build_filename (dir, filename, (char *) NULL);
1460     if (edit)
1461         edit_file_at_line (fullname_vpath, use_internal_edit, line);
1462     else
1463         view_file_at_line (fullname_vpath, unparsed_view, use_internal_view, line, search_start,
1464                            search_end);
1465     vfs_path_free (fullname_vpath, TRUE);
1466 }
1467 
1468 /* --------------------------------------------------------------------------------------------- */
1469 
1470 static cb_ret_t
1471 view_edit_currently_selected_file (gboolean unparsed_view, gboolean edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1472 {
1473     char *text = NULL;
1474     find_match_location_t *location;
1475 
1476     listbox_get_current (find_list, &text, (void **) &location);
1477 
1478     if ((text == NULL) || (location == NULL) || (location->dir == NULL))
1479         return MSG_NOT_HANDLED;
1480 
1481     find_do_view_edit (unparsed_view, edit, location->dir, text, location->start, location->end);
1482     return MSG_HANDLED;
1483 }
1484 
1485 /* --------------------------------------------------------------------------------------------- */
1486 
1487 static void
1488 find_calc_button_locations (const WDialog * h, gboolean all_buttons)
     /* [previous][next][first][last][top][bottom][index][help]  */
1489 {
1490     const int cols = CONST_WIDGET (h)->rect.cols;
1491 
1492     int l1, l2;
1493 
1494     l1 = fbuts[0].len + fbuts[1].len + fbuts[is_start ? 3 : 2].len + fbuts[4].len + 3;
1495     l2 = fbuts[5].len + fbuts[6].len + fbuts[7].len + 2;
1496 
1497     fbuts[0].x = (cols - l1) / 2;
1498     fbuts[1].x = fbuts[0].x + fbuts[0].len + 1;
1499     fbuts[2].x = fbuts[1].x + fbuts[1].len + 1;
1500     fbuts[3].x = fbuts[2].x;
1501     fbuts[4].x = fbuts[2].x + fbuts[is_start ? 3 : 2].len + 1;
1502 
1503     if (all_buttons)
1504     {
1505         fbuts[5].x = (cols - l2) / 2;
1506         fbuts[6].x = fbuts[5].x + fbuts[5].len + 1;
1507         fbuts[7].x = fbuts[6].x + fbuts[6].len + 1;
1508     }
1509 }
1510 
1511 /* --------------------------------------------------------------------------------------------- */
1512 
1513 static void
1514 find_adjust_header (WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
1515 {
1516     char title[BUF_MEDIUM];
1517     int title_len;
1518 
1519     if (content_pattern != NULL)
1520         g_snprintf (title, sizeof (title), _("Find File: \"%s\". Content: \"%s\""), find_pattern,
1521                     content_pattern);
1522     else
1523         g_snprintf (title, sizeof (title), _("Find File: \"%s\""), find_pattern);
1524 
1525     title_len = str_term_width1 (title);
1526     if (title_len > WIDGET (h)->rect.cols - 6)
1527     {
1528         /* title is too wide, truncate it */
1529         title_len = WIDGET (h)->rect.cols - 6;
1530         title_len = str_column_to_pos (title, title_len);
1531         title_len -= 3;         /* reserve space for three dots */
1532         title_len = str_offset_to_pos (title, title_len);
1533         /* mark that title is truncated */
1534         memmove (title + title_len, "...", 4);
1535     }
1536 
1537     frame_set_title (FRAME (h->bg), title);
1538 }
1539 
1540 /* --------------------------------------------------------------------------------------------- */
1541 
1542 static void
1543 find_relocate_buttons (const WDialog * h, gboolean all_buttons)
     /* [previous][next][first][last][top][bottom][index][help]  */
1544 {
1545     size_t i;
1546 
1547     find_calc_button_locations (h, all_buttons);
1548 
1549     for (i = 0; i < fbuts_num; i++)
1550         fbuts[i].button->rect.x = CONST_WIDGET (h)->rect.x + fbuts[i].x;
1551 }
1552 
1553 /* --------------------------------------------------------------------------------------------- */
1554 
1555 static cb_ret_t
1556 find_resize (WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
1557 {
1558     Widget *w = WIDGET (h);
1559     WRect r = w->rect;
1560 
1561     r.lines = LINES - 4;
1562     r.cols = COLS - 16;
1563     dlg_default_callback (w, NULL, MSG_RESIZE, 0, &r);
1564     find_adjust_header (h);
1565     find_relocate_buttons (h, TRUE);
1566 
1567     return MSG_HANDLED;
1568 }
1569 
1570 /* --------------------------------------------------------------------------------------------- */
1571 
1572 static cb_ret_t
1573 find_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
1574 {
1575     WDialog *h = DIALOG (w);
1576 
1577     switch (msg)
1578     {
1579     case MSG_INIT:
1580         group_default_callback (w, NULL, MSG_INIT, 0, NULL);
1581         find_adjust_header (h);
1582         return MSG_HANDLED;
1583 
1584     case MSG_KEY:
1585         if (parm == KEY_F (3) || parm == KEY_F (13))
1586         {
1587             gboolean unparsed_view = (parm == KEY_F (13));
1588 
1589             return view_edit_currently_selected_file (unparsed_view, FALSE);
1590         }
1591         if (parm == KEY_F (4))
1592             return view_edit_currently_selected_file (FALSE, TRUE);
1593         return MSG_NOT_HANDLED;
1594 
1595     case MSG_RESIZE:
1596         return find_resize (h);
1597 
1598     case MSG_IDLE:
1599         do_search (h);
1600         return MSG_HANDLED;
1601 
1602     default:
1603         return dlg_default_callback (w, sender, msg, parm, data);
1604     }
1605 }
1606 
1607 /* --------------------------------------------------------------------------------------------- */
1608 /** Handles the Stop/Start button in the find window */
1609 
1610 static int
1611 start_stop (WButton * button, int action)
     /* [previous][next][first][last][top][bottom][index][help]  */
1612 {
1613     Widget *w = WIDGET (button);
1614 
1615     (void) action;
1616 
1617     running = is_start;
1618     widget_idle (WIDGET (find_dlg), running);
1619     is_start = !is_start;
1620 
1621     status_update (is_start ? _("Stopped") : _("Searching"));
1622     button_set_text (button, fbuts[is_start ? 3 : 2].text);
1623 
1624     find_relocate_buttons (DIALOG (w->owner), FALSE);
1625     widget_draw (WIDGET (w->owner));
1626 
1627     return 0;
1628 }
1629 
1630 /* --------------------------------------------------------------------------------------------- */
1631 /** Handle view command, when invoked as a button */
1632 
1633 static int
1634 find_do_view_file (WButton * button, int action)
     /* [previous][next][first][last][top][bottom][index][help]  */
1635 {
1636     (void) button;
1637     (void) action;
1638 
1639     view_edit_currently_selected_file (FALSE, FALSE);
1640     return 0;
1641 }
1642 
1643 /* --------------------------------------------------------------------------------------------- */
1644 /** Handle edit command, when invoked as a button */
1645 
1646 static int
1647 find_do_edit_file (WButton * button, int action)
     /* [previous][next][first][last][top][bottom][index][help]  */
1648 {
1649     (void) button;
1650     (void) action;
1651 
1652     view_edit_currently_selected_file (FALSE, TRUE);
1653     return 0;
1654 }
1655 
1656 /* --------------------------------------------------------------------------------------------- */
1657 
1658 static void
1659 setup_gui (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1660 {
1661     WGroup *g;
1662     size_t i;
1663     int lines, cols;
1664     int y;
1665 
1666     static gboolean i18n_flag = FALSE;
1667 
1668     if (!i18n_flag)
1669     {
1670         for (i = 0; i < fbuts_num; i++)
1671         {
1672 #ifdef ENABLE_NLS
1673             fbuts[i].text = _(fbuts[i].text);
1674 #endif /* ENABLE_NLS */
1675             fbuts[i].len = str_term_width1 (fbuts[i].text) + 3;
1676             if (fbuts[i].flags == DEFPUSH_BUTTON)
1677                 fbuts[i].len += 2;
1678         }
1679 
1680         i18n_flag = TRUE;
1681     }
1682 
1683     lines = LINES - 4;
1684     cols = COLS - 16;
1685 
1686     find_dlg =
1687         dlg_create (TRUE, 0, 0, lines, cols, WPOS_CENTER, FALSE, dialog_colors, find_callback, NULL,
1688                     "[Find File]", NULL);
1689     g = GROUP (find_dlg);
1690 
1691     find_calc_button_locations (find_dlg, TRUE);
1692 
1693     y = 2;
1694     find_list = listbox_new (y, 2, lines - 10, cols - 4, FALSE, NULL);
1695     group_add_widget_autopos (g, find_list, WPOS_KEEP_ALL, NULL);
1696     y += WIDGET (find_list)->rect.lines;
1697 
1698     group_add_widget_autopos (g, hline_new (y++, -1, -1), WPOS_KEEP_BOTTOM, NULL);
1699 
1700     found_num_label = label_new (y++, 4, "");
1701     group_add_widget_autopos (g, found_num_label, WPOS_KEEP_BOTTOM, NULL);
1702 
1703     status_label = label_new (y++, 4, _("Searching"));
1704     group_add_widget_autopos (g, status_label, WPOS_KEEP_BOTTOM, NULL);
1705 
1706     group_add_widget_autopos (g, hline_new (y++, -1, -1), WPOS_KEEP_BOTTOM, NULL);
1707 
1708     for (i = 0; i < fbuts_num; i++)
1709     {
1710         if (i == 3)
1711             fbuts[3].button = fbuts[2].button;
1712         else
1713         {
1714             fbuts[i].button =
1715                 WIDGET (button_new
1716                         (y, fbuts[i].x, fbuts[i].ret_cmd, fbuts[i].flags, fbuts[i].text,
1717                          fbuts[i].callback));
1718             group_add_widget_autopos (g, fbuts[i].button, WPOS_KEEP_BOTTOM, NULL);
1719         }
1720 
1721         if (i == quit_button)
1722             y++;
1723     }
1724 
1725     widget_select (WIDGET (find_list));
1726 }
1727 
1728 /* --------------------------------------------------------------------------------------------- */
1729 
1730 static int
1731 run_process (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1732 {
1733     int ret;
1734 
1735     search_content_handle = mc_search_new (content_pattern, NULL);
1736     if (search_content_handle)
1737     {
1738         search_content_handle->search_type =
1739             options.content_regexp ? MC_SEARCH_T_REGEX : MC_SEARCH_T_NORMAL;
1740         search_content_handle->is_case_sensitive = options.content_case_sens;
1741         search_content_handle->whole_words = options.content_whole_words;
1742 #ifdef HAVE_CHARSET
1743         search_content_handle->is_all_charsets = options.content_all_charsets;
1744 #endif
1745     }
1746     search_file_handle = mc_search_new (find_pattern, NULL);
1747     search_file_handle->search_type = options.file_pattern ? MC_SEARCH_T_GLOB : MC_SEARCH_T_REGEX;
1748     search_file_handle->is_case_sensitive = options.file_case_sens;
1749 #ifdef HAVE_CHARSET
1750     search_file_handle->is_all_charsets = options.file_all_charsets;
1751 #endif
1752     search_file_handle->is_entire_line = options.file_pattern;
1753 
1754     resuming = FALSE;
1755 
1756     widget_idle (WIDGET (find_dlg), TRUE);
1757     ret = dlg_run (find_dlg);
1758 
1759     mc_search_free (search_file_handle);
1760     search_file_handle = NULL;
1761     mc_search_free (search_content_handle);
1762     search_content_handle = NULL;
1763 
1764     return ret;
1765 }
1766 
1767 /* --------------------------------------------------------------------------------------------- */
1768 
1769 static void
1770 kill_gui (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1771 {
1772     Widget *w = WIDGET (find_dlg);
1773 
1774     widget_idle (w, FALSE);
1775     widget_destroy (w);
1776 }
1777 
1778 /* --------------------------------------------------------------------------------------------- */
1779 
1780 static int
1781 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]  */
1782          char **dirname, char **filename)
1783 {
1784     int return_value = 0;
1785     char *dir_tmp = NULL, *file_tmp = NULL;
1786 
1787     setup_gui ();
1788 
1789     init_find_vars ();
1790     parse_ignore_dirs (ignore_dirs);
1791     push_directory (vfs_path_from_str (start_dir));
1792 
1793     return_value = run_process ();
1794 
1795     /* Clear variables */
1796     init_find_vars ();
1797 
1798     get_list_info (&file_tmp, &dir_tmp, NULL, NULL);
1799 
1800     if (dir_tmp)
1801         *dirname = g_strdup (dir_tmp);
1802     if (file_tmp)
1803         *filename = g_strdup (file_tmp);
1804 
1805     if (return_value == B_PANELIZE && *filename)
1806     {
1807         int i;
1808         struct stat st;
1809         GList *entry;
1810         dir_list *list = &panel->dir;
1811         char *name = NULL;
1812 
1813         panel_clean_dir (panel);
1814         dir_list_init (list);
1815 
1816         for (i = 0, entry = listbox_get_first_link (find_list); entry != NULL;
1817              i++, entry = g_list_next (entry))
1818         {
1819             const char *lc_filename = NULL;
1820             WLEntry *le = LENTRY (entry->data);
1821             find_match_location_t *location = le->data;
1822             char *p;
1823             gboolean link_to_dir, stale_link;
1824 
1825             if ((le->text == NULL) || (location == NULL) || (location->dir == NULL))
1826                 continue;
1827 
1828             if (!content_is_empty)
1829                 lc_filename = strchr (le->text + 4, ':') + 1;
1830             else
1831                 lc_filename = le->text + 4;
1832 
1833             name = mc_build_filename (location->dir, lc_filename, (char *) NULL);
1834             /* skip initial start dir */
1835             if (start_dir_len < 0)
1836                 p = name;
1837             else
1838             {
1839                 p = name + (size_t) start_dir_len;
1840                 if (IS_PATH_SEP (*p))
1841                     p++;
1842             }
1843 
1844             if (!handle_path (p, &st, &link_to_dir, &stale_link))
1845             {
1846                 g_free (name);
1847                 continue;
1848             }
1849             /* Need to grow the *list? */
1850             if (list->len == list->size && !dir_list_grow (list, DIR_LIST_RESIZE_STEP))
1851             {
1852                 g_free (name);
1853                 break;
1854             }
1855 
1856             /* don't add files more than once to the panel */
1857             if (!content_is_empty && list->len != 0
1858                 && strcmp (list->list[list->len - 1].fname->str, p) == 0)
1859             {
1860                 g_free (name);
1861                 continue;
1862             }
1863 
1864             list->list[list->len].fname = g_string_new (p);
1865             list->list[list->len].f.marked = 0;
1866             list->list[list->len].f.link_to_dir = link_to_dir ? 1 : 0;
1867             list->list[list->len].f.stale_link = stale_link ? 1 : 0;
1868             list->list[list->len].f.dir_size_computed = 0;
1869             list->list[list->len].st = st;
1870             list->list[list->len].sort_key = NULL;
1871             list->list[list->len].second_sort_key = NULL;
1872             list->len++;
1873             g_free (name);
1874             if ((list->len & 15) == 0)
1875                 rotate_dash (TRUE);
1876         }
1877 
1878         panel->is_panelized = TRUE;
1879         panelize_absolutize_if_needed (panel);
1880         panelize_save_panel (panel);
1881     }
1882 
1883     kill_gui ();
1884     do_search (NULL);           /* force do_search to release resources */
1885     MC_PTR_FREE (old_dir);
1886     rotate_dash (FALSE);
1887 
1888     return return_value;
1889 }
1890 
1891 /* --------------------------------------------------------------------------------------------- */
1892 /*** public functions ****************************************************************************/
1893 /* --------------------------------------------------------------------------------------------- */
1894 
1895 void
1896 find_cmd (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
1897 {
1898     char *start_dir = NULL, *ignore_dirs = NULL;
1899     ssize_t start_dir_len;
1900 
1901     find_pattern = NULL;
1902     content_pattern = NULL;
1903 
1904     while (find_parameters (panel, &start_dir, &start_dir_len,
1905                             &ignore_dirs, &find_pattern, &content_pattern))
1906     {
1907         char *filename = NULL, *dirname = NULL;
1908         int v = B_CANCEL;
1909 
1910         content_is_empty = content_pattern == NULL;
1911 
1912         if (find_pattern[0] != '\0')
1913         {
1914             last_refresh = 0;
1915 
1916             is_start = FALSE;
1917 
1918             if (!content_is_empty && !str_is_valid_string (content_pattern))
1919                 MC_PTR_FREE (content_pattern);
1920 
1921             v = do_find (panel, start_dir, start_dir_len, ignore_dirs, &dirname, &filename);
1922         }
1923 
1924         g_free (start_dir);
1925         g_free (ignore_dirs);
1926         MC_PTR_FREE (find_pattern);
1927 
1928         if (v == B_ENTER)
1929         {
1930             if (dirname != NULL)
1931             {
1932                 vfs_path_t *dirname_vpath;
1933 
1934                 dirname_vpath = vfs_path_from_str (dirname);
1935                 panel_cd (panel, dirname_vpath, cd_exact);
1936                 vfs_path_free (dirname_vpath, TRUE);
1937                 if (filename != NULL)
1938                     try_to_select (panel,
1939                                    filename + (content_pattern != NULL
1940                                                ? strchr (filename + 4, ':') - filename + 1 : 4));
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             try_to_select (panel, NULL);
1963             break;
1964         }
1965     }
1966 }
1967 
1968 /* --------------------------------------------------------------------------------------------- */

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