src/find.c: Micro fix for 'Skip hidden' shortcut
[midnight-commander.git] / src / find.c
blobd025f41b0f5ce6122bcfd833f3d2c0c8efe88bee
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"
37 /* Dialog manager and widgets */
38 #include "dialog.h"
39 #include "widget.h"
41 #include "dir.h"
42 #include "panel.h" /* current_panel */
43 #include "main.h" /* do_cd, try_to_select */
44 #include "wtools.h"
45 #include "cmd.h" /* view_file_at_line */
46 #include "boxes.h"
47 #include "key.h"
49 /* Size of the find parameters window */
50 #define FIND_Y 16
51 static int FIND_X = 50;
53 /* Size of the find window */
54 #define FIND2_Y (LINES - 4)
56 static int FIND2_X = 64;
57 #define FIND2_X_USE (FIND2_X - 20)
59 /* A couple of extra messages we need */
60 enum {
61 B_STOP = B_USER + 1,
62 B_AGAIN,
63 B_PANELIZE,
64 B_TREE,
65 B_VIEW
68 typedef enum {
69 FIND_CONT,
70 FIND_SUSPEND,
71 FIND_ABORT
72 } FindProgressStatus;
74 /* List of directories to be ignored, separated by ':' */
75 char *find_ignore_dirs = 0;
77 static WInput *in_start; /* Start path */
78 static WInput *in_name; /* Pattern to search */
79 static WInput *in_with; /* Text inside filename */
80 static WCheck *case_sense; /* "case sensitive" checkbox */
81 static WCheck *find_regex_cbox; /* [x] find regular expression */
83 static int running = 0; /* nice flag */
84 static char *find_pattern; /* Pattern to search */
85 static char *content_pattern; /* pattern to search inside files; if
86 find_regex_flag is true, it contains the
87 regex pattern, else the search string. */
88 static int count; /* Number of files displayed */
89 static int matches; /* Number of matches */
90 static int is_start; /* Status of the start/stop toggle button */
91 static char *old_dir;
93 /* Where did we stop */
94 static int resuming;
95 static int last_line;
96 static int last_pos;
98 static Dlg_head *find_dlg; /* The dialog */
100 static WButton *stop_button; /* pointer to the stop button */
101 static WLabel *status_label; /* Finished, Searching etc. */
102 static WListbox *find_list; /* Listbox with the file list */
104 /* This keeps track of the directory stack */
105 typedef struct dir_stack {
106 char *name;
107 struct dir_stack *prev;
108 } dir_stack ;
110 static dir_stack *dir_stack_base = 0;
112 static struct {
113 const char* text;
114 int len; /* length including space and brackets */
115 int x;
116 } fbuts [] = {
117 { N_("&Suspend"), 11, 29 },
118 { N_("Con&tinue"), 12, 29 },
119 { N_("&Chdir"), 11, 3 },
120 { N_("&Again"), 9, 17 },
121 { N_("&Quit"), 8, 43 },
122 { N_("Pane&lize"), 12, 3 },
123 { N_("&View - F3"), 13, 20 },
124 { N_("&Edit - F4"), 13, 38 }
127 static inline char * add_to_list (const char *text, void *data) {
128 return listbox_add_item (find_list, 0, 0, text, data);
130 static inline void stop_idle (void *data) {
131 set_idle_proc (data, 0);
133 static inline void status_update (const char *text) {
134 label_set_text (status_label, text);
136 static void get_list_info (char **file, char **dir) {
137 listbox_get_current (find_list, file, dir);
140 /* FIXME: r should be local variable */
141 static regex_t *r; /* Pointer to compiled content_pattern */
143 static int case_sensitive = 1;
144 static gboolean find_regex_flag = TRUE;
145 static gboolean skip_hidden_flag = FALSE;
146 static int find_recursively = 1;
149 * Callback for the parameter dialog.
150 * Validate regex, prevent closing the dialog if it's invalid.
152 static cb_ret_t
153 find_parm_callback (struct Dlg_head *h, dlg_msg_t msg, int parm)
155 int flags;
157 switch (msg) {
158 case DLG_VALIDATE:
159 if ((h->ret_value != B_ENTER) || !in_with->buffer[0]
160 || !(find_regex_cbox->state & C_BOOL))
161 return MSG_HANDLED;
163 flags = REG_EXTENDED | REG_NOSUB;
165 if (!(case_sense->state & C_BOOL))
166 flags |= REG_ICASE;
168 if (regcomp (r, in_with->buffer, flags)) {
169 message (D_ERROR, MSG_ERROR, _(" Malformed regular expression "));
170 dlg_select_widget (in_with);
171 h->running = 1; /* Don't stop the dialog */
173 return MSG_HANDLED;
175 default:
176 return default_dlg_callback (h, msg, parm);
181 * find_parameters: gets information from the user
183 * If the return value is true, then the following holds:
185 * START_DIR and PATTERN are pointers to char * and upon return they
186 * contain the information provided by the user.
188 * CONTENT holds a strdup of the contents specified by the user if he
189 * asked for them or 0 if not (note, this is different from the
190 * behavior for the other two parameters.
193 static int
194 find_parameters (char **start_dir, char **pattern, char **content)
196 int return_value;
197 char *temp_dir;
198 static const char *case_label = N_("case &Sensitive");
199 static const char *recurs_label = N_("&Find recursively");
200 static const char *skip_hidden_label = N_("S&kip hidden");
201 static const char *regexp_label = N_("&Regular expression");
203 WCheck *recursively_cbox;
204 WCheck *skip_hidden_cbox;
206 static char *in_contents = NULL;
207 static char *in_start_dir = NULL;
208 static char *in_start_name = NULL;
210 static const char *labs[] =
211 { N_("Start at:"), N_("Filename:"), N_("Content: ") };
212 static const char *buts[] = { N_("&OK"), N_("&Tree"), N_("&Cancel") };
213 static int ilen = 30, istart = 14;
214 static int b0 = 3, b1 = 16, b2 = 36;
216 #ifdef ENABLE_NLS
217 static int i18n_flag = 0;
219 if (!i18n_flag) {
220 register int i = sizeof (labs) / sizeof (labs[0]);
221 int l1, maxlen = 0;
223 while (i--) {
224 l1 = strlen (labs[i] = _(labs[i]));
225 if (l1 > maxlen)
226 maxlen = l1;
228 i = maxlen + ilen + 7;
229 if (i > FIND_X)
230 FIND_X = i;
232 for (i = sizeof (buts) / sizeof (buts[0]), l1 = 0; i--;) {
233 l1 += strlen (buts[i] = _(buts[i]));
235 l1 += 21;
236 if (l1 > FIND_X)
237 FIND_X = l1;
239 ilen = FIND_X - 7 - maxlen; /* for the case of very long buttons :) */
240 istart = FIND_X - 3 - ilen;
242 b1 = b0 + strlen (buts[0]) + 7;
243 b2 = FIND_X - (strlen (buts[2]) + 6);
245 i18n_flag = 1;
246 case_label = _(case_label);
247 recurs_label = _(recurs_label);
249 #endif /* ENABLE_NLS */
251 find_par_start:
252 if (!in_start_dir)
253 in_start_dir = g_strdup (".");
254 if (!in_start_name)
255 in_start_name = g_strdup (easy_patterns ? "*" : ".");
256 if (!in_contents)
257 in_contents = g_strdup ("");
259 find_dlg =
260 create_dlg (0, 0, FIND_Y, FIND_X, dialog_colors,
261 find_parm_callback, "[Find File]", _("Find File"),
262 DLG_CENTER | DLG_REVERSE);
264 add_widget (find_dlg,
265 button_new (FIND_Y - 3, b2, B_CANCEL, NORMAL_BUTTON, buts[2], 0));
266 add_widget (find_dlg,
267 button_new (FIND_Y - 3, b1, B_TREE, NORMAL_BUTTON, buts[1], 0));
268 add_widget (find_dlg,
269 button_new (FIND_Y - 3, b0, B_ENTER, DEFPUSH_BUTTON, buts[0], 0));
271 recursively_cbox = check_new (6, istart, find_recursively, recurs_label);
272 skip_hidden_cbox = check_new (7, istart, skip_hidden_flag, skip_hidden_label);
273 find_regex_cbox = check_new (11, istart, find_regex_flag, regexp_label);
274 case_sense = check_new (10, istart, case_sensitive, case_label);
276 in_with = input_new (9, istart, INPUT_COLOR, ilen, in_contents, "content", INPUT_COMPLETE_DEFAULT);
277 in_name = input_new (5, istart, INPUT_COLOR, ilen, in_start_name, "name", INPUT_COMPLETE_DEFAULT);
278 in_start = input_new (3, istart, INPUT_COLOR, ilen, in_start_dir, "start", INPUT_COMPLETE_DEFAULT);
280 add_widget (find_dlg, find_regex_cbox);
281 add_widget (find_dlg, case_sense);
282 add_widget (find_dlg, in_with);
283 add_widget (find_dlg, skip_hidden_cbox);
284 add_widget (find_dlg, recursively_cbox);
285 add_widget (find_dlg, in_name);
286 add_widget (find_dlg, in_start);
288 add_widget (find_dlg, label_new (8, 3, labs[2]));
289 add_widget (find_dlg, label_new (5, 3, labs[1]));
290 add_widget (find_dlg, label_new (3, 3, labs[0]));
292 dlg_select_widget (in_name);
294 run_dlg (find_dlg);
296 switch (find_dlg->ret_value) {
297 case B_CANCEL:
298 return_value = 0;
299 break;
301 case B_TREE:
302 temp_dir = g_strdup (in_start->buffer);
303 case_sensitive = case_sense->state & C_BOOL;
304 find_regex_flag = find_regex_cbox->state & C_BOOL;
305 find_recursively = recursively_cbox->state & C_BOOL;
306 skip_hidden_flag = skip_hidden_cbox->state & C_BOOL;
307 destroy_dlg (find_dlg);
308 g_free (in_start_dir);
309 if (strcmp (temp_dir, ".") == 0) {
310 g_free (temp_dir);
311 temp_dir = g_strdup (current_panel->cwd);
313 in_start_dir = tree_box (temp_dir);
314 if (in_start_dir)
315 g_free (temp_dir);
316 else
317 in_start_dir = temp_dir;
318 /* Warning: Dreadful goto */
319 goto find_par_start;
320 break;
322 default:
323 g_free (in_contents);
324 if (in_with->buffer[0]) {
325 *content = g_strdup (in_with->buffer);
326 in_contents = g_strdup (*content);
327 } else {
328 *content = in_contents = NULL;
329 r = 0;
332 case_sensitive = case_sense->state & C_BOOL;
333 find_regex_flag = find_regex_cbox->state & C_BOOL;
334 find_recursively = recursively_cbox->state & C_BOOL;
335 skip_hidden_flag = skip_hidden_cbox->state & C_BOOL;
336 return_value = 1;
337 *start_dir = g_strdup (in_start->buffer);
338 *pattern = g_strdup (in_name->buffer);
340 g_free (in_start_dir);
341 in_start_dir = g_strdup (*start_dir);
342 g_free (in_start_name);
343 in_start_name = g_strdup (*pattern);
346 destroy_dlg (find_dlg);
348 return return_value;
351 static void
352 push_directory (const char *dir)
354 dir_stack *new;
356 new = g_new (dir_stack, 1);
357 new->name = concat_dir_and_file (dir, "");
358 new->prev = dir_stack_base;
359 dir_stack_base = new;
362 static char*
363 pop_directory (void)
365 char *name;
366 dir_stack *next;
368 if (dir_stack_base){
369 name = dir_stack_base->name;
370 next = dir_stack_base->prev;
371 g_free (dir_stack_base);
372 dir_stack_base = next;
373 return name;
374 } else
375 return 0;
378 static void
379 insert_file (const char *dir, const char *file)
381 char *tmp_name;
382 static char *dirname;
384 while (dir [0] == PATH_SEP && dir [1] == PATH_SEP)
385 dir++;
387 if (old_dir){
388 if (strcmp (old_dir, dir)){
389 g_free (old_dir);
390 old_dir = g_strdup (dir);
391 dirname = add_to_list (dir, NULL);
393 } else {
394 old_dir = g_strdup (dir);
395 dirname = add_to_list (dir, NULL);
398 tmp_name = g_strconcat (" ", file, (char *) NULL);
399 add_to_list (tmp_name, dirname);
400 g_free (tmp_name);
403 static void
404 find_add_match (Dlg_head *h, const char *dir, const char *file)
406 int p = ++matches & 7;
408 (void) h;
410 insert_file (dir, file);
412 /* Scroll nicely */
413 if (!p)
414 listbox_select_last (find_list, 1);
415 else
416 listbox_select_last (find_list, 0);
417 /* Updates the current listing */
418 send_message (&find_list->widget, WIDGET_DRAW, 0);
419 if (p == 7)
420 mc_refresh ();
424 * get_line_at:
426 * Returns malloced null-terminated line from file file_fd.
427 * Input is buffered in buf_size long buffer.
428 * Current pos in buf is stored in pos.
429 * n_read - number of read chars.
430 * has_newline - is there newline ?
432 static char *
433 get_line_at (int file_fd, char *buf, int *pos, int *n_read, int buf_size,
434 int *has_newline)
436 char *buffer = 0;
437 int buffer_size = 0;
438 char ch = 0;
439 int i = 0;
441 for (;;) {
442 if (*pos >= *n_read) {
443 *pos = 0;
444 if ((*n_read = mc_read (file_fd, buf, buf_size)) <= 0)
445 break;
448 ch = buf[(*pos)++];
449 if (ch == 0) {
450 /* skip possible leading zero(s) */
451 if (i == 0)
452 continue;
453 else
454 break;
457 if (i >= buffer_size - 1) {
458 buffer = g_realloc (buffer, buffer_size += 80);
460 /* Strip newline */
461 if (ch == '\n')
462 break;
464 buffer[i++] = ch;
467 *has_newline = ch ? 1 : 0;
469 if (buffer) {
470 buffer[i] = 0;
473 return buffer;
476 static FindProgressStatus
477 check_find_events(Dlg_head *h)
479 Gpm_Event event;
480 int c;
482 c = get_event (&event, h->mouse_status == MOU_REPEAT, 0);
483 if (c != EV_NONE) {
484 dlg_process_event (h, c, &event);
485 if (h->ret_value == B_ENTER
486 || h->ret_value == B_CANCEL
487 || h->ret_value == B_AGAIN
488 || h->ret_value == B_PANELIZE) {
489 /* dialog terminated */
490 return FIND_ABORT;
492 if (!(h->flags & DLG_WANT_IDLE)) {
493 /* searching suspended */
494 return FIND_SUSPEND;
498 return FIND_CONT;
502 * search_content:
504 * Search the global (FIXME) regexp compiled content_pattern string in the
505 * DIRECTORY/FILE. It will add the found entries to the find listbox.
507 * returns 0 if do_search should look for another file
508 * 1 if do_search should exit and proceed to the event handler
510 static int
511 search_content (Dlg_head *h, const char *directory, const char *filename)
513 struct stat s;
514 char buffer [BUF_SMALL];
515 char *fname;
516 int file_fd;
517 int ret_val = 0;
519 fname = concat_dir_and_file (directory, filename);
521 if (mc_stat (fname, &s) != 0 || !S_ISREG (s.st_mode)){
522 g_free (fname);
523 return 0;
526 file_fd = mc_open (fname, O_RDONLY);
527 g_free (fname);
529 if (file_fd == -1)
530 return 0;
532 g_snprintf (buffer, sizeof (buffer), _("Grepping in %s"), name_trunc (filename, FIND2_X_USE));
534 status_update (buffer);
535 mc_refresh ();
537 enable_interrupt_key ();
538 got_interrupt ();
541 int line = 1;
542 int pos = 0;
543 int n_read = 0;
544 int has_newline;
545 char *p;
546 int found = 0;
547 typedef const char * (*search_fn) (const char *, const char *);
548 search_fn search_func;
550 if (resuming) {
551 /* We've been previously suspended, start from the previous position */
552 resuming = 0;
553 line = last_line;
554 pos = last_pos;
557 search_func = (case_sensitive) ? cstrstr : cstrcasestr;
559 while ((p = get_line_at (file_fd, buffer, &pos, &n_read, sizeof (buffer), &has_newline)) && (ret_val == 0)){
560 if (found == 0){ /* Search in binary line once */
561 if (find_regex_flag) {
562 if (regexec (r, p, 1, 0, 0) == 0){
563 g_free (p);
564 p = g_strdup_printf ("%d:%s", line, filename);
565 find_add_match (h, directory, p);
566 found = 1;
568 } else {
569 if (search_func (p, content_pattern) != NULL) {
570 char *match = g_strdup_printf("%d:%s", line, filename);
571 find_add_match (h, directory, match);
572 found = TRUE;
576 if (has_newline){
577 line++;
578 found = 0;
580 g_free (p);
582 if ((line & 0xff) == 0) {
583 FindProgressStatus res;
584 res = check_find_events(h);
585 switch (res) {
586 case FIND_ABORT:
587 stop_idle (h);
588 ret_val = 1;
589 break;
590 case FIND_SUSPEND:
591 resuming = 1;
592 last_line = line;
593 last_pos = pos;
594 ret_val = 1;
595 break;
596 default:
597 break;
603 disable_interrupt_key ();
604 mc_close (file_fd);
605 return ret_val;
608 static int
609 do_search (struct Dlg_head *h)
611 static struct dirent *dp = 0;
612 static DIR *dirp = 0;
613 static char *directory;
614 struct stat tmp_stat;
615 static int pos;
616 static int subdirs_left = 0;
618 if (!h) { /* someone forces me to close dirp */
619 if (dirp) {
620 mc_closedir (dirp);
621 dirp = 0;
623 g_free (directory);
624 directory = NULL;
625 dp = 0;
626 return 1;
628 do_search_begin:
629 while (!dp){
631 if (dirp){
632 mc_closedir (dirp);
633 dirp = 0;
636 while (!dirp){
637 char *tmp;
639 attrset (REVERSE_COLOR);
640 while (1) {
641 tmp = pop_directory ();
642 if (!tmp){
643 running = 0;
644 status_update (_("Finished"));
645 stop_idle (h);
646 return 0;
648 if (find_ignore_dirs){
649 int found;
650 char *temp_dir = g_strconcat (":", tmp, ":", (char *) NULL);
652 found = strstr (find_ignore_dirs, temp_dir) != 0;
653 g_free (temp_dir);
654 if (found)
655 g_free (tmp);
656 else
657 break;
658 } else
659 break;
662 g_free (directory);
663 directory = tmp;
665 if (verbose){
666 char buffer [BUF_SMALL];
668 g_snprintf (buffer, sizeof (buffer), _("Searching %s"),
669 name_trunc (directory, FIND2_X_USE));
670 status_update (buffer);
672 /* mc_stat should not be called after mc_opendir
673 because vfs_s_opendir modifies the st_nlink
675 if (!mc_stat (directory, &tmp_stat))
676 subdirs_left = tmp_stat.st_nlink - 2;
677 else
678 subdirs_left = 0;
679 /* Commented out as unnecessary
680 if (subdirs_left < 0)
681 subdirs_left = MAXINT;
683 dirp = mc_opendir (directory);
684 } /* while (!dirp) */
685 dp = mc_readdir (dirp);
686 } /* while (!dp) */
688 if (strcmp (dp->d_name, ".") == 0 ||
689 strcmp (dp->d_name, "..") == 0){
690 dp = mc_readdir (dirp);
691 return 1;
694 if (!(skip_hidden_flag && dp->d_name[0] == '.')) {
695 if (subdirs_left && find_recursively && directory) { /* Can directory be NULL ? */
696 char *tmp_name = concat_dir_and_file (directory, dp->d_name);
697 if (!mc_lstat (tmp_name, &tmp_stat)
698 && S_ISDIR (tmp_stat.st_mode)) {
699 push_directory (tmp_name);
700 subdirs_left--;
702 g_free (tmp_name);
705 if (regexp_match (find_pattern, dp->d_name, match_file)){
706 if (content_pattern) {
707 if (search_content (h, directory, dp->d_name)) {
708 return 1;
710 } else
711 find_add_match (h, directory, dp->d_name);
715 dp = mc_readdir (dirp);
717 /* Displays the nice dot */
718 count++;
719 if (!(count & 31)){
720 /* For nice updating */
721 const char *rotating_dash = "|/-\\";
723 if (verbose){
724 pos = (pos + 1) % 4;
725 attrset (DLG_NORMALC (h));
726 dlg_move (h, FIND2_Y-6, FIND2_X - 4);
727 addch (rotating_dash [pos]);
728 mc_refresh ();
730 } else
731 goto do_search_begin;
732 return 1;
735 static void
736 init_find_vars (void)
738 char *dir;
740 g_free (old_dir);
741 old_dir = 0;
742 count = 0;
743 matches = 0;
745 /* Remove all the items in the stack */
746 while ((dir = pop_directory ()) != NULL)
747 g_free (dir);
750 static char *
751 make_fullname (const char *dirname, const char *filename)
754 if (strcmp(dirname, ".") == 0 || strcmp(dirname, "."PATH_SEP_STR) == 0)
755 return g_strdup (filename);
756 if (strncmp(dirname, "."PATH_SEP_STR, 2) == 0)
757 return concat_dir_and_file (dirname + 2, filename);
758 return concat_dir_and_file (dirname, filename);
761 static void
762 find_do_view_edit (int unparsed_view, int edit, char *dir, char *file)
764 char *fullname;
765 const char *filename;
766 int line;
768 if (content_pattern){
769 filename = strchr (file + 4, ':') + 1;
770 line = atoi (file + 4);
771 } else {
772 filename = file + 4;
773 line = 0;
776 fullname = make_fullname (dir, filename);
777 if (edit)
778 do_edit_at_line (fullname, line);
779 else
780 view_file_at_line (fullname, unparsed_view, use_internal_view, line);
781 g_free (fullname);
784 static int
785 view_edit_currently_selected_file (int unparsed_view, int edit)
787 WLEntry *entry = find_list->current;
788 char *dir;
790 if (!entry)
791 return MSG_NOT_HANDLED;
793 dir = entry->data;
795 if (!entry->text || !dir)
796 return MSG_NOT_HANDLED;
798 find_do_view_edit (unparsed_view, edit, dir, entry->text);
799 return MSG_HANDLED;
802 static cb_ret_t
803 find_callback (struct Dlg_head *h, dlg_msg_t msg, int parm)
805 switch (msg) {
806 case DLG_KEY:
807 if (parm == KEY_F (3) || parm == KEY_F (13)) {
808 int unparsed_view = (parm == KEY_F (13));
809 return view_edit_currently_selected_file (unparsed_view, 0);
811 if (parm == KEY_F (4)) {
812 return view_edit_currently_selected_file (0, 1);
814 return MSG_NOT_HANDLED;
816 case DLG_IDLE:
817 do_search (h);
818 return MSG_HANDLED;
820 default:
821 return default_dlg_callback (h, msg, parm);
825 /* Handles the Stop/Start button in the find window */
826 static int
827 start_stop (int button)
829 (void) button;
831 running = is_start;
832 set_idle_proc (find_dlg, running);
833 is_start = !is_start;
835 status_update (is_start ? _("Stopped") : _("Searching"));
836 button_set_text (stop_button, fbuts [is_start].text);
838 return 0;
841 /* Handle view command, when invoked as a button */
842 static int
843 find_do_view_file (int button)
845 (void) button;
847 view_edit_currently_selected_file (0, 0);
848 return 0;
851 /* Handle edit command, when invoked as a button */
852 static int
853 find_do_edit_file (int button)
855 (void) button;
857 view_edit_currently_selected_file (0, 1);
858 return 0;
861 static void
862 setup_gui (void)
864 #ifdef ENABLE_NLS
865 static int i18n_flag = 0;
866 if (!i18n_flag) {
867 register int i = sizeof (fbuts) / sizeof (fbuts[0]);
868 while (i--)
869 fbuts[i].len = strlen (fbuts[i].text = _(fbuts[i].text)) + 3;
870 fbuts[2].len += 2; /* DEFPUSH_BUTTON */
871 i18n_flag = 1;
873 #endif /* ENABLE_NLS */
876 * Dynamically place buttons centered within current window size
879 int l0 = max (fbuts[0].len, fbuts[1].len);
880 int l1 = fbuts[2].len + fbuts[3].len + l0 + fbuts[4].len;
881 int l2 = fbuts[5].len + fbuts[6].len + fbuts[7].len;
882 int r1, r2;
884 FIND2_X = COLS - 16;
886 /* Check, if both button rows fit within FIND2_X */
887 if (l1 + 9 > FIND2_X)
888 FIND2_X = l1 + 9;
889 if (l2 + 8 > FIND2_X)
890 FIND2_X = l2 + 8;
892 /* compute amount of space between buttons for each row */
893 r1 = (FIND2_X - 4 - l1) % 5;
894 l1 = (FIND2_X - 4 - l1) / 5;
895 r2 = (FIND2_X - 4 - l2) % 4;
896 l2 = (FIND2_X - 4 - l2) / 4;
898 /* ...and finally, place buttons */
899 fbuts[2].x = 2 + r1 / 2 + l1;
900 fbuts[3].x = fbuts[2].x + fbuts[2].len + l1;
901 fbuts[0].x = fbuts[3].x + fbuts[3].len + l1;
902 fbuts[4].x = fbuts[0].x + l0 + l1;
903 fbuts[5].x = 2 + r2 / 2 + l2;
904 fbuts[6].x = fbuts[5].x + fbuts[5].len + l2;
905 fbuts[7].x = fbuts[6].x + fbuts[6].len + l2;
908 find_dlg =
909 create_dlg (0, 0, FIND2_Y, FIND2_X, dialog_colors, find_callback,
910 "[Find File]", _("Find File"), DLG_CENTER | DLG_REVERSE);
912 add_widget (find_dlg,
913 button_new (FIND2_Y - 3, fbuts[7].x, B_VIEW, NORMAL_BUTTON,
914 fbuts[7].text, find_do_edit_file));
915 add_widget (find_dlg,
916 button_new (FIND2_Y - 3, fbuts[6].x, B_VIEW, NORMAL_BUTTON,
917 fbuts[6].text, find_do_view_file));
918 add_widget (find_dlg,
919 button_new (FIND2_Y - 3, fbuts[5].x, B_PANELIZE,
920 NORMAL_BUTTON, fbuts[5].text, 0));
922 add_widget (find_dlg,
923 button_new (FIND2_Y - 4, fbuts[4].x, B_CANCEL,
924 NORMAL_BUTTON, fbuts[4].text, 0));
925 stop_button =
926 button_new (FIND2_Y - 4, fbuts[0].x, B_STOP, NORMAL_BUTTON,
927 fbuts[0].text, start_stop);
928 add_widget (find_dlg, stop_button);
929 add_widget (find_dlg,
930 button_new (FIND2_Y - 4, fbuts[3].x, B_AGAIN,
931 NORMAL_BUTTON, fbuts[3].text, 0));
932 add_widget (find_dlg,
933 button_new (FIND2_Y - 4, fbuts[2].x, B_ENTER,
934 DEFPUSH_BUTTON, fbuts[2].text, 0));
936 status_label = label_new (FIND2_Y - 6, 4, _("Searching"));
937 add_widget (find_dlg, status_label);
939 find_list =
940 listbox_new (2, 2, FIND2_X - 4, FIND2_Y - 9, 0);
941 add_widget (find_dlg, find_list);
944 static int
945 run_process (void)
947 resuming = 0;
948 set_idle_proc (find_dlg, 1);
949 run_dlg (find_dlg);
950 return find_dlg->ret_value;
953 static void
954 kill_gui (void)
956 set_idle_proc (find_dlg, 0);
957 destroy_dlg (find_dlg);
960 static int
961 find_file (char *start_dir, char *pattern, char *content, char **dirname,
962 char **filename)
964 int return_value = 0;
965 char *dir;
966 char *dir_tmp, *file_tmp;
968 setup_gui ();
970 /* FIXME: Need to cleanup this, this ought to be passed non-globaly */
971 find_pattern = pattern;
972 content_pattern = content;
974 init_find_vars ();
975 push_directory (start_dir);
977 return_value = run_process ();
979 /* Remove all the items in the stack */
980 while ((dir = pop_directory ()) != NULL)
981 g_free (dir);
983 get_list_info (&file_tmp, &dir_tmp);
985 if (dir_tmp)
986 *dirname = g_strdup (dir_tmp);
987 if (file_tmp)
988 *filename = g_strdup (file_tmp);
990 if (return_value == B_PANELIZE && *filename) {
991 int status, link_to_dir, stale_link;
992 int next_free = 0;
993 int i;
994 struct stat st;
995 WLEntry *entry = find_list->list;
996 dir_list *list = &current_panel->dir;
997 char *name;
999 for (i = 0; entry && i < find_list->count;
1000 entry = entry->next, i++) {
1001 const char *filename;
1003 if (!entry->text || !entry->data)
1004 continue;
1006 if (content_pattern)
1007 filename = strchr (entry->text + 4, ':') + 1;
1008 else
1009 filename = entry->text + 4;
1011 name = make_fullname (entry->data, filename);
1012 status =
1013 handle_path (list, name, &st, next_free, &link_to_dir,
1014 &stale_link);
1015 if (status == 0) {
1016 g_free (name);
1017 continue;
1019 if (status == -1) {
1020 g_free (name);
1021 break;
1024 /* don't add files more than once to the panel */
1025 if (content_pattern && next_free > 0) {
1026 if (strcmp (list->list[next_free - 1].fname, name) == 0) {
1027 g_free (name);
1028 continue;
1032 if (!next_free) /* first turn i.e clean old list */
1033 panel_clean_dir (current_panel);
1034 list->list[next_free].fnamelen = strlen (name);
1035 list->list[next_free].fname = name;
1036 list->list[next_free].f.marked = 0;
1037 list->list[next_free].f.link_to_dir = link_to_dir;
1038 list->list[next_free].f.stale_link = stale_link;
1039 list->list[next_free].f.dir_size_computed = 0;
1040 list->list[next_free].st = st;
1041 next_free++;
1042 if (!(next_free & 15))
1043 rotate_dash ();
1045 if (next_free) {
1046 current_panel->count = next_free;
1047 current_panel->is_panelized = 1;
1048 /* Done by panel_clean_dir a few lines above
1049 current_panel->dirs_marked = 0;
1050 current_panel->marked = 0;
1051 current_panel->total = 0;
1052 current_panel->top_file = 0;
1053 current_panel->selected = 0; */
1055 if (start_dir[0] == PATH_SEP) {
1056 strcpy (current_panel->cwd, PATH_SEP_STR);
1057 chdir (PATH_SEP_STR);
1062 kill_gui ();
1063 do_search (0); /* force do_search to release resources */
1064 g_free (old_dir);
1065 old_dir = 0;
1067 return return_value;
1070 void
1071 do_find (void)
1073 char *start_dir = NULL, *pattern = NULL, *content = NULL;
1074 char *filename, *dirname;
1075 int v, dir_and_file_set;
1076 regex_t rx; /* Compiled content_pattern to search inside files */
1078 for (r = &rx; find_parameters (&start_dir, &pattern, &content); r = &rx){
1080 dirname = filename = NULL;
1081 is_start = 0;
1082 v = find_file (start_dir, pattern, content, &dirname, &filename);
1083 g_free (start_dir);
1084 g_free (pattern);
1085 if (find_regex_flag && r)
1086 regfree (r);
1088 if (v == B_ENTER){
1089 if (dirname || filename){
1090 if (dirname){
1091 do_cd (dirname, cd_exact);
1092 if (filename)
1093 try_to_select (current_panel, filename + (content ?
1094 (strchr (filename + 4, ':') - filename + 1) : 4) );
1095 } else if (filename)
1096 do_cd (filename, cd_exact);
1097 select_item (current_panel);
1099 g_free (dirname);
1100 g_free (filename);
1101 break;
1103 g_free (content);
1104 dir_and_file_set = dirname && filename;
1105 g_free (dirname);
1106 g_free (filename);
1107 if (v == B_CANCEL)
1108 break;
1110 if (v == B_PANELIZE){
1111 if (dir_and_file_set){
1112 try_to_select (current_panel, NULL);
1113 panel_re_sort (current_panel);
1114 try_to_select (current_panel, NULL);
1116 break;