Editor: sync with new global config location (user menu and syntax files).
[midnight-commander.git] / src / find.c
blob6ea2e6817103804d688940234fdcedc9ff44f3da
1 /* Find file command for the Midnight Commander
2 Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
3 2006, 2007 Free Software Foundation, Inc.
4 Written 1995 by Miguel de Icaza
6 Complete rewrote.
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
22 /** \file find.c
23 * \brief Source: Find file command
26 #include <config.h>
28 #include <ctype.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <sys/stat.h>
34 #include "global.h"
35 #include "tty.h"
36 #include "win.h"
37 #include "color.h"
38 #include "setup.h"
39 #include "find.h"
40 #include "strutil.h"
42 /* Dialog manager and widgets */
43 #include "dialog.h"
44 #include "widget.h"
46 #include "dir.h"
47 #include "panel.h" /* current_panel */
48 #include "main.h" /* do_cd, try_to_select */
49 #include "wtools.h"
50 #include "cmd.h" /* view_file_at_line */
51 #include "boxes.h"
52 #include "key.h"
53 #include "../src/search/search.h"
55 /* Size of the find parameters window */
56 #define FIND_Y 16
57 static int FIND_X = 60;
59 /* Size of the find window */
60 #define FIND2_Y (LINES - 4)
62 static int FIND2_X = 64;
63 #define FIND2_X_USE (FIND2_X - 20)
65 /* A couple of extra messages we need */
66 enum {
67 B_STOP = B_USER + 1,
68 B_AGAIN,
69 B_PANELIZE,
70 B_TREE,
71 B_VIEW
74 typedef enum {
75 FIND_CONT,
76 FIND_SUSPEND,
77 FIND_ABORT
78 } FindProgressStatus;
80 /* List of directories to be ignored, separated by ':' */
81 char *find_ignore_dirs = 0;
83 static WInput *in_start; /* Start path */
84 static WInput *in_name; /* Pattern to search */
85 static WInput *in_with; /* Text inside filename */
86 static WCheck *case_sense; /* "case sensitive" checkbox */
87 static WCheck *find_regex_cbox; /* [x] find regular expression */
89 static int running = 0; /* nice flag */
90 static char *find_pattern; /* Pattern to search */
91 static char *content_pattern; /* pattern to search inside files; if
92 content_regexp_flag is true, it contains the
93 regex pattern, else the search string. */
94 static int count; /* Number of files displayed */
95 static int matches; /* Number of matches */
96 static int is_start; /* Status of the start/stop toggle button */
97 static char *old_dir;
99 /* Where did we stop */
100 static int resuming;
101 static int last_line;
102 static int last_pos;
104 static Dlg_head *find_dlg; /* The dialog */
106 static WButton *stop_button; /* pointer to the stop button */
107 static WLabel *status_label; /* Finished, Searching etc. */
108 static WListbox *find_list; /* Listbox with the file list */
110 /* This keeps track of the directory stack */
111 typedef struct dir_stack {
112 char *name;
113 struct dir_stack *prev;
114 } dir_stack ;
116 static dir_stack *dir_stack_base = 0;
118 static struct {
119 const char* text;
120 int len; /* length including space and brackets */
121 int x;
122 } fbuts [] = {
123 { N_("&Suspend"), 11, 29 },
124 { N_("Con&tinue"), 12, 29 },
125 { N_("&Chdir"), 11, 3 },
126 { N_("&Again"), 9, 17 },
127 { N_("&Quit"), 8, 43 },
128 { N_("Pane&lize"), 12, 3 },
129 { N_("&View - F3"), 13, 20 },
130 { N_("&Edit - F4"), 13, 38 }
133 static inline char * add_to_list (const char *text, void *data) {
134 return listbox_add_item (find_list, 0, 0, text, data);
136 static inline void stop_idle (void *data) {
137 set_idle_proc (data, 0);
139 static inline void status_update (const char *text) {
140 label_set_text (status_label, text);
142 static void get_list_info (char **file, char **dir) {
143 listbox_get_current (find_list, file, dir);
146 static mc_search_t *search_file_handle = NULL;
147 static gboolean skip_hidden_flag = FALSE;
148 static gboolean file_regexp_flag = FALSE;
149 static gboolean file_all_charsets_flag = FALSE;
150 static gboolean file_case_sentitive = TRUE;
152 static mc_search_t *search_content_handle = NULL;
153 static gboolean content_regexp_flag = FALSE;
154 static gboolean content_all_charsets_flag = FALSE;
155 static gboolean content_case_sensitive = TRUE;
157 static int find_recursively = 1;
160 * Callback for the parameter dialog.
161 * Validate regex, prevent closing the dialog if it's invalid.
163 static cb_ret_t
164 find_parm_callback (struct Dlg_head *h, dlg_msg_t msg, int parm)
166 switch (msg) {
167 case DLG_VALIDATE:
168 if ((h->ret_value != B_ENTER) || !in_with->buffer[0]
169 || !(find_regex_cbox->state & C_BOOL))
170 return MSG_HANDLED;
176 if (regcomp (r, in_with->buffer, flags)) {
177 message (D_ERROR, MSG_ERROR, _(" Malformed regular expression "));
178 dlg_select_widget (in_with);
179 h->running = 1; // Don't stop the dialog
182 return MSG_HANDLED;
184 default:
185 return default_dlg_callback (h, msg, parm);
190 * find_parameters: gets information from the user
192 * If the return value is true, then the following holds:
194 * START_DIR and PATTERN are pointers to char * and upon return they
195 * contain the information provided by the user.
197 * CONTENT holds a strdup of the contents specified by the user if he
198 * asked for them or 0 if not (note, this is different from the
199 * behavior for the other two parameters.
202 static int
203 find_parameters (char **start_dir, char **pattern, char **content)
205 int return_value;
206 char *temp_dir;
207 static const char *case_label = N_("case &Sensitive");
208 static const char *recurs_label = N_("&Find recursively");
209 static const char *skip_hidden_label = N_("S&kip hidden");
210 static const char *all_charsets_label = N_("All charsets");
211 static const char *regexp_label = N_("&Regular expression");
212 static const char *file_regexp_label = N_("Regular expression");
214 WCheck *recursively_cbox;
215 WCheck *file_regexp_cbox;
216 WCheck *skip_hidden_cbox;
217 #ifdef HAVE_CHARSET
218 WCheck *file_all_charsets_cbox;
219 WCheck *content_all_charsets_cbox;
220 #endif
222 static char *in_contents = NULL;
223 static char *in_start_dir = NULL;
224 static char *in_start_name = NULL;
226 static const char *labs[] =
227 { N_("Start at:"), N_("Filename:"), N_("Content: ") };
228 static const char *buts[] = { N_("&OK"), N_("&Tree"), N_("&Cancel") };
229 static int ilen = 30, istart = 14;
230 static int b0 = 3, b1 = 16, b2 = 36;
232 #ifdef ENABLE_NLS
233 static int i18n_flag = 0;
235 if (!i18n_flag) {
236 register int i = sizeof (labs) / sizeof (labs[0]);
237 int l1, maxlen = 0;
239 while (i--) {
240 l1 = str_term_width1 (labs[i] = _(labs[i]));
241 if (l1 > maxlen)
242 maxlen = l1;
244 i = maxlen + ilen + 7;
245 if (i > FIND_X)
246 FIND_X = i;
248 for (i = sizeof (buts) / sizeof (buts[0]), l1 = 0; i--;) {
249 l1 += str_term_width1 (buts[i] = _(buts[i]));
251 l1 += 21;
252 if (l1 > FIND_X)
253 FIND_X = l1;
255 ilen = FIND_X - 7 - maxlen; /* for the case of very long buttons :) */
256 istart = FIND_X - 3 - ilen;
258 b1 = b0 + str_term_width1 (buts[0]) + 7;
259 b2 = FIND_X - (str_term_width1 (buts[2]) + 6);
261 i18n_flag = 1;
262 case_label = _(case_label);
263 recurs_label = _(recurs_label);
265 #endif /* ENABLE_NLS */
267 find_par_start:
268 if (!in_start_dir)
269 in_start_dir = g_strdup (".");
270 if (!in_start_name)
271 in_start_name = g_strdup (easy_patterns ? "*" : ".");
272 if (!in_contents)
273 in_contents = g_strdup ("");
275 find_dlg =
276 create_dlg (0, 0, FIND_Y, FIND_X, dialog_colors,
277 find_parm_callback, "[Find File]", _("Find File"),
278 DLG_CENTER | DLG_REVERSE);
280 add_widget (find_dlg,
281 button_new (FIND_Y - 3, b2, B_CANCEL, NORMAL_BUTTON, buts[2], 0));
282 add_widget (find_dlg,
283 button_new (FIND_Y - 3, b1, B_TREE, NORMAL_BUTTON, buts[1], 0));
284 add_widget (find_dlg,
285 button_new (FIND_Y - 3, b0, B_ENTER, DEFPUSH_BUTTON, buts[0], 0));
287 file_regexp_cbox = check_new (6, 3, file_regexp_flag, file_regexp_label);
288 recursively_cbox = check_new (7, 3, find_recursively, recurs_label);
289 skip_hidden_cbox = check_new (6, 33, skip_hidden_flag, skip_hidden_label);
290 #ifdef HAVE_CHARSET
291 file_all_charsets_cbox = check_new (7, 33, file_all_charsets_flag, all_charsets_label);
292 #endif
294 find_regex_cbox = check_new (11, 3, content_regexp_flag, regexp_label);
295 case_sense = check_new (10, 3, content_case_sensitive, case_label);
296 #ifdef HAVE_CHARSET
297 content_all_charsets_cbox = check_new (10, 33, content_all_charsets_flag, all_charsets_label);
298 #endif
300 in_with = input_new (9, istart, INPUT_COLOR, ilen, in_contents, "content", INPUT_COMPLETE_DEFAULT);
301 in_name = input_new (5, istart, INPUT_COLOR, ilen, in_start_name, "name", INPUT_COMPLETE_DEFAULT);
302 in_start = input_new (3, istart, INPUT_COLOR, ilen, in_start_dir, "start", INPUT_COMPLETE_DEFAULT);
304 #ifdef HAVE_CHARSET
305 add_widget (find_dlg, content_all_charsets_cbox);
306 #endif
307 add_widget (find_dlg, find_regex_cbox);
308 add_widget (find_dlg, case_sense);
309 add_widget (find_dlg, in_with);
310 #ifdef HAVE_CHARSET
311 add_widget (find_dlg, file_all_charsets_cbox);
312 #endif
313 add_widget (find_dlg, skip_hidden_cbox);
314 add_widget (find_dlg, recursively_cbox);
315 add_widget (find_dlg, file_regexp_cbox);
316 add_widget (find_dlg, in_name);
317 add_widget (find_dlg, in_start);
319 add_widget (find_dlg, label_new (9, 3, labs[2]));
320 add_widget (find_dlg, label_new (5, 3, labs[1]));
321 add_widget (find_dlg, label_new (3, 3, labs[0]));
323 dlg_select_widget (in_name);
325 run_dlg (find_dlg);
327 switch (find_dlg->ret_value) {
328 case B_CANCEL:
329 return_value = 0;
330 break;
332 case B_TREE:
333 temp_dir = g_strdup (in_start->buffer);
334 #ifdef HAVE_CHARSET
335 file_all_charsets_flag = file_all_charsets_cbox->state & C_BOOL;
336 content_all_charsets_flag = content_all_charsets_cbox->state & C_BOOL;
337 #endif
338 content_case_sensitive = case_sense->state & C_BOOL;
339 content_regexp_flag = find_regex_cbox->state & C_BOOL;
340 file_regexp_flag = file_regexp_cbox->state & C_BOOL;
341 find_recursively = recursively_cbox->state & C_BOOL;
342 skip_hidden_flag = skip_hidden_cbox->state & C_BOOL;
343 destroy_dlg (find_dlg);
344 g_free (in_start_dir);
345 if (strcmp (temp_dir, ".") == 0) {
346 g_free (temp_dir);
347 temp_dir = g_strdup (current_panel->cwd);
349 in_start_dir = tree_box (temp_dir);
350 if (in_start_dir)
351 g_free (temp_dir);
352 else
353 in_start_dir = temp_dir;
354 /* Warning: Dreadful goto */
355 goto find_par_start;
356 break;
358 default:
359 g_free (in_contents);
360 if (in_with->buffer[0]) {
361 *content = g_strdup (in_with->buffer);
362 in_contents = g_strdup (*content);
363 } else {
364 *content = in_contents = NULL;
366 #ifdef HAVE_CHARSET
367 file_all_charsets_flag = file_all_charsets_cbox->state & C_BOOL;
368 content_all_charsets_flag = content_all_charsets_cbox->state & C_BOOL;
369 #endif
370 content_case_sensitive = case_sense->state & C_BOOL;
371 content_regexp_flag = find_regex_cbox->state & C_BOOL;
372 find_recursively = recursively_cbox->state & C_BOOL;
373 file_regexp_flag = file_regexp_cbox->state & C_BOOL;
374 skip_hidden_flag = skip_hidden_cbox->state & C_BOOL;
375 return_value = 1;
376 *start_dir = g_strdup (in_start->buffer);
377 *pattern = g_strdup (in_name->buffer);
379 g_free (in_start_dir);
380 in_start_dir = g_strdup (*start_dir);
381 g_free (in_start_name);
382 in_start_name = g_strdup (*pattern);
385 destroy_dlg (find_dlg);
387 return return_value;
390 static void
391 push_directory (const char *dir)
393 dir_stack *new;
395 new = g_new (dir_stack, 1);
396 new->name = concat_dir_and_file (dir, "");
397 new->prev = dir_stack_base;
398 dir_stack_base = new;
401 static char*
402 pop_directory (void)
404 char *name;
405 dir_stack *next;
407 if (dir_stack_base){
408 name = dir_stack_base->name;
409 next = dir_stack_base->prev;
410 g_free (dir_stack_base);
411 dir_stack_base = next;
412 return name;
413 } else
414 return 0;
417 static void
418 insert_file (const char *dir, const char *file)
420 char *tmp_name;
421 static char *dirname;
423 while (dir [0] == PATH_SEP && dir [1] == PATH_SEP)
424 dir++;
426 if (old_dir){
427 if (strcmp (old_dir, dir)){
428 g_free (old_dir);
429 old_dir = g_strdup (dir);
430 dirname = add_to_list (dir, NULL);
432 } else {
433 old_dir = g_strdup (dir);
434 dirname = add_to_list (dir, NULL);
437 tmp_name = g_strconcat (" ", file, (char *) NULL);
438 add_to_list (tmp_name, dirname);
439 g_free (tmp_name);
442 static void
443 find_add_match (Dlg_head *h, const char *dir, const char *file)
445 int p = ++matches & 7;
447 (void) h;
449 insert_file (dir, file);
451 /* Scroll nicely */
452 if (!p)
453 listbox_select_last (find_list, 1);
454 else
455 listbox_select_last (find_list, 0);
456 /* Updates the current listing */
457 send_message (&find_list->widget, WIDGET_DRAW, 0);
458 if (p == 7)
459 mc_refresh ();
463 * get_line_at:
465 * Returns malloced null-terminated line from file file_fd.
466 * Input is buffered in buf_size long buffer.
467 * Current pos in buf is stored in pos.
468 * n_read - number of read chars.
469 * has_newline - is there newline ?
471 static char *
472 get_line_at (int file_fd, char *buf, int *pos, int *n_read, int buf_size,
473 int *has_newline)
475 char *buffer = 0;
476 int buffer_size = 0;
477 char ch = 0;
478 int i = 0;
480 for (;;) {
481 if (*pos >= *n_read) {
482 *pos = 0;
483 if ((*n_read = mc_read (file_fd, buf, buf_size)) <= 0)
484 break;
487 ch = buf[(*pos)++];
488 if (ch == 0) {
489 /* skip possible leading zero(s) */
490 if (i == 0)
491 continue;
492 else
493 break;
496 if (i >= buffer_size - 1) {
497 buffer = g_realloc (buffer, buffer_size += 80);
499 /* Strip newline */
500 if (ch == '\n')
501 break;
503 buffer[i++] = ch;
506 *has_newline = ch ? 1 : 0;
508 if (buffer) {
509 buffer[i] = 0;
512 return buffer;
515 static FindProgressStatus
516 check_find_events(Dlg_head *h)
518 Gpm_Event event;
519 int c;
521 c = get_event (&event, h->mouse_status == MOU_REPEAT, 0);
522 if (c != EV_NONE) {
523 dlg_process_event (h, c, &event);
524 if (h->ret_value == B_ENTER
525 || h->ret_value == B_CANCEL
526 || h->ret_value == B_AGAIN
527 || h->ret_value == B_PANELIZE) {
528 /* dialog terminated */
529 return FIND_ABORT;
531 if (!(h->flags & DLG_WANT_IDLE)) {
532 /* searching suspended */
533 return FIND_SUSPEND;
537 return FIND_CONT;
541 * search_content:
543 * Search the global (FIXME) regexp compiled content_pattern string in the
544 * DIRECTORY/FILE. It will add the found entries to the find listbox.
546 * returns 0 if do_search should look for another file
547 * 1 if do_search should exit and proceed to the event handler
549 static int
550 search_content (Dlg_head *h, const char *directory, const char *filename)
552 struct stat s;
553 char buffer [BUF_4K];
554 char *fname;
555 int file_fd;
556 int ret_val = 0;
558 fname = concat_dir_and_file (directory, filename);
560 if (mc_stat (fname, &s) != 0 || !S_ISREG (s.st_mode)){
561 g_free (fname);
562 return 0;
565 file_fd = mc_open (fname, O_RDONLY);
566 g_free (fname);
568 if (file_fd == -1)
569 return 0;
571 g_snprintf (buffer, sizeof (buffer), _("Grepping in %s"), str_trunc (filename, FIND2_X_USE));
573 status_update (buffer);
574 mc_refresh ();
576 enable_interrupt_key ();
577 got_interrupt ();
580 int line = 1;
581 int pos = 0;
582 int n_read = 0;
583 int has_newline;
584 char *p;
585 gboolean found = FALSE;
586 gsize founded_len;
588 if (resuming) {
589 /* We've been previously suspended, start from the previous position */
590 resuming = 0;
591 line = last_line;
592 pos = last_pos;
594 while ((p = get_line_at (file_fd, buffer, &pos, &n_read, sizeof (buffer), &has_newline)) && (ret_val == 0)){
595 if (found == FALSE){ /* Search in binary line once */
596 if (mc_search_run (search_content_handle, (const void *) p, 0, strlen(p), &founded_len))
598 char *fnd_info = g_strdup_printf ("%d:%s", line, filename);
599 find_add_match (h, directory, fnd_info);
600 found = TRUE;
603 if (has_newline){
604 line++;
605 found = FALSE;
607 g_free (p);
609 if ((line & 0xff) == 0) {
610 FindProgressStatus res;
611 res = check_find_events(h);
612 switch (res) {
613 case FIND_ABORT:
614 stop_idle (h);
615 ret_val = 1;
616 break;
617 case FIND_SUSPEND:
618 resuming = 1;
619 last_line = line;
620 last_pos = pos;
621 ret_val = 1;
622 break;
623 default:
624 break;
630 disable_interrupt_key ();
631 mc_close (file_fd);
632 return ret_val;
635 static int
636 do_search (struct Dlg_head *h)
638 static struct dirent *dp = 0;
639 static DIR *dirp = 0;
640 static char *directory;
641 struct stat tmp_stat;
642 static int pos;
643 static int subdirs_left = 0;
644 gsize bytes_found;
646 if (!h) { /* someone forces me to close dirp */
647 if (dirp) {
648 mc_closedir (dirp);
649 dirp = 0;
651 g_free (directory);
652 directory = NULL;
653 dp = 0;
654 return 1;
657 search_content_handle = mc_search_new(content_pattern, -1);
658 if (search_content_handle)
660 search_content_handle->search_type = (content_regexp_flag) ? MC_SEARCH_T_REGEX : MC_SEARCH_T_NORMAL;
661 search_content_handle->is_case_sentitive = content_case_sensitive;
662 search_content_handle->is_all_charsets = content_all_charsets_flag;
664 search_file_handle = mc_search_new(find_pattern, -1);
665 search_file_handle->search_type = (file_regexp_flag) ? MC_SEARCH_T_REGEX : MC_SEARCH_T_GLOB;
666 search_file_handle->is_case_sentitive = file_case_sentitive;
667 search_file_handle->is_all_charsets = file_all_charsets_flag;
668 search_file_handle->is_entire_line = !file_regexp_flag;
670 do_search_begin:
671 while (!dp){
673 if (dirp){
674 mc_closedir (dirp);
675 dirp = 0;
678 while (!dirp){
679 char *tmp;
681 attrset (REVERSE_COLOR);
682 while (1) {
683 tmp = pop_directory ();
684 if (!tmp){
685 running = 0;
686 status_update (_("Finished"));
687 stop_idle (h);
688 mc_search_free(search_file_handle);
689 search_file_handle = NULL;
690 mc_search_free(search_content_handle);
691 search_content_handle = NULL;
692 return 0;
694 if (find_ignore_dirs){
695 int found;
696 char *temp_dir = g_strconcat (":", tmp, ":", (char *) NULL);
698 found = strstr (find_ignore_dirs, temp_dir) != 0;
699 g_free (temp_dir);
700 if (found)
701 g_free (tmp);
702 else
703 break;
704 } else
705 break;
708 g_free (directory);
709 directory = tmp;
711 if (verbose){
712 char buffer [BUF_SMALL];
714 g_snprintf (buffer, sizeof (buffer), _("Searching %s"),
715 str_trunc (directory, FIND2_X_USE));
716 status_update (buffer);
718 /* mc_stat should not be called after mc_opendir
719 because vfs_s_opendir modifies the st_nlink
721 if (!mc_stat (directory, &tmp_stat))
722 subdirs_left = tmp_stat.st_nlink - 2;
723 else
724 subdirs_left = 0;
725 /* Commented out as unnecessary
726 if (subdirs_left < 0)
727 subdirs_left = MAXINT;
729 dirp = mc_opendir (directory);
730 } /* while (!dirp) */
731 dp = mc_readdir (dirp);
732 /* skip invalid filenames */
733 while (dp != NULL && !str_is_valid_string (dp->d_name))
734 dp = mc_readdir (dirp);
735 } /* while (!dp) */
737 if (strcmp (dp->d_name, ".") == 0 ||
738 strcmp (dp->d_name, "..") == 0){
739 dp = mc_readdir (dirp);
740 /* skip invalid filenames */
741 while (dp != NULL && !str_is_valid_string (dp->d_name))
742 dp = mc_readdir (dirp);
744 mc_search_free(search_file_handle);
745 search_file_handle = NULL;
746 mc_search_free(search_content_handle);
747 search_content_handle = NULL;
748 return 1;
751 if (!(skip_hidden_flag && dp->d_name[0] == '.')) {
752 if (subdirs_left && find_recursively && directory) { /* Can directory be NULL ? */
753 char *tmp_name = concat_dir_and_file (directory, dp->d_name);
754 if (!mc_lstat (tmp_name, &tmp_stat)
755 && S_ISDIR (tmp_stat.st_mode)) {
756 push_directory (tmp_name);
757 subdirs_left--;
759 g_free (tmp_name);
761 if (mc_search_run (search_file_handle,dp->d_name, 0, strlen (dp->d_name), &bytes_found)) {
762 if (content_pattern) {
763 if (search_content (h, directory, dp->d_name)) {
764 mc_search_free(search_file_handle);
765 search_file_handle = NULL;
766 mc_search_free(search_content_handle);
767 search_content_handle = NULL;
768 return 1;
770 } else
771 find_add_match (h, directory, dp->d_name);
775 dp = mc_readdir (dirp);
776 /* skip invalid filenames */
777 while (dp != NULL && !str_is_valid_string (dp->d_name))
778 dp = mc_readdir (dirp);
780 /* Displays the nice dot */
781 count++;
782 if (!(count & 31)){
783 /* For nice updating */
784 const char *rotating_dash = "|/-\\";
786 if (verbose){
787 pos = (pos + 1) % 4;
788 attrset (DLG_NORMALC (h));
789 dlg_move (h, FIND2_Y-6, FIND2_X - 4);
790 addch (rotating_dash [pos]);
791 mc_refresh ();
793 } else
794 goto do_search_begin;
795 mc_search_free(search_file_handle);
796 search_file_handle = NULL;
797 mc_search_free(search_content_handle);
798 search_content_handle = NULL;
799 return 1;
802 static void
803 init_find_vars (void)
805 char *dir;
807 g_free (old_dir);
808 old_dir = 0;
809 count = 0;
810 matches = 0;
812 /* Remove all the items in the stack */
813 while ((dir = pop_directory ()) != NULL)
814 g_free (dir);
817 static char *
818 make_fullname (const char *dirname, const char *filename)
821 if (strcmp(dirname, ".") == 0 || strcmp(dirname, "."PATH_SEP_STR) == 0)
822 return g_strdup (filename);
823 if (strncmp(dirname, "."PATH_SEP_STR, 2) == 0)
824 return concat_dir_and_file (dirname + 2, filename);
825 return concat_dir_and_file (dirname, filename);
828 static void
829 find_do_view_edit (int unparsed_view, int edit, char *dir, char *file)
831 char *fullname;
832 const char *filename;
833 int line;
835 if (content_pattern){
836 filename = strchr (file + 4, ':') + 1;
837 line = atoi (file + 4);
838 } else {
839 filename = file + 4;
840 line = 0;
843 fullname = make_fullname (dir, filename);
844 if (edit)
845 do_edit_at_line (fullname, line);
846 else
847 view_file_at_line (fullname, unparsed_view, use_internal_view, line);
848 g_free (fullname);
851 static int
852 view_edit_currently_selected_file (int unparsed_view, int edit)
854 WLEntry *entry = find_list->current;
855 char *dir;
857 if (!entry)
858 return MSG_NOT_HANDLED;
860 dir = entry->data;
862 if (!entry->text || !dir)
863 return MSG_NOT_HANDLED;
865 find_do_view_edit (unparsed_view, edit, dir, entry->text);
866 return MSG_HANDLED;
869 static cb_ret_t
870 find_callback (struct Dlg_head *h, dlg_msg_t msg, int parm)
872 switch (msg) {
873 case DLG_KEY:
874 if (parm == KEY_F (3) || parm == KEY_F (13)) {
875 int unparsed_view = (parm == KEY_F (13));
876 return view_edit_currently_selected_file (unparsed_view, 0);
878 if (parm == KEY_F (4)) {
879 return view_edit_currently_selected_file (0, 1);
881 return MSG_NOT_HANDLED;
883 case DLG_IDLE:
884 do_search (h);
885 return MSG_HANDLED;
887 default:
888 return default_dlg_callback (h, msg, parm);
892 /* Handles the Stop/Start button in the find window */
893 static int
894 start_stop (int button)
896 (void) button;
898 running = is_start;
899 set_idle_proc (find_dlg, running);
900 is_start = !is_start;
902 status_update (is_start ? _("Stopped") : _("Searching"));
903 button_set_text (stop_button, fbuts [is_start].text);
905 return 0;
908 /* Handle view command, when invoked as a button */
909 static int
910 find_do_view_file (int button)
912 (void) button;
914 view_edit_currently_selected_file (0, 0);
915 return 0;
918 /* Handle edit command, when invoked as a button */
919 static int
920 find_do_edit_file (int button)
922 (void) button;
924 view_edit_currently_selected_file (0, 1);
925 return 0;
928 static void
929 setup_gui (void)
931 #ifdef ENABLE_NLS
932 static int i18n_flag = 0;
933 if (!i18n_flag) {
934 register int i = sizeof (fbuts) / sizeof (fbuts[0]);
935 while (i--)
936 fbuts[i].len = str_term_width1 (fbuts[i].text = _(fbuts[i].text)) + 3;
937 fbuts[2].len += 2; /* DEFPUSH_BUTTON */
938 i18n_flag = 1;
940 #endif /* ENABLE_NLS */
943 * Dynamically place buttons centered within current window size
946 int l0 = max (fbuts[0].len, fbuts[1].len);
947 int l1 = fbuts[2].len + fbuts[3].len + l0 + fbuts[4].len;
948 int l2 = fbuts[5].len + fbuts[6].len + fbuts[7].len;
949 int r1, r2;
951 FIND2_X = COLS - 16;
953 /* Check, if both button rows fit within FIND2_X */
954 if (l1 + 9 > FIND2_X)
955 FIND2_X = l1 + 9;
956 if (l2 + 8 > FIND2_X)
957 FIND2_X = l2 + 8;
959 /* compute amount of space between buttons for each row */
960 r1 = (FIND2_X - 4 - l1) % 5;
961 l1 = (FIND2_X - 4 - l1) / 5;
962 r2 = (FIND2_X - 4 - l2) % 4;
963 l2 = (FIND2_X - 4 - l2) / 4;
965 /* ...and finally, place buttons */
966 fbuts[2].x = 2 + r1 / 2 + l1;
967 fbuts[3].x = fbuts[2].x + fbuts[2].len + l1;
968 fbuts[0].x = fbuts[3].x + fbuts[3].len + l1;
969 fbuts[4].x = fbuts[0].x + l0 + l1;
970 fbuts[5].x = 2 + r2 / 2 + l2;
971 fbuts[6].x = fbuts[5].x + fbuts[5].len + l2;
972 fbuts[7].x = fbuts[6].x + fbuts[6].len + l2;
975 find_dlg =
976 create_dlg (0, 0, FIND2_Y, FIND2_X, dialog_colors, find_callback,
977 "[Find File]", _("Find File"), DLG_CENTER | DLG_REVERSE);
979 add_widget (find_dlg,
980 button_new (FIND2_Y - 3, fbuts[7].x, B_VIEW, NORMAL_BUTTON,
981 fbuts[7].text, find_do_edit_file));
982 add_widget (find_dlg,
983 button_new (FIND2_Y - 3, fbuts[6].x, B_VIEW, NORMAL_BUTTON,
984 fbuts[6].text, find_do_view_file));
985 add_widget (find_dlg,
986 button_new (FIND2_Y - 3, fbuts[5].x, B_PANELIZE,
987 NORMAL_BUTTON, fbuts[5].text, 0));
989 add_widget (find_dlg,
990 button_new (FIND2_Y - 4, fbuts[4].x, B_CANCEL,
991 NORMAL_BUTTON, fbuts[4].text, 0));
992 stop_button =
993 button_new (FIND2_Y - 4, fbuts[0].x, B_STOP, NORMAL_BUTTON,
994 fbuts[0].text, start_stop);
995 add_widget (find_dlg, stop_button);
996 add_widget (find_dlg,
997 button_new (FIND2_Y - 4, fbuts[3].x, B_AGAIN,
998 NORMAL_BUTTON, fbuts[3].text, 0));
999 add_widget (find_dlg,
1000 button_new (FIND2_Y - 4, fbuts[2].x, B_ENTER,
1001 DEFPUSH_BUTTON, fbuts[2].text, 0));
1003 status_label = label_new (FIND2_Y - 6, 4, _("Searching"));
1004 add_widget (find_dlg, status_label);
1006 find_list =
1007 listbox_new (2, 2, FIND2_Y - 9, FIND2_X - 4, NULL);
1008 add_widget (find_dlg, find_list);
1011 static int
1012 run_process (void)
1014 resuming = 0;
1015 set_idle_proc (find_dlg, 1);
1016 run_dlg (find_dlg);
1017 return find_dlg->ret_value;
1020 static void
1021 kill_gui (void)
1023 set_idle_proc (find_dlg, 0);
1024 destroy_dlg (find_dlg);
1027 static int
1028 find_file (char *start_dir, char *pattern, char *content, char **dirname,
1029 char **filename)
1031 int return_value = 0;
1032 char *dir;
1033 char *dir_tmp, *file_tmp;
1035 setup_gui ();
1037 /* FIXME: Need to cleanup this, this ought to be passed non-globaly */
1038 find_pattern = pattern;
1039 content_pattern = (content != NULL && str_is_valid_string (content))
1040 ? str_create_search_needle (content, content_case_sensitive)
1041 : NULL;
1043 init_find_vars ();
1044 push_directory (start_dir);
1046 return_value = run_process ();
1048 /* Remove all the items in the stack */
1049 while ((dir = pop_directory ()) != NULL)
1050 g_free (dir);
1052 get_list_info (&file_tmp, &dir_tmp);
1054 if (dir_tmp)
1055 *dirname = g_strdup (dir_tmp);
1056 if (file_tmp)
1057 *filename = g_strdup (file_tmp);
1059 if (return_value == B_PANELIZE && *filename) {
1060 int status, link_to_dir, stale_link;
1061 int next_free = 0;
1062 int i;
1063 struct stat st;
1064 WLEntry *entry = find_list->list;
1065 dir_list *list = &current_panel->dir;
1066 char *name;
1068 for (i = 0; entry && i < find_list->count;
1069 entry = entry->next, i++) {
1070 const char *filename;
1072 if (!entry->text || !entry->data)
1073 continue;
1075 if (content_pattern)
1076 filename = strchr (entry->text + 4, ':') + 1;
1077 else
1078 filename = entry->text + 4;
1080 name = make_fullname (entry->data, filename);
1081 status =
1082 handle_path (list, name, &st, next_free, &link_to_dir,
1083 &stale_link);
1084 if (status == 0) {
1085 g_free (name);
1086 continue;
1088 if (status == -1) {
1089 g_free (name);
1090 break;
1093 /* don't add files more than once to the panel */
1094 if (content_pattern && next_free > 0) {
1095 if (strcmp (list->list[next_free - 1].fname, name) == 0) {
1096 g_free (name);
1097 continue;
1101 if (!next_free) /* first turn i.e clean old list */
1102 panel_clean_dir (current_panel);
1103 list->list[next_free].fnamelen = strlen (name);
1104 list->list[next_free].fname = name;
1105 list->list[next_free].f.marked = 0;
1106 list->list[next_free].f.link_to_dir = link_to_dir;
1107 list->list[next_free].f.stale_link = stale_link;
1108 list->list[next_free].f.dir_size_computed = 0;
1109 list->list[next_free].st = st;
1110 list->list[next_free].sort_key = NULL;
1111 list->list[next_free].second_sort_key = NULL;
1112 next_free++;
1113 if (!(next_free & 15))
1114 rotate_dash ();
1116 if (next_free) {
1117 current_panel->count = next_free;
1118 current_panel->is_panelized = 1;
1119 /* Done by panel_clean_dir a few lines above
1120 current_panel->dirs_marked = 0;
1121 current_panel->marked = 0;
1122 current_panel->total = 0;
1123 current_panel->top_file = 0;
1124 current_panel->selected = 0; */
1126 if (start_dir[0] == PATH_SEP) {
1127 strcpy (current_panel->cwd, PATH_SEP_STR);
1128 chdir (PATH_SEP_STR);
1133 if (content_pattern != NULL) str_release_search_needle (content_pattern, content_case_sensitive);
1134 kill_gui ();
1135 do_search (0); /* force do_search to release resources */
1136 g_free (old_dir);
1137 old_dir = 0;
1139 return return_value;
1142 void
1143 do_find (void)
1145 char *start_dir = NULL, *pattern = NULL, *content = NULL;
1146 char *filename, *dirname;
1147 int v, dir_and_file_set;
1149 while (find_parameters (&start_dir, &pattern, &content)){
1151 dirname = filename = NULL;
1152 is_start = 0;
1153 v = find_file (start_dir, pattern, content, &dirname, &filename);
1154 g_free (start_dir);
1155 g_free (pattern);
1157 if (v == B_ENTER){
1158 if (dirname || filename){
1159 if (dirname){
1160 do_cd (dirname, cd_exact);
1161 if (filename)
1162 try_to_select (current_panel, filename + (content ?
1163 (strchr (filename + 4, ':') - filename + 1) : 4) );
1164 } else if (filename)
1165 do_cd (filename, cd_exact);
1166 select_item (current_panel);
1168 g_free (dirname);
1169 g_free (filename);
1170 break;
1172 g_free (content);
1173 dir_and_file_set = dirname && filename;
1174 g_free (dirname);
1175 g_free (filename);
1176 if (v == B_CANCEL)
1177 break;
1179 if (v == B_PANELIZE){
1180 if (dir_and_file_set){
1181 try_to_select (current_panel, NULL);
1182 panel_re_sort (current_panel);
1183 try_to_select (current_panel, NULL);
1185 break;