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_file

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

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