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. clear_stack
  19. insert_file
  20. find_add_match
  21. check_find_events
  22. search_content
  23. find_ignore_dir_search
  24. find_rotate_dash
  25. do_search
  26. init_find_vars
  27. find_do_view_edit
  28. view_edit_currently_selected_file
  29. find_calc_button_locations
  30. find_adjust_header
  31. find_relocate_buttons
  32. find_resize
  33. find_callback
  34. start_stop
  35. find_do_view_file
  36. find_do_edit_file
  37. setup_gui
  38. run_process
  39. kill_gui
  40. do_find
  41. find_cmd

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

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