Rework strutils for usage GString instread of self-made buffers.
[midnight-commander.git] / src / find.c
blobeb38055d58736f82c60039ec4a2150be9bf0f8d0
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 #include <config.h>
24 #include <ctype.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/stat.h>
30 #include "global.h"
31 #include "tty.h"
32 #include "win.h"
33 #include "color.h"
34 #include "setup.h"
35 #include "find.h"
36 #include "strutil.h"
38 /* Dialog manager and widgets */
39 #include "dialog.h"
40 #include "widget.h"
42 #include "dir.h"
43 #include "panel.h" /* current_panel */
44 #include "main.h" /* do_cd, try_to_select */
45 #include "wtools.h"
46 #include "cmd.h" /* view_file_at_line */
47 #include "boxes.h"
48 #include "key.h"
50 /* Size of the find parameters window */
51 #define FIND_Y 16
52 static int FIND_X = 50;
54 /* Size of the find window */
55 #define FIND2_Y (LINES - 4)
57 static int FIND2_X = 64;
58 #define FIND2_X_USE (FIND2_X - 20)
60 /* A couple of extra messages we need */
61 enum {
62 B_STOP = B_USER + 1,
63 B_AGAIN,
64 B_PANELIZE,
65 B_TREE,
66 B_VIEW
69 typedef enum {
70 FIND_CONT,
71 FIND_SUSPEND,
72 FIND_ABORT
73 } FindProgressStatus;
75 /* List of directories to be ignored, separated by ':' */
76 char *find_ignore_dirs = 0;
78 static WInput *in_start; /* Start path */
79 static WInput *in_name; /* Pattern to search */
80 static WInput *in_with; /* Text inside filename */
81 static WCheck *case_sense; /* "case sensitive" checkbox */
82 static WCheck *find_regex_cbox; /* [x] find regular expression */
84 static int running = 0; /* nice flag */
85 static char *find_pattern; /* Pattern to search */
86 static char *content_pattern; /* pattern to search inside files; if
87 find_regex_flag is true, it contains the
88 regex pattern, else the search string. */
89 static int count; /* Number of files displayed */
90 static int matches; /* Number of matches */
91 static int is_start; /* Status of the start/stop toggle button */
92 static char *old_dir;
94 /* Where did we stop */
95 static int resuming;
96 static int last_line;
97 static int last_pos;
99 static Dlg_head *find_dlg; /* The dialog */
101 static WButton *stop_button; /* pointer to the stop button */
102 static WLabel *status_label; /* Finished, Searching etc. */
103 static WListbox *find_list; /* Listbox with the file list */
105 /* This keeps track of the directory stack */
106 typedef struct dir_stack {
107 char *name;
108 struct dir_stack *prev;
109 } dir_stack ;
111 static dir_stack *dir_stack_base = 0;
113 static struct {
114 const char* text;
115 int len; /* length including space and brackets */
116 int x;
117 } fbuts [] = {
118 { N_("&Suspend"), 11, 29 },
119 { N_("Con&tinue"), 12, 29 },
120 { N_("&Chdir"), 11, 3 },
121 { N_("&Again"), 9, 17 },
122 { N_("&Quit"), 8, 43 },
123 { N_("Pane&lize"), 12, 3 },
124 { N_("&View - F3"), 13, 20 },
125 { N_("&Edit - F4"), 13, 38 }
128 static inline char * add_to_list (const char *text, void *data) {
129 return listbox_add_item (find_list, 0, 0, text, data);
131 static inline void stop_idle (void *data) {
132 set_idle_proc (data, 0);
134 static inline void status_update (const char *text) {
135 label_set_text (status_label, text);
137 static void get_list_info (char **file, char **dir) {
138 listbox_get_current (find_list, file, dir);
141 /* FIXME: r should be local variable */
142 static regex_t *r; /* Pointer to compiled content_pattern */
144 static int case_sensitive = 1;
145 static gboolean find_regex_flag = TRUE;
146 static gboolean skip_hidden_flag = FALSE;
147 static int find_recursively = 1;
150 * Callback for the parameter dialog.
151 * Validate regex, prevent closing the dialog if it's invalid.
153 static cb_ret_t
154 find_parm_callback (struct Dlg_head *h, dlg_msg_t msg, int parm)
156 int flags;
158 switch (msg) {
159 case DLG_VALIDATE:
160 if ((h->ret_value != B_ENTER) || !in_with->buffer[0]
161 || !(find_regex_cbox->state & C_BOOL))
162 return MSG_HANDLED;
164 flags = REG_EXTENDED | REG_NOSUB;
166 if (!(case_sense->state & C_BOOL))
167 flags |= REG_ICASE;
169 if (regcomp (r, in_with->buffer, flags)) {
170 message (D_ERROR, MSG_ERROR, _(" Malformed regular expression "));
171 dlg_select_widget (in_with);
172 h->running = 1; /* Don't stop the dialog */
174 return MSG_HANDLED;
176 default:
177 return default_dlg_callback (h, msg, parm);
182 * find_parameters: gets information from the user
184 * If the return value is true, then the following holds:
186 * START_DIR and PATTERN are pointers to char * and upon return they
187 * contain the information provided by the user.
189 * CONTENT holds a strdup of the contents specified by the user if he
190 * asked for them or 0 if not (note, this is different from the
191 * behavior for the other two parameters.
194 static int
195 find_parameters (char **start_dir, char **pattern, char **content)
197 int return_value;
198 char *temp_dir;
199 static const char *case_label = N_("case &Sensitive");
200 static const char *recurs_label = N_("&Find recursively");
201 static const char *skip_hidden_label = N_("S&kip hidden");
202 static const char *regexp_label = N_("&Regular expression");
204 WCheck *recursively_cbox;
205 WCheck *skip_hidden_cbox;
207 static char *in_contents = NULL;
208 static char *in_start_dir = NULL;
209 static char *in_start_name = NULL;
211 static const char *labs[] =
212 { N_("Start at:"), N_("Filename:"), N_("Content: ") };
213 static const char *buts[] = { N_("&OK"), N_("&Tree"), N_("&Cancel") };
214 static int ilen = 30, istart = 14;
215 static int b0 = 3, b1 = 16, b2 = 36;
217 #ifdef ENABLE_NLS
218 static int i18n_flag = 0;
220 if (!i18n_flag) {
221 register int i = sizeof (labs) / sizeof (labs[0]);
222 int l1, maxlen = 0;
224 while (i--) {
225 l1 = str_term_width1 (labs[i] = _(labs[i]));
226 if (l1 > maxlen)
227 maxlen = l1;
229 i = maxlen + ilen + 7;
230 if (i > FIND_X)
231 FIND_X = i;
233 for (i = sizeof (buts) / sizeof (buts[0]), l1 = 0; i--;) {
234 l1 += str_term_width1 (buts[i] = _(buts[i]));
236 l1 += 21;
237 if (l1 > FIND_X)
238 FIND_X = l1;
240 ilen = FIND_X - 7 - maxlen; /* for the case of very long buttons :) */
241 istart = FIND_X - 3 - ilen;
243 b1 = b0 + str_term_width1 (buts[0]) + 7;
244 b2 = FIND_X - (str_term_width1 (buts[2]) + 6);
246 i18n_flag = 1;
247 case_label = _(case_label);
248 recurs_label = _(recurs_label);
250 #endif /* ENABLE_NLS */
252 find_par_start:
253 if (!in_start_dir)
254 in_start_dir = g_strdup (".");
255 if (!in_start_name)
256 in_start_name = g_strdup (easy_patterns ? "*" : ".");
257 if (!in_contents)
258 in_contents = g_strdup ("");
260 find_dlg =
261 create_dlg (0, 0, FIND_Y, FIND_X, dialog_colors,
262 find_parm_callback, "[Find File]", _("Find File"),
263 DLG_CENTER | DLG_REVERSE);
265 add_widget (find_dlg,
266 button_new (FIND_Y - 3, b2, B_CANCEL, NORMAL_BUTTON, buts[2], 0));
267 add_widget (find_dlg,
268 button_new (FIND_Y - 3, b1, B_TREE, NORMAL_BUTTON, buts[1], 0));
269 add_widget (find_dlg,
270 button_new (FIND_Y - 3, b0, B_ENTER, DEFPUSH_BUTTON, buts[0], 0));
272 recursively_cbox = check_new (6, istart, find_recursively, recurs_label);
273 skip_hidden_cbox = check_new (7, istart, skip_hidden_flag, skip_hidden_label);
274 find_regex_cbox = check_new (11, istart, find_regex_flag, regexp_label);
275 case_sense = check_new (10, istart, case_sensitive, case_label);
277 in_with = input_new (9, istart, INPUT_COLOR, ilen, in_contents, "content", INPUT_COMPLETE_DEFAULT);
278 in_name = input_new (5, istart, INPUT_COLOR, ilen, in_start_name, "name", INPUT_COMPLETE_DEFAULT);
279 in_start = input_new (3, istart, INPUT_COLOR, ilen, in_start_dir, "start", INPUT_COMPLETE_DEFAULT);
281 add_widget (find_dlg, find_regex_cbox);
282 add_widget (find_dlg, case_sense);
283 add_widget (find_dlg, in_with);
284 add_widget (find_dlg, skip_hidden_cbox);
285 add_widget (find_dlg, recursively_cbox);
286 add_widget (find_dlg, in_name);
287 add_widget (find_dlg, in_start);
289 add_widget (find_dlg, label_new (8, 3, labs[2]));
290 add_widget (find_dlg, label_new (5, 3, labs[1]));
291 add_widget (find_dlg, label_new (3, 3, labs[0]));
293 dlg_select_widget (in_name);
295 run_dlg (find_dlg);
297 switch (find_dlg->ret_value) {
298 case B_CANCEL:
299 return_value = 0;
300 break;
302 case B_TREE:
303 temp_dir = g_strdup (in_start->buffer);
304 case_sensitive = case_sense->state & C_BOOL;
305 find_regex_flag = find_regex_cbox->state & C_BOOL;
306 find_recursively = recursively_cbox->state & C_BOOL;
307 skip_hidden_flag = skip_hidden_cbox->state & C_BOOL;
308 destroy_dlg (find_dlg);
309 g_free (in_start_dir);
310 if (strcmp (temp_dir, ".") == 0) {
311 g_free (temp_dir);
312 temp_dir = g_strdup (current_panel->cwd);
314 in_start_dir = tree_box (temp_dir);
315 if (in_start_dir)
316 g_free (temp_dir);
317 else
318 in_start_dir = temp_dir;
319 /* Warning: Dreadful goto */
320 goto find_par_start;
321 break;
323 default:
324 g_free (in_contents);
325 if (in_with->buffer[0]) {
326 *content = g_strdup (in_with->buffer);
327 in_contents = g_strdup (*content);
328 } else {
329 *content = in_contents = NULL;
330 r = 0;
333 case_sensitive = case_sense->state & C_BOOL;
334 find_regex_flag = find_regex_cbox->state & C_BOOL;
335 find_recursively = recursively_cbox->state & C_BOOL;
336 skip_hidden_flag = skip_hidden_cbox->state & C_BOOL;
337 return_value = 1;
338 *start_dir = g_strdup (in_start->buffer);
339 *pattern = g_strdup (in_name->buffer);
341 g_free (in_start_dir);
342 in_start_dir = g_strdup (*start_dir);
343 g_free (in_start_name);
344 in_start_name = g_strdup (*pattern);
347 destroy_dlg (find_dlg);
349 return return_value;
352 static void
353 push_directory (const char *dir)
355 dir_stack *new;
357 new = g_new (dir_stack, 1);
358 new->name = concat_dir_and_file (dir, "");
359 new->prev = dir_stack_base;
360 dir_stack_base = new;
363 static char*
364 pop_directory (void)
366 char *name;
367 dir_stack *next;
369 if (dir_stack_base){
370 name = dir_stack_base->name;
371 next = dir_stack_base->prev;
372 g_free (dir_stack_base);
373 dir_stack_base = next;
374 return name;
375 } else
376 return 0;
379 static void
380 insert_file (const char *dir, const char *file)
382 char *tmp_name;
383 static char *dirname;
385 while (dir [0] == PATH_SEP && dir [1] == PATH_SEP)
386 dir++;
388 if (old_dir){
389 if (strcmp (old_dir, dir)){
390 g_free (old_dir);
391 old_dir = g_strdup (dir);
392 dirname = add_to_list (dir, NULL);
394 } else {
395 old_dir = g_strdup (dir);
396 dirname = add_to_list (dir, NULL);
399 tmp_name = g_strconcat (" ", file, (char *) NULL);
400 add_to_list (tmp_name, dirname);
401 g_free (tmp_name);
404 static void
405 find_add_match (Dlg_head *h, const char *dir, const char *file)
407 int p = ++matches & 7;
409 (void) h;
411 insert_file (dir, file);
413 /* Scroll nicely */
414 if (!p)
415 listbox_select_last (find_list, 1);
416 else
417 listbox_select_last (find_list, 0);
418 /* Updates the current listing */
419 send_message (&find_list->widget, WIDGET_DRAW, 0);
420 if (p == 7)
421 mc_refresh ();
425 * get_line_at:
427 * Returns malloced null-terminated line from file file_fd.
428 * Input is buffered in buf_size long buffer.
429 * Current pos in buf is stored in pos.
430 * n_read - number of read chars.
431 * has_newline - is there newline ?
433 static char *
434 get_line_at (int file_fd, char *buf, int *pos, int *n_read, int buf_size,
435 int *has_newline)
437 char *buffer = 0;
438 int buffer_size = 0;
439 char ch = 0;
440 int i = 0;
442 for (;;) {
443 if (*pos >= *n_read) {
444 *pos = 0;
445 if ((*n_read = mc_read (file_fd, buf, buf_size)) <= 0)
446 break;
449 ch = buf[(*pos)++];
450 if (ch == 0) {
451 /* skip possible leading zero(s) */
452 if (i == 0)
453 continue;
454 else
455 break;
458 if (i >= buffer_size - 1) {
459 buffer = g_realloc (buffer, buffer_size += 80);
461 /* Strip newline */
462 if (ch == '\n')
463 break;
465 buffer[i++] = ch;
468 *has_newline = ch ? 1 : 0;
470 if (buffer) {
471 buffer[i] = 0;
474 return buffer;
477 static FindProgressStatus
478 check_find_events(Dlg_head *h)
480 Gpm_Event event;
481 int c;
483 c = get_event (&event, h->mouse_status == MOU_REPEAT, 0);
484 if (c != EV_NONE) {
485 dlg_process_event (h, c, &event);
486 if (h->ret_value == B_ENTER
487 || h->ret_value == B_CANCEL
488 || h->ret_value == B_AGAIN
489 || h->ret_value == B_PANELIZE) {
490 /* dialog terminated */
491 return FIND_ABORT;
493 if (!(h->flags & DLG_WANT_IDLE)) {
494 /* searching suspended */
495 return FIND_SUSPEND;
499 return FIND_CONT;
503 * search_content:
505 * Search the global (FIXME) regexp compiled content_pattern string in the
506 * DIRECTORY/FILE. It will add the found entries to the find listbox.
508 * returns 0 if do_search should look for another file
509 * 1 if do_search should exit and proceed to the event handler
511 static int
512 search_content (Dlg_head *h, const char *directory, const char *filename)
514 struct stat s;
515 char buffer [BUF_SMALL];
516 char *fname;
517 int file_fd;
518 int ret_val = 0;
520 fname = concat_dir_and_file (directory, filename);
522 if (mc_stat (fname, &s) != 0 || !S_ISREG (s.st_mode)){
523 g_free (fname);
524 return 0;
527 file_fd = mc_open (fname, O_RDONLY);
528 g_free (fname);
530 if (file_fd == -1)
531 return 0;
533 g_snprintf (buffer, sizeof (buffer), _("Grepping in %s"), str_trunc (filename, FIND2_X_USE));
535 status_update (buffer);
536 mc_refresh ();
538 enable_interrupt_key ();
539 got_interrupt ();
542 int line = 1;
543 int pos = 0;
544 int n_read = 0;
545 int has_newline;
546 char *p;
547 int found = 0;
549 if (resuming) {
550 /* We've been previously suspended, start from the previous position */
551 resuming = 0;
552 line = last_line;
553 pos = last_pos;
556 while ((p = get_line_at (file_fd, buffer, &pos, &n_read, sizeof (buffer), &has_newline)) && (ret_val == 0)){
557 if (found == 0){ /* Search in binary line once */
558 if (find_regex_flag) {
559 if (regexec (r, p, 1, 0, 0) == 0){
560 g_free (p);
561 p = g_strdup_printf ("%d:%s", line, filename);
562 find_add_match (h, directory, p);
563 found = 1;
565 } else {
566 // str_case_search_first do not accept invalid strings
567 if (str_is_valid_string (p) && str_search_first (p, content_pattern, case_sensitive) != NULL) {
568 char *match = g_strdup_printf("%d:%s", line, filename);
569 find_add_match (h, directory, match);
570 found = TRUE;
574 if (has_newline){
575 line++;
576 found = 0;
578 g_free (p);
580 if ((line & 0xff) == 0) {
581 FindProgressStatus res;
582 res = check_find_events(h);
583 switch (res) {
584 case FIND_ABORT:
585 stop_idle (h);
586 ret_val = 1;
587 break;
588 case FIND_SUSPEND:
589 resuming = 1;
590 last_line = line;
591 last_pos = pos;
592 ret_val = 1;
593 break;
594 default:
595 break;
601 disable_interrupt_key ();
602 mc_close (file_fd);
603 return ret_val;
606 static int
607 do_search (struct Dlg_head *h)
609 static struct dirent *dp = 0;
610 static DIR *dirp = 0;
611 static char *directory;
612 struct stat tmp_stat;
613 static int pos;
614 static int subdirs_left = 0;
616 if (!h) { /* someone forces me to close dirp */
617 if (dirp) {
618 mc_closedir (dirp);
619 dirp = 0;
621 g_free (directory);
622 directory = NULL;
623 dp = 0;
624 return 1;
626 do_search_begin:
627 while (!dp){
629 if (dirp){
630 mc_closedir (dirp);
631 dirp = 0;
634 while (!dirp){
635 char *tmp;
637 attrset (REVERSE_COLOR);
638 while (1) {
639 tmp = pop_directory ();
640 if (!tmp){
641 running = 0;
642 status_update (_("Finished"));
643 stop_idle (h);
644 return 0;
646 if (find_ignore_dirs){
647 int found;
648 char *temp_dir = g_strconcat (":", tmp, ":", (char *) NULL);
650 found = strstr (find_ignore_dirs, temp_dir) != 0;
651 g_free (temp_dir);
652 if (found)
653 g_free (tmp);
654 else
655 break;
656 } else
657 break;
660 g_free (directory);
661 directory = tmp;
663 if (verbose){
664 char buffer [BUF_SMALL];
666 g_snprintf (buffer, sizeof (buffer), _("Searching %s"),
667 str_trunc (directory, FIND2_X_USE));
668 status_update (buffer);
670 /* mc_stat should not be called after mc_opendir
671 because vfs_s_opendir modifies the st_nlink
673 if (!mc_stat (directory, &tmp_stat))
674 subdirs_left = tmp_stat.st_nlink - 2;
675 else
676 subdirs_left = 0;
677 /* Commented out as unnecessary
678 if (subdirs_left < 0)
679 subdirs_left = MAXINT;
681 dirp = mc_opendir (directory);
682 } /* while (!dirp) */
683 dp = mc_readdir (dirp);
684 /* skip invalid filenames */
685 while (dp != NULL && !str_is_valid_string (dp->d_name))
686 dp = mc_readdir (dirp);
687 } /* while (!dp) */
689 if (strcmp (dp->d_name, ".") == 0 ||
690 strcmp (dp->d_name, "..") == 0){
691 dp = mc_readdir (dirp);
692 /* skip invalid filenames */
693 while (dp != NULL && !str_is_valid_string (dp->d_name))
694 dp = mc_readdir (dirp);
695 return 1;
698 if (!(skip_hidden_flag && dp->d_name[0] == '.')) {
699 if (subdirs_left && find_recursively && directory) { /* Can directory be NULL ? */
700 char *tmp_name = concat_dir_and_file (directory, dp->d_name);
701 if (!mc_lstat (tmp_name, &tmp_stat)
702 && S_ISDIR (tmp_stat.st_mode)) {
703 push_directory (tmp_name);
704 subdirs_left--;
706 g_free (tmp_name);
709 if (regexp_match (find_pattern, dp->d_name, match_file)){
710 if (content_pattern) {
711 if (search_content (h, directory, dp->d_name)) {
712 return 1;
714 } else
715 find_add_match (h, directory, dp->d_name);
719 dp = mc_readdir (dirp);
720 /* skip invalid filenames */
721 while (dp != NULL && !str_is_valid_string (dp->d_name))
722 dp = mc_readdir (dirp);
724 /* Displays the nice dot */
725 count++;
726 if (!(count & 31)){
727 /* For nice updating */
728 const char *rotating_dash = "|/-\\";
730 if (verbose){
731 pos = (pos + 1) % 4;
732 attrset (DLG_NORMALC (h));
733 dlg_move (h, FIND2_Y-6, FIND2_X - 4);
734 addch (rotating_dash [pos]);
735 mc_refresh ();
737 } else
738 goto do_search_begin;
739 return 1;
742 static void
743 init_find_vars (void)
745 char *dir;
747 g_free (old_dir);
748 old_dir = 0;
749 count = 0;
750 matches = 0;
752 /* Remove all the items in the stack */
753 while ((dir = pop_directory ()) != NULL)
754 g_free (dir);
757 static char *
758 make_fullname (const char *dirname, const char *filename)
761 if (strcmp(dirname, ".") == 0 || strcmp(dirname, "."PATH_SEP_STR) == 0)
762 return g_strdup (filename);
763 if (strncmp(dirname, "."PATH_SEP_STR, 2) == 0)
764 return concat_dir_and_file (dirname + 2, filename);
765 return concat_dir_and_file (dirname, filename);
768 static void
769 find_do_view_edit (int unparsed_view, int edit, char *dir, char *file)
771 char *fullname;
772 const char *filename;
773 int line;
775 if (content_pattern){
776 filename = strchr (file + 4, ':') + 1;
777 line = atoi (file + 4);
778 } else {
779 filename = file + 4;
780 line = 0;
783 fullname = make_fullname (dir, filename);
784 if (edit)
785 do_edit_at_line (fullname, line);
786 else
787 view_file_at_line (fullname, unparsed_view, use_internal_view, line);
788 g_free (fullname);
791 static int
792 view_edit_currently_selected_file (int unparsed_view, int edit)
794 WLEntry *entry = find_list->current;
795 char *dir;
797 if (!entry)
798 return MSG_NOT_HANDLED;
800 dir = entry->data;
802 if (!entry->text || !dir)
803 return MSG_NOT_HANDLED;
805 find_do_view_edit (unparsed_view, edit, dir, entry->text);
806 return MSG_HANDLED;
809 static cb_ret_t
810 find_callback (struct Dlg_head *h, dlg_msg_t msg, int parm)
812 switch (msg) {
813 case DLG_KEY:
814 if (parm == KEY_F (3) || parm == KEY_F (13)) {
815 int unparsed_view = (parm == KEY_F (13));
816 return view_edit_currently_selected_file (unparsed_view, 0);
818 if (parm == KEY_F (4)) {
819 return view_edit_currently_selected_file (0, 1);
821 return MSG_NOT_HANDLED;
823 case DLG_IDLE:
824 do_search (h);
825 return MSG_HANDLED;
827 default:
828 return default_dlg_callback (h, msg, parm);
832 /* Handles the Stop/Start button in the find window */
833 static int
834 start_stop (int button)
836 (void) button;
838 running = is_start;
839 set_idle_proc (find_dlg, running);
840 is_start = !is_start;
842 status_update (is_start ? _("Stopped") : _("Searching"));
843 button_set_text (stop_button, fbuts [is_start].text);
845 return 0;
848 /* Handle view command, when invoked as a button */
849 static int
850 find_do_view_file (int button)
852 (void) button;
854 view_edit_currently_selected_file (0, 0);
855 return 0;
858 /* Handle edit command, when invoked as a button */
859 static int
860 find_do_edit_file (int button)
862 (void) button;
864 view_edit_currently_selected_file (0, 1);
865 return 0;
868 static void
869 setup_gui (void)
871 #ifdef ENABLE_NLS
872 static int i18n_flag = 0;
873 if (!i18n_flag) {
874 register int i = sizeof (fbuts) / sizeof (fbuts[0]);
875 while (i--)
876 fbuts[i].len = str_term_width1 (fbuts[i].text = _(fbuts[i].text)) + 3;
877 fbuts[2].len += 2; /* DEFPUSH_BUTTON */
878 i18n_flag = 1;
880 #endif /* ENABLE_NLS */
883 * Dynamically place buttons centered within current window size
886 int l0 = max (fbuts[0].len, fbuts[1].len);
887 int l1 = fbuts[2].len + fbuts[3].len + l0 + fbuts[4].len;
888 int l2 = fbuts[5].len + fbuts[6].len + fbuts[7].len;
889 int r1, r2;
891 FIND2_X = COLS - 16;
893 /* Check, if both button rows fit within FIND2_X */
894 if (l1 + 9 > FIND2_X)
895 FIND2_X = l1 + 9;
896 if (l2 + 8 > FIND2_X)
897 FIND2_X = l2 + 8;
899 /* compute amount of space between buttons for each row */
900 r1 = (FIND2_X - 4 - l1) % 5;
901 l1 = (FIND2_X - 4 - l1) / 5;
902 r2 = (FIND2_X - 4 - l2) % 4;
903 l2 = (FIND2_X - 4 - l2) / 4;
905 /* ...and finally, place buttons */
906 fbuts[2].x = 2 + r1 / 2 + l1;
907 fbuts[3].x = fbuts[2].x + fbuts[2].len + l1;
908 fbuts[0].x = fbuts[3].x + fbuts[3].len + l1;
909 fbuts[4].x = fbuts[0].x + l0 + l1;
910 fbuts[5].x = 2 + r2 / 2 + l2;
911 fbuts[6].x = fbuts[5].x + fbuts[5].len + l2;
912 fbuts[7].x = fbuts[6].x + fbuts[6].len + l2;
915 find_dlg =
916 create_dlg (0, 0, FIND2_Y, FIND2_X, dialog_colors, find_callback,
917 "[Find File]", _("Find File"), DLG_CENTER | DLG_REVERSE);
919 add_widget (find_dlg,
920 button_new (FIND2_Y - 3, fbuts[7].x, B_VIEW, NORMAL_BUTTON,
921 fbuts[7].text, find_do_edit_file));
922 add_widget (find_dlg,
923 button_new (FIND2_Y - 3, fbuts[6].x, B_VIEW, NORMAL_BUTTON,
924 fbuts[6].text, find_do_view_file));
925 add_widget (find_dlg,
926 button_new (FIND2_Y - 3, fbuts[5].x, B_PANELIZE,
927 NORMAL_BUTTON, fbuts[5].text, 0));
929 add_widget (find_dlg,
930 button_new (FIND2_Y - 4, fbuts[4].x, B_CANCEL,
931 NORMAL_BUTTON, fbuts[4].text, 0));
932 stop_button =
933 button_new (FIND2_Y - 4, fbuts[0].x, B_STOP, NORMAL_BUTTON,
934 fbuts[0].text, start_stop);
935 add_widget (find_dlg, stop_button);
936 add_widget (find_dlg,
937 button_new (FIND2_Y - 4, fbuts[3].x, B_AGAIN,
938 NORMAL_BUTTON, fbuts[3].text, 0));
939 add_widget (find_dlg,
940 button_new (FIND2_Y - 4, fbuts[2].x, B_ENTER,
941 DEFPUSH_BUTTON, fbuts[2].text, 0));
943 status_label = label_new (FIND2_Y - 6, 4, _("Searching"));
944 add_widget (find_dlg, status_label);
946 find_list =
947 listbox_new (2, 2, FIND2_X - 4, FIND2_Y - 9, 0);
948 add_widget (find_dlg, find_list);
951 static int
952 run_process (void)
954 resuming = 0;
955 set_idle_proc (find_dlg, 1);
956 run_dlg (find_dlg);
957 return find_dlg->ret_value;
960 static void
961 kill_gui (void)
963 set_idle_proc (find_dlg, 0);
964 destroy_dlg (find_dlg);
967 static int
968 find_file (char *start_dir, char *pattern, char *content, char **dirname,
969 char **filename)
971 int return_value = 0;
972 char *dir;
973 char *dir_tmp, *file_tmp;
975 setup_gui ();
977 /* FIXME: Need to cleanup this, this ought to be passed non-globaly */
978 find_pattern = pattern;
979 content_pattern = (content != NULL && str_is_valid_string (content))
980 ? str_create_search_needle (content, case_sensitive)
981 : NULL;
983 init_find_vars ();
984 push_directory (start_dir);
986 return_value = run_process ();
988 /* Remove all the items in the stack */
989 while ((dir = pop_directory ()) != NULL)
990 g_free (dir);
992 get_list_info (&file_tmp, &dir_tmp);
994 if (dir_tmp)
995 *dirname = g_strdup (dir_tmp);
996 if (file_tmp)
997 *filename = g_strdup (file_tmp);
999 if (return_value == B_PANELIZE && *filename) {
1000 int status, link_to_dir, stale_link;
1001 int next_free = 0;
1002 int i;
1003 struct stat st;
1004 WLEntry *entry = find_list->list;
1005 dir_list *list = &current_panel->dir;
1006 char *name;
1008 for (i = 0; entry && i < find_list->count;
1009 entry = entry->next, i++) {
1010 const char *filename;
1012 if (!entry->text || !entry->data)
1013 continue;
1015 if (content_pattern)
1016 filename = strchr (entry->text + 4, ':') + 1;
1017 else
1018 filename = entry->text + 4;
1020 name = make_fullname (entry->data, filename);
1021 status =
1022 handle_path (list, name, &st, next_free, &link_to_dir,
1023 &stale_link);
1024 if (status == 0) {
1025 g_free (name);
1026 continue;
1028 if (status == -1) {
1029 g_free (name);
1030 break;
1033 /* don't add files more than once to the panel */
1034 if (content_pattern && next_free > 0) {
1035 if (strcmp (list->list[next_free - 1].fname, name) == 0) {
1036 g_free (name);
1037 continue;
1041 if (!next_free) /* first turn i.e clean old list */
1042 panel_clean_dir (current_panel);
1043 list->list[next_free].fnamelen = strlen (name);
1044 list->list[next_free].fname = name;
1045 list->list[next_free].f.marked = 0;
1046 list->list[next_free].f.link_to_dir = link_to_dir;
1047 list->list[next_free].f.stale_link = stale_link;
1048 list->list[next_free].f.dir_size_computed = 0;
1049 list->list[next_free].st = st;
1050 list->list[next_free].sort_key = NULL;
1051 list->list[next_free].second_sort_key = NULL;
1052 next_free++;
1053 if (!(next_free & 15))
1054 rotate_dash ();
1056 if (next_free) {
1057 current_panel->count = next_free;
1058 current_panel->is_panelized = 1;
1059 /* Done by panel_clean_dir a few lines above
1060 current_panel->dirs_marked = 0;
1061 current_panel->marked = 0;
1062 current_panel->total = 0;
1063 current_panel->top_file = 0;
1064 current_panel->selected = 0; */
1066 if (start_dir[0] == PATH_SEP) {
1067 strcpy (current_panel->cwd, PATH_SEP_STR);
1068 chdir (PATH_SEP_STR);
1073 if (content_pattern != NULL) str_release_search_needle (content_pattern, case_sensitive);
1074 kill_gui ();
1075 do_search (0); /* force do_search to release resources */
1076 g_free (old_dir);
1077 old_dir = 0;
1079 return return_value;
1082 void
1083 do_find (void)
1085 char *start_dir = NULL, *pattern = NULL, *content = NULL;
1086 char *filename, *dirname;
1087 int v, dir_and_file_set;
1088 regex_t rx; /* Compiled content_pattern to search inside files */
1090 for (r = &rx; find_parameters (&start_dir, &pattern, &content); r = &rx){
1092 dirname = filename = NULL;
1093 is_start = 0;
1094 v = find_file (start_dir, pattern, content, &dirname, &filename);
1095 g_free (start_dir);
1096 g_free (pattern);
1097 if (find_regex_flag && r)
1098 regfree (r);
1100 if (v == B_ENTER){
1101 if (dirname || filename){
1102 if (dirname){
1103 do_cd (dirname, cd_exact);
1104 if (filename)
1105 try_to_select (current_panel, filename + (content ?
1106 (strchr (filename + 4, ':') - filename + 1) : 4) );
1107 } else if (filename)
1108 do_cd (filename, cd_exact);
1109 select_item (current_panel);
1111 g_free (dirname);
1112 g_free (filename);
1113 break;
1115 g_free (content);
1116 dir_and_file_set = dirname && filename;
1117 g_free (dirname);
1118 g_free (filename);
1119 if (v == B_CANCEL)
1120 break;
1122 if (v == B_PANELIZE){
1123 if (dir_and_file_set){
1124 try_to_select (current_panel, NULL);
1125 panel_re_sort (current_panel);
1126 try_to_select (current_panel, NULL);
1128 break;