root/src/filemanager/find.c

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

DEFINITIONS

This source file includes following definitions.
  1. max
  2. parse_ignore_dirs
  3. find_load_options
  4. find_save_options
  5. add_to_list
  6. add_to_list_take
  7. stop_idle
  8. status_update
  9. found_num_update
  10. get_list_info
  11. find_check_regexp
  12. find_toggle_enable_ignore_dirs
  13. find_toggle_enable_params
  14. find_toggle_enable_content
  15. find_parm_callback
  16. find_parameters
  17. push_directory
  18. pop_directory
  19. queue_dir_free
  20. clear_stack
  21. insert_file
  22. find_add_match
  23. check_find_events
  24. search_content
  25. find_ignore_dir_search
  26. find_rotate_dash
  27. do_search
  28. init_find_vars
  29. find_do_view_edit
  30. view_edit_currently_selected_file
  31. find_calc_button_locations
  32. find_adjust_header
  33. find_relocate_buttons
  34. find_resize
  35. find_callback
  36. start_stop
  37. find_do_view_file
  38. find_do_edit_file
  39. setup_gui
  40. run_process
  41. kill_gui
  42. do_find
  43. find_cmd

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

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