1 /* $Id: browser.c 4461 2009-12-09 17:09:37Z astyanax $ */
2 /**************************************************************************
5 * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 *
6 * Free Software Foundation, Inc. *
7 * This program is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation; either version 3, or (at your option) *
10 * any later version. *
12 * This program is distributed in the hope that it will be useful, but *
13 * WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
15 * General Public License for more details. *
17 * You should have received a copy of the GNU General Public License *
18 * along with this program; if not, write to the Free Software *
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA *
22 **************************************************************************/
31 #ifndef DISABLE_BROWSER
33 static char **filelist
= NULL
;
34 /* The list of files to display in the file browser. */
35 static size_t filelist_len
= 0;
36 /* The number of files in the list. */
38 /* The number of files that we can display per line. */
39 static int longest
= 0;
40 /* The number of columns in the longest filename in the list. */
41 static size_t selected
= 0;
42 /* The currently selected filename in the list. This variable
44 static bool search_last_file
= FALSE
;
45 /* Have we gone past the last file while searching? */
47 /* Our main file browser function. path is the tilde-expanded path we
48 * start browsing from. */
49 char *do_browser(char *path
, DIR *dir
)
53 bool meta_key
, func_key
, old_const_update
= ISSET(CONST_UPDATE
);
55 /* Whether we should abort the file browser. */
56 char *prev_dir
= NULL
;
57 /* The directory we were in, if any, before backing up via
58 * browsing to "..". */
60 /* The last answer the user typed at the statusbar prompt. */
62 /* The selected file we had before the current selected file. */
68 #if !defined(DISABLE_HELP) || !defined(DISABLE_MOUSE)
72 wnoutrefresh(bottomwin
);
76 ans
= mallocstrcpy(NULL
, "");
78 change_browser_directory
:
79 /* We go here after we select a new directory. */
81 /* Start with no key pressed. */
84 path
= mallocstrassn(path
, get_full_path(path
));
86 assert(path
!= NULL
&& path
[strlen(path
) - 1] == '/');
88 /* Get the file list, and set longest and width in the process. */
89 browser_init(path
, dir
);
91 assert(filelist
!= NULL
);
93 /* Sort the file list. */
94 qsort(filelist
, filelist_len
, sizeof(char *), diralphasort
);
96 /* If prev_dir isn't NULL, select the directory saved in it, and
97 * then blow it away. */
98 if (prev_dir
!= NULL
) {
99 browser_select_filename(prev_dir
);
103 /* Otherwise, select the first file or directory in the list. */
107 old_selected
= (size_t)-1;
114 size_t fileline
= selected
/ width
;
115 /* The line number the selected file is on. */
117 /* The path we switch to at the "Go to Directory"
120 /* Display the file list if we don't have a key, or if the
121 * selected file has changed, and set width in the process. */
122 if (kbinput
== ERR
|| old_selected
!= selected
)
125 old_selected
= selected
;
127 kbinput
= get_kbinput(edit
, &meta_key
, &func_key
);
129 #ifndef DISABLE_MOUSE
130 if (kbinput
== KEY_MOUSE
) {
132 int mouse_x
, mouse_y
;
134 /* We can click on the edit window to select a
136 if (get_mouseinput(&mouse_x
, &mouse_y
, TRUE
) == 0 &&
137 wmouse_trafo(edit
, &mouse_y
, &mouse_x
, FALSE
)) {
138 /* longest is the width of each column. There
139 * are two spaces between each column. */
140 selected
= (fileline
/ editwinrows
) *
141 (editwinrows
* width
) + (mouse_y
*
142 width
) + (mouse_x
/ (longest
+ 2));
144 /* If they clicked beyond the end of a row,
145 * select the filename at the end of that
147 if (mouse_x
> width
* (longest
+ 2))
150 /* If we're off the screen, select the last
152 if (selected
> filelist_len
- 1)
153 selected
= filelist_len
- 1;
155 /* If we selected the same filename as last
156 * time, put back the Enter key so that it's
158 if (old_selected
== selected
)
159 unget_kbinput(sc_seq_or(DO_ENTER
, 0), FALSE
, FALSE
);
162 #endif /* !DISABLE_MOUSE */
164 parse_browser_input(&kbinput
, &meta_key
, &func_key
);
165 s
= get_shortcut(MBROWSER
, &kbinput
, &meta_key
, &func_key
);
168 f
= sctofunc((sc
*) s
);
172 if (f
->scfunc
== TOTAL_REFRESH
) {
174 } else if (f
->scfunc
== DO_HELP_VOID
) {
181 /* Search for a filename. */
182 } else if (f
->scfunc
== DO_SEARCH
) {
186 /* Search for another filename. */
187 } else if (f
->scfunc
== DO_RESEARCH
) {
189 } else if (f
->scfunc
== DO_PAGE_UP
) {
190 if (selected
>= (editwinrows
+ fileline
% editwinrows
) *
192 selected
-= (editwinrows
+ fileline
% editwinrows
) *
196 } else if (f
->scfunc
== DO_PAGE_DOWN
) {
197 selected
+= (editwinrows
- fileline
% editwinrows
) *
199 if (selected
> filelist_len
- 1)
200 selected
= filelist_len
- 1;
201 } else if (f
->scfunc
== FIRST_FILE_MSG
) {
204 } else if (f
->scfunc
== LAST_FILE_MSG
) {
206 selected
= filelist_len
- 1;
207 /* Go to a specific directory. */
208 } else if (f
->scfunc
== GOTO_DIR_MSG
) {
212 #ifndef DISABLE_TABCOMP
216 &meta_key
, &func_key
,
220 browser_refresh
, N_("Go To Directory"));
223 #if !defined(DISABLE_HELP) || !defined(DISABLE_MOUSE)
226 bottombars(MBROWSER
);
228 /* If the directory begins with a newline (i.e. an
229 * encoded null), treat it as though it's blank. */
230 if (i
< 0 || *answer
== '\n') {
231 /* We canceled. Indicate that on the statusbar, and
232 * blank out ans, since we're done with it. */
233 statusbar(_("Cancelled"));
234 ans
= mallocstrcpy(ans
, "");
237 /* Put back the "Go to Directory" key and save
238 * answer in ans, so that the file list is displayed
239 * again, the prompt is displayed again, and what we
240 * typed before at the prompt is displayed again. */
241 unget_kbinput(sc_seq_or(DO_GOTOLINECOLUMN_VOID
, 0), FALSE
, FALSE
);
242 ans
= mallocstrcpy(ans
, answer
);
246 /* We have a directory. Blank out ans, since we're done
248 ans
= mallocstrcpy(ans
, "");
250 /* Convert newlines to nulls, just before we go to the
255 new_path
= real_dir_from_tilde(answer
);
257 if (new_path
[0] != '/') {
258 new_path
= charealloc(new_path
, strlen(path
) +
260 sprintf(new_path
, "%s%s", path
, answer
);
263 #ifndef DISABLE_OPERATINGDIR
264 if (check_operating_dir(new_path
, FALSE
)) {
266 _("Can't go outside of %s in restricted mode"),
273 dir
= opendir(new_path
);
275 /* We can't open this directory for some reason.
277 statusbar(_("Error reading %s: %s"), answer
,
284 /* Start over again with the new path value. */
287 goto change_browser_directory
;
288 } else if (f
->scfunc
== DO_UP_VOID
) {
289 if (selected
>= width
)
291 } else if (f
->scfunc
== DO_LEFT
) {
294 } else if (f
->scfunc
== DO_DOWN_VOID
) {
295 if (selected
+ width
<= filelist_len
- 1)
297 } else if (f
->scfunc
== DO_RIGHT
) {
298 if (selected
< filelist_len
- 1)
300 } else if (f
->scfunc
== DO_ENTER
) {
301 /* We can't move up from "/". */
302 if (strcmp(filelist
[selected
], "/..") == 0) {
303 statusbar(_("Can't move up a directory"));
308 #ifndef DISABLE_OPERATINGDIR
309 /* Note: The selected file can be outside the operating
310 * directory if it's ".." or if it's a symlink to a
311 * directory outside the operating directory. */
312 if (check_operating_dir(filelist
[selected
], FALSE
)) {
314 _("Can't go outside of %s in restricted mode"),
321 if (stat(filelist
[selected
], &st
) == -1) {
322 /* We can't open this file for some reason.
324 statusbar(_("Error reading %s: %s"),
325 filelist
[selected
], strerror(errno
));
330 /* If we've successfully opened a file, we're done, so
332 if (!S_ISDIR(st
.st_mode
)) {
333 retval
= mallocstrcpy(NULL
, filelist
[selected
]);
336 /* If we've successfully opened a directory, and it's
337 * "..", save the current directory in prev_dir, so that
338 * we can select it later. */
339 } else if (strcmp(tail(filelist
[selected
]), "..") == 0)
340 prev_dir
= mallocstrcpy(NULL
,
341 striponedir(filelist
[selected
]));
343 dir
= opendir(filelist
[selected
]);
345 /* We can't open this directory for some reason.
347 statusbar(_("Error reading %s: %s"),
348 filelist
[selected
], strerror(errno
));
353 path
= mallocstrcpy(path
, filelist
[selected
]);
355 /* Start over again with the new path value. */
356 goto change_browser_directory
;
357 /* Abort the file browser. */
358 } else if (f
->scfunc
== DO_EXIT
) {
365 if (old_const_update
)
371 free_chararray(filelist
, filelist_len
);
378 /* The file browser front end. We check to see if inpath has a
379 * directory in it. If it does, we start do_browser() from there.
380 * Otherwise, we start do_browser() from the current directory. */
381 char *do_browse_from(const char *inpath
)
385 /* This holds the tilde-expanded version of inpath. */
388 assert(inpath
!= NULL
);
390 path
= real_dir_from_tilde(inpath
);
392 /* Perhaps path is a directory. If so, we'll pass it to
393 * do_browser(). Or perhaps path is a directory / a file. If so,
394 * we'll try stripping off the last path element and passing it to
395 * do_browser(). Or perhaps path doesn't have a directory portion
396 * at all. If so, we'll just pass the current directory to
398 if (stat(path
, &st
) == -1 || !S_ISDIR(st
.st_mode
)) {
399 path
= mallocstrassn(path
, striponedir(path
));
401 if (stat(path
, &st
) == -1 || !S_ISDIR(st
.st_mode
)) {
404 path
= charalloc(PATH_MAX
+ 1);
405 path
= getcwd(path
, PATH_MAX
+ 1);
412 #ifndef DISABLE_OPERATINGDIR
413 /* If the resulting path isn't in the operating directory, use
414 * the operating directory instead. */
415 if (check_operating_dir(path
, FALSE
))
416 path
= mallocstrcpy(path
, operating_dir
);
422 /* If we can't open the path, get out. */
430 return do_browser(path
, dir
);
433 /* Set filelist to the list of files contained in the directory path,
434 * set filelist_len to the number of files in that list, set longest to
435 * the width in columns of the longest filename in that list (between 15
436 * and COLS), and set width to the number of files that we can display
437 * per line. longest needs to be at least 15 columns in order to
438 * display ".. (parent dir)", as Pico does. Assume path exists and is a
440 void browser_init(const char *path
, DIR *dir
)
442 const struct dirent
*nextdir
;
443 size_t i
= 0, path_len
= strlen(path
);
445 /* The maximum number of columns that the filenames will take
448 /* The maximum number of lines that the filenames will take
450 int filesperline
= 0;
451 /* The number of files that we can display per line. */
453 assert(path
!= NULL
&& path
[strlen(path
) - 1] == '/' && dir
!= NULL
);
455 /* Set longest to zero, just before we initialize it. */
458 while ((nextdir
= readdir(dir
)) != NULL
) {
461 /* Don't show the "." entry. */
462 if (strcmp(nextdir
->d_name
, ".") == 0)
465 d_len
= strlenpt(nextdir
->d_name
);
467 longest
= (d_len
> COLS
) ? COLS
: d_len
;
474 /* Put 10 columns' worth of blank space between columns of filenames
475 * in the list whenever possible, as Pico does. */
478 if (filelist
!= NULL
)
479 free_chararray(filelist
, filelist_len
);
483 filelist
= (char **)nmalloc(filelist_len
* sizeof(char *));
487 while ((nextdir
= readdir(dir
)) != NULL
&& i
< filelist_len
) {
488 /* Don't show the "." entry. */
489 if (strcmp(nextdir
->d_name
, ".") == 0)
492 filelist
[i
] = charalloc(path_len
+ strlen(nextdir
->d_name
) + 1);
493 sprintf(filelist
[i
], "%s%s", path
, nextdir
->d_name
);
498 /* Maybe the number of files in the directory changed between the
499 * first time we scanned and the second. i is the actual length of
500 * filelist, so record it. */
505 /* Make sure longest is between 15 and COLS. */
511 /* Set width to zero, just before we initialize it. */
514 for (i
= 0; i
< filelist_len
&& line
< editwinrows
; i
++) {
515 /* Calculate the number of columns one filename will take up. */
519 /* Add some space between the columns. */
522 /* If the next entry isn't going to fit on the current line,
523 * move to the next line. */
524 if (col
> COLS
- longest
) {
528 /* If width isn't initialized yet, and we've taken up more
529 * than one line, it means that width is equal to
532 width
= filesperline
;
536 /* If width isn't initialized yet, and we've taken up only one line,
537 * it means that width is equal to longest. */
542 /* Determine the shortcut key corresponding to the values of kbinput
543 * (the key itself), meta_key (whether the key is a meta sequence), and
544 * func_key (whether the key is a function key), if any. In the
545 * process, convert certain non-shortcut keys into their corresponding
547 void parse_browser_input(int *kbinput
, bool *meta_key
, bool *func_key
)
549 get_shortcut(MBROWSER
, kbinput
, meta_key
, func_key
);
551 /* Pico compatibility. */
555 *kbinput
= sc_seq_or(DO_PAGE_DOWN
, 0);
558 *kbinput
= sc_seq_or(DO_PAGE_UP
, 0);
562 *kbinput
= sc_seq_or(DO_HELP_VOID
, 0);
565 /* Cancel equivalent to Exit here. */
568 *kbinput
= sc_seq_or(DO_EXIT
, 0);
572 *kbinput
= sc_seq_or(GOTO_DIR_MSG
, 0);
576 *kbinput
= sc_seq_or(DO_ENTER
, 0);
580 *kbinput
= sc_seq_or(DO_SEARCH
, 0);
586 /* Set width to the number of files that we can display per line, if
587 * necessary, and display the list of files. */
588 void browser_refresh(void)
590 static int uimax_digits
= -1;
593 /* The maximum number of columns that the filenames will take
596 /* The maximum number of lines that the filenames will take
599 /* The file information that we'll display. */
601 if (uimax_digits
== -1)
602 uimax_digits
= digits(UINT_MAX
);
608 i
= width
* editwinrows
* ((selected
/ width
) / editwinrows
);
610 for (; i
< filelist_len
&& line
< editwinrows
; i
++) {
612 const char *filetail
= tail(filelist
[i
]);
613 /* The filename we display, minus the path. */
614 size_t filetaillen
= strlenpt(filetail
);
615 /* The length of the filename in columns. */
617 /* The length of the file information in columns. */
619 /* The maximum length of the file information in
620 * columns: seven for "--", "(dir)", or the file size,
621 * and 12 for "(parent dir)". */
622 bool dots
= (COLS
>= 15 && filetaillen
>= longest
-
624 /* Do we put an ellipsis before the filename? Don't set
625 * this to TRUE if we have fewer than 15 columns (i.e.
626 * one column for padding, plus seven columns for a
627 * filename other than ".."). */
628 char *disp
= display_string(filetail
, dots
? filetaillen
-
629 longest
+ foomaxlen
+ 4 : 0, longest
, FALSE
);
630 /* If we put an ellipsis before the filename, reserve
631 * one column for padding, plus seven columns for "--",
632 * "(dir)", or the file size, plus three columns for the
635 /* Start highlighting the currently selected file or
638 wattron(edit
, reverse_attr
);
640 blank_line(edit
, line
, col
, longest
);
642 /* If dots is TRUE, we will display something like
645 mvwaddstr(edit
, line
, col
, "...");
646 mvwaddstr(edit
, line
, dots
? col
+ 3 : col
, disp
);
652 /* Show information about the file. We don't want to report
653 * file sizes for links, so we use lstat(). */
654 if (lstat(filelist
[i
], &st
) == -1 || S_ISLNK(st
.st_mode
)) {
655 /* If the file doesn't exist (i.e. it's been deleted while
656 * the file browser is open), or it's a symlink that doesn't
657 * point to a directory, display "--". */
658 if (stat(filelist
[i
], &st
) == -1 || !S_ISDIR(st
.st_mode
))
659 foo
= mallocstrcpy(NULL
, "--");
660 /* If the file is a symlink that points to a directory,
661 * display it as a directory. */
663 /* TRANSLATORS: Try to keep this at most 7
665 foo
= mallocstrcpy(NULL
, _("(dir)"));
666 } else if (S_ISDIR(st
.st_mode
)) {
667 /* If the file is a directory, display it as such. */
668 if (strcmp(filetail
, "..") == 0) {
669 /* TRANSLATORS: Try to keep this at most 12
671 foo
= mallocstrcpy(NULL
, _("(parent dir)"));
674 foo
= mallocstrcpy(NULL
, _("(dir)"));
676 unsigned long result
= st
.st_size
;
679 foo
= charalloc(uimax_digits
+ 4);
682 if (st
.st_size
< (1 << 10))
685 else if (st
.st_size
< (1 << 20)) {
689 } else if (st
.st_size
< (1 << 30)) {
698 sprintf(foo
, "%4lu %cB", result
, modifier
);
701 /* Make sure foo takes up no more than foomaxlen columns. */
702 foolen
= strlenpt(foo
);
703 if (foolen
> foomaxlen
) {
704 null_at(&foo
, actual_x(foo
, foomaxlen
));
708 mvwaddstr(edit
, line
, col
- foolen
, foo
);
710 /* Finish highlighting the currently selected file or
713 wattroff(edit
, reverse_attr
);
717 /* Add some space between the columns. */
720 /* If the next entry isn't going to fit on the current line,
721 * move to the next line. */
722 if (col
> COLS
- longest
) {
727 wmove(edit
, line
, col
);
733 /* Look for needle. If we find it, set selected to its location. Note
734 * that needle must be an exact match for a file in the list. The
735 * return value specifies whether we found anything. */
736 bool browser_select_filename(const char *needle
)
741 for (currselected
= 0; currselected
< filelist_len
;
743 if (strcmp(filelist
[currselected
], needle
) == 0) {
750 selected
= currselected
;
755 /* Set up the system variables for a filename search. Return -1 if the
756 * search should be canceled (due to Cancel, a blank search string, or a
757 * failed regcomp()), return 0 on success, and return 1 on rerun calling
759 int filesearch_init(void)
763 bool meta_key
, func_key
;
765 static char *backupstring
= NULL
;
766 /* The search string we'll be using. */
768 /* If backupstring doesn't exist, initialize it to "". */
769 if (backupstring
== NULL
)
770 backupstring
= mallocstrcpy(NULL
, "");
772 /* We display the search prompt below. If the user types a partial
773 * search string and then Replace or a toggle, we will return to
774 * do_search() or do_replace() and be called again. In that case,
775 * we should put the same search string back up. */
777 search_init_globals();
779 if (last_search
[0] != '\0') {
780 char *disp
= display_string(last_search
, 0, COLS
/ 3, FALSE
);
782 buf
= charalloc(strlen(disp
) + 7);
783 /* We use (COLS / 3) here because we need to see more on the
785 sprintf(buf
, " [%s%s]", disp
,
786 (strlenpt(last_search
) > COLS
/ 3) ? "..." : "");
789 buf
= mallocstrcpy(NULL
, "");
791 /* This is now one simple call. It just does a lot. */
793 #ifndef DISABLE_TABCOMP
796 MWHEREISFILE
, backupstring
,
797 &meta_key
, &func_key
,
801 browser_refresh
, "%s%s%s%s%s%s", _("Search"),
803 /* This string is just a modifier for the search prompt; no
804 * grammar is implied. */
805 ISSET(CASE_SENSITIVE
) ? _(" [Case Sensitive]") :
809 /* This string is just a modifier for the search prompt; no
810 * grammar is implied. */
811 ISSET(USE_REGEXP
) ? _(" [Regexp]") :
815 /* This string is just a modifier for the search prompt; no
816 * grammar is implied. */
817 ISSET(BACKWARDS_SEARCH
) ? _(" [Backwards]") :
821 /* Release buf now that we don't need it anymore. */
827 /* Cancel any search, or just return with no previous search. */
828 if (i
== -1 || (i
< 0 && *last_search
== '\0') || (i
== 0 &&
830 statusbar(_("Cancelled"));
833 s
= get_shortcut(MBROWSER
, &i
, &meta_key
, &func_key
);
834 if (i
== -2 || i
== 0) {
836 /* Use last_search if answer is an empty string, or
837 * answer if it isn't. */
838 if (ISSET(USE_REGEXP
) && !regexp_init((i
== -2) ?
839 last_search
: answer
))
844 if (s
&& s
->scfunc
== CASE_SENS_MSG
) {
845 TOGGLE(CASE_SENSITIVE
);
846 backupstring
= mallocstrcpy(backupstring
, answer
);
848 } else if (s
&& s
->scfunc
== BACKWARDS_MSG
) {
849 TOGGLE(BACKWARDS_SEARCH
);
850 backupstring
= mallocstrcpy(backupstring
, answer
);
855 if (s
&& s
->scfunc
== REGEXP_MSG
) {
857 backupstring
= mallocstrcpy(backupstring
, answer
);
867 /* Look for needle. If no_sameline is TRUE, skip over selected when
868 * looking for needle. begin is the location of the filename where we
869 * first started searching. The return value specifies whether we found
871 bool findnextfile(bool no_sameline
, size_t begin
, const char *needle
)
873 size_t currselected
= selected
;
874 /* The location in the current file list of the match we
876 const char *filetail
= tail(filelist
[currselected
]);
877 /* The filename we display, minus the path. */
878 const char *rev_start
= filetail
, *found
= NULL
;
881 if (ISSET(BACKWARDS_SEARCH
))
882 rev_start
+= strlen(rev_start
);
885 /* Look for needle in the current filename we're searching. */
887 found
= strstrwrapper(filetail
, needle
, rev_start
);
889 /* We've found a potential match. If we're not allowed to find
890 * a match on the same filename we started on and this potential
891 * match is on that line, continue searching. */
892 if (found
!= NULL
&& (!no_sameline
|| currselected
!= begin
))
895 /* We've finished processing the filenames, so get out. */
896 if (search_last_file
) {
897 not_found_msg(needle
);
901 /* Move to the previous or next filename in the list. If we've
902 * reached the start or end of the list, wrap around. */
904 if (ISSET(BACKWARDS_SEARCH
)) {
905 if (currselected
> 0)
908 currselected
= filelist_len
- 1;
909 statusbar(_("Search Wrapped"));
913 if (currselected
< filelist_len
- 1)
917 statusbar(_("Search Wrapped"));
923 /* We've reached the original starting file. */
924 if (currselected
== begin
)
925 search_last_file
= TRUE
;
927 filetail
= tail(filelist
[currselected
]);
929 rev_start
= filetail
;
931 if (ISSET(BACKWARDS_SEARCH
))
932 rev_start
+= strlen(rev_start
);
936 /* We've definitely found something. */
937 selected
= currselected
;
942 /* Clear the flag indicating that a search reached the last file in the
943 * list. We need to do this just before a new search. */
944 void findnextfile_wrap_reset(void)
946 search_last_file
= FALSE
;
949 /* Abort the current filename search. Clean up by setting the current
950 * shortcut list to the browser shortcut list, displaying it, and
951 * decompiling the compiled regular expression we used in the last
953 void filesearch_abort(void)
956 bottombars(MBROWSER
);
962 /* Search for a filename. */
963 void do_filesearch(void)
965 size_t begin
= selected
;
969 i
= filesearch_init();
970 if (i
== -1) /* Cancel, blank search string, or regcomp()
973 #if !defined(NANO_TINY) || defined(HAVE_REGEX_H)
974 else if (i
== 1) /* Case Sensitive, Backwards, or Regexp search
982 /* If answer is now "", copy last_search into answer. */
984 answer
= mallocstrcpy(answer
, last_search
);
986 last_search
= mallocstrcpy(last_search
, answer
);
989 /* If answer is not "", add this search string to the search history
991 if (answer
[0] != '\0')
992 update_history(&search_history
, answer
);
995 findnextfile_wrap_reset();
996 didfind
= findnextfile(FALSE
, begin
, answer
);
998 /* Check to see if there's only one occurrence of the string and
999 * we're on it now. */
1000 if (selected
== begin
&& didfind
) {
1001 /* Do the search again, skipping over the current line. We
1002 * should only end up back at the same position if the string
1003 * isn't found again, in which case it's the only occurrence. */
1004 didfind
= findnextfile(TRUE
, begin
, answer
);
1005 if (selected
== begin
&& !didfind
)
1006 statusbar(_("This is the only occurrence"));
1012 /* Search for the last filename without prompting. */
1013 void do_fileresearch(void)
1015 size_t begin
= selected
;
1018 search_init_globals();
1020 if (last_search
[0] != '\0') {
1022 /* Since answer is "", use last_search! */
1023 if (ISSET(USE_REGEXP
) && !regexp_init(last_search
))
1027 findnextfile_wrap_reset();
1028 didfind
= findnextfile(FALSE
, begin
, answer
);
1030 /* Check to see if there's only one occurrence of the string and
1031 * we're on it now. */
1032 if (selected
== begin
&& didfind
) {
1033 /* Do the search again, skipping over the current line. We
1034 * should only end up back at the same position if the
1035 * string isn't found again, in which case it's the only
1037 didfind
= findnextfile(TRUE
, begin
, answer
);
1038 if (selected
== begin
&& !didfind
)
1039 statusbar(_("This is the only occurrence"));
1042 statusbar(_("No current search pattern"));
1047 /* Select the first file in the list. */
1048 void do_first_file(void)
1053 /* Select the last file in the list. */
1054 void do_last_file(void)
1056 selected
= filelist_len
- 1;
1059 /* Strip one directory from the end of path, and return the stripped
1060 * path. The returned string is dynamically allocated, and should be
1062 char *striponedir(const char *path
)
1066 assert(path
!= NULL
);
1068 retval
= mallocstrcpy(NULL
, path
);
1070 tmp
= strrchr(retval
, '/');
1073 null_at(&retval
, tmp
- retval
);
1078 #endif /* !DISABLE_BROWSER */