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

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