blob: 5f84060c4ab8b39da2137c1eae02bb4d87aa9232 [file] [log] [blame]
David Lawrence Ramsey43fff012005-11-05 20:01:11 +00001/**************************************************************************
Benno Schulenberg514cd9a2016-08-29 17:10:49 +02002 * browser.c -- This file is part of GNU nano. *
David Lawrence Ramsey43fff012005-11-05 20:01:11 +00003 * *
Benno Schulenberg7a9f4a42014-04-30 20:18:26 +00004 * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, *
Benno Schulenberg2dc9cbe2015-04-08 18:40:40 +00005 * 2010, 2011, 2013, 2014, 2015 Free Software Foundation, Inc. *
Benno Schulenberg406e5242016-08-29 15:14:18 +02006 * Copyright (C) 2015, 2016 Benno Schulenberg *
Benno Schulenberg2dc9cbe2015-04-08 18:40:40 +00007 * *
Benno Schulenberg514cd9a2016-08-29 17:10:49 +02008 * GNU nano is free software: you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published *
10 * by the Free Software Foundation, either version 3 of the License, *
11 * or (at your option) any later version. *
David Lawrence Ramsey43fff012005-11-05 20:01:11 +000012 * *
Benno Schulenberg514cd9a2016-08-29 17:10:49 +020013 * GNU nano is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty *
15 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
16 * See the GNU General Public License for more details. *
David Lawrence Ramsey43fff012005-11-05 20:01:11 +000017 * *
18 * You should have received a copy of the GNU General Public License *
Benno Schulenberg514cd9a2016-08-29 17:10:49 +020019 * along with this program. If not, see http://www.gnu.org/licenses/. *
David Lawrence Ramsey43fff012005-11-05 20:01:11 +000020 * *
21 **************************************************************************/
22
David Lawrence Ramsey034b9942005-12-08 02:47:10 +000023#include "proto.h"
David Lawrence Ramsey43fff012005-11-05 20:01:11 +000024
Benno Schulenberg8cde95e2015-07-17 19:38:22 +000025#include <stdint.h>
David Lawrence Ramsey43fff012005-11-05 20:01:11 +000026#include <stdio.h>
27#include <string.h>
28#include <unistd.h>
29#include <errno.h>
David Lawrence Ramsey43fff012005-11-05 20:01:11 +000030
31#ifndef DISABLE_BROWSER
32
David Lawrence Ramsey68160072006-02-18 21:32:29 +000033static char **filelist = NULL;
David Lawrence Ramsey9f2e3f72006-07-04 21:47:06 +000034 /* The list of files to display in the file browser. */
David Lawrence Ramsey68160072006-02-18 21:32:29 +000035static size_t filelist_len = 0;
36 /* The number of files in the list. */
37static int width = 0;
Benno Schulenberg892762d2017-01-12 18:00:54 +010038 /* The number of files that we can display per screen row. */
David Lawrence Ramsey68160072006-02-18 21:32:29 +000039static int longest = 0;
40 /* The number of columns in the longest filename in the list. */
David Lawrence Ramsey27ad0cd2006-03-23 20:36:29 +000041static size_t selected = 0;
Benno Schulenberg2dc9cbe2015-04-08 18:40:40 +000042 /* The currently selected filename in the list; zero-based. */
David Lawrence Ramsey68160072006-02-18 21:32:29 +000043
David Lawrence Ramsey6e1cd2d2007-01-11 21:47:06 +000044/* Our main file browser function. path is the tilde-expanded path we
David Lawrence Ramsey46c29c42006-07-02 15:54:14 +000045 * start browsing from. */
Rishabh Davebd3f1562016-05-25 11:16:58 +020046char *do_browser(char *path)
David Lawrence Ramsey43fff012005-11-05 20:01:11 +000047{
Rishabh Dave8fa72fb2016-07-13 14:31:25 +053048 char *retval = NULL;
David Lawrence Ramsey68160072006-02-18 21:32:29 +000049 int kbinput;
Benno Schulenbergc6ec4352016-05-10 17:46:26 +020050 char *present_name = NULL;
51 /* The name of the currently selected file, or of the directory we
52 * were in before backing up to "..". */
David Lawrence Ramsey60d22f02006-07-05 02:24:23 +000053 size_t old_selected;
Benno Schulenbergc6ec4352016-05-10 17:46:26 +020054 /* The number of the selected file before the current selected file. */
Benno Schulenberg6418ffa2014-07-02 09:29:05 +000055 functionptrtype func;
56 /* The function of the key the user typed in. */
Benno Schulenberga730b252016-07-02 13:07:34 +020057 DIR *dir;
58 /* The directory whose contents we are showing. */
David Lawrence Ramsey43fff012005-11-05 20:01:11 +000059
Benno Schulenberg568d2a32016-02-13 19:41:12 +000060 /* Don't show a cursor in the file list. */
David Lawrence Ramsey43fff012005-11-05 20:01:11 +000061 curs_set(0);
David Lawrence Ramsey43fff012005-11-05 20:01:11 +000062
Benno Schulenbergc6ec4352016-05-10 17:46:26 +020063 read_directory_contents:
Rishabh Dave8a5b4f62016-05-10 16:40:01 +020064 /* We come here when we refresh or select a new directory. */
David Lawrence Ramsey43fff012005-11-05 20:01:11 +000065
Benno Schulenberg8b28de12016-07-13 15:04:40 +020066 path = free_and_assign(path, get_full_path(path));
David Lawrence Ramsey43fff012005-11-05 20:01:11 +000067
Rishabh Dave8fa72fb2016-07-13 14:31:25 +053068 if (path != NULL)
Benno Schulenberg956fead2016-06-03 12:12:45 +020069 dir = opendir(path);
70
71 if (path == NULL || dir == NULL) {
David Lawrence Ramseyb5400ff2016-11-30 10:20:30 -060072 statusline(ALERT, _("Cannot open directory: %s"), strerror(errno));
Benno Schulenberg956fead2016-06-03 12:12:45 +020073 /* If we don't have a file list yet, there is nothing to show. */
74 if (filelist == NULL) {
75 napms(1200);
76 lastmessage = HUSH;
77 free(path);
78 free(present_name);
79 return NULL;
80 }
81 path = mallocstrcpy(path, present_path);
82 present_name = mallocstrcpy(present_name, filelist[selected]);
83 }
Benno Schulenberg75d64e62015-05-28 13:02:29 +000084
David Lawrence Ramsey43fff012005-11-05 20:01:11 +000085 assert(path != NULL && path[strlen(path) - 1] == '/');
86
Benno Schulenberg956fead2016-06-03 12:12:45 +020087 if (dir != NULL) {
88 /* Get the file list, and set longest and width in the process. */
89 read_the_list(path, dir);
90 closedir(dir);
91 dir = NULL;
92 }
Rishabh Davebd3f1562016-05-25 11:16:58 +020093
Benno Schulenbergc6ec4352016-05-10 17:46:26 +020094 /* If given, reselect the present_name and then discard it. */
95 if (present_name != NULL) {
96 browser_select_dirname(present_name);
David Lawrence Ramseyc0ba4bf2006-07-05 01:10:18 +000097
Benno Schulenbergc6ec4352016-05-10 17:46:26 +020098 free(present_name);
99 present_name = NULL;
David Lawrence Ramsey2f7c1a02006-07-05 02:05:24 +0000100 /* Otherwise, select the first file or directory in the list. */
101 } else
102 selected = 0;
David Lawrence Ramseyc0ba4bf2006-07-05 01:10:18 +0000103
David Lawrence Ramsey60d22f02006-07-05 02:24:23 +0000104 old_selected = (size_t)-1;
105
Benno Schulenberg956fead2016-06-03 12:12:45 +0200106 present_path = mallocstrcpy(present_path, path);
107
David Lawrence Ramsey43fff012005-11-05 20:01:11 +0000108 titlebar(path);
109
Benno Schulenberg64039e92014-06-22 21:21:00 +0000110 while (TRUE) {
Benno Schulenberg568d2a32016-02-13 19:41:12 +0000111 /* Make sure that the cursor is off. */
112 curs_set(0);
Benno Schulenbergc8f530a2016-05-19 20:43:08 +0200113 lastmessage = HUSH;
Benno Schulenberg568d2a32016-02-13 19:41:12 +0000114
Benno Schulenberg9106cc82016-05-08 12:01:33 +0200115 bottombars(MBROWSER);
116
Benno Schulenbergd9d8eb22016-06-29 21:22:38 +0200117 /* Display (or redisplay) the file list if the list itself or
118 * the selected file has changed. */
119 if (old_selected != selected)
David Lawrence Ramseyc0ba4bf2006-07-05 01:10:18 +0000120 browser_refresh();
David Lawrence Ramseya43b1082006-06-30 22:28:37 +0000121
David Lawrence Ramsey60d22f02006-07-05 02:24:23 +0000122 old_selected = selected;
123
Benno Schulenberg7e5324d2014-06-30 18:04:33 +0000124 kbinput = get_kbinput(edit);
Chris Allegrettac0b78722008-03-11 04:52:57 +0000125
David Lawrence Ramsey43fff012005-11-05 20:01:11 +0000126#ifndef DISABLE_MOUSE
Benno Schulenberg1de337d2014-06-04 16:02:51 +0000127 if (kbinput == KEY_MOUSE) {
Benno Schulenberg492e9f62014-06-20 10:48:26 +0000128 int mouse_x, mouse_y;
David Lawrence Ramsey2f94f422006-06-28 22:38:11 +0000129
Benno Schulenbergb7f11362016-05-11 10:00:09 +0200130 /* We can click on the edit window to select a filename. */
Benno Schulenberg492e9f62014-06-20 10:48:26 +0000131 if (get_mouseinput(&mouse_x, &mouse_y, TRUE) == 0 &&
Benno Schulenbergb7f11362016-05-11 10:00:09 +0200132 wmouse_trafo(edit, &mouse_y, &mouse_x, FALSE)) {
Benno Schulenberg492e9f62014-06-20 10:48:26 +0000133 /* longest is the width of each column. There
134 * are two spaces between each column. */
Benno Schulenbergd7d3a872016-05-17 18:22:42 +0200135 selected = selected - selected % (editwinrows * width) +
Benno Schulenbergb7f11362016-05-11 10:00:09 +0200136 (mouse_y * width) + (mouse_x / (longest + 2));
David Lawrence Ramsey2f94f422006-06-28 22:38:11 +0000137
Benno Schulenberg492e9f62014-06-20 10:48:26 +0000138 /* If they clicked beyond the end of a row,
Benno Schulenberg25ce5f62015-04-07 08:23:52 +0000139 * select the last filename in that row. */
Benno Schulenberg492e9f62014-06-20 10:48:26 +0000140 if (mouse_x > width * (longest + 2))
141 selected--;
David Lawrence Ramsey2f94f422006-06-28 22:38:11 +0000142
Benno Schulenbergb7f11362016-05-11 10:00:09 +0200143 /* If we're beyond the list, select the last filename. */
Benno Schulenberg492e9f62014-06-20 10:48:26 +0000144 if (selected > filelist_len - 1)
145 selected = filelist_len - 1;
David Lawrence Ramseybc7dc362006-07-05 02:34:37 +0000146
Benno Schulenbergb7f11362016-05-11 10:00:09 +0200147 /* If we selected the same filename as last time, fake a
148 * press of the Enter key so that the file is read in. */
Benno Schulenberg492e9f62014-06-20 10:48:26 +0000149 if (old_selected == selected)
Benno Schulenberg8bf86822016-12-22 12:13:03 +0100150 unget_kbinput(KEY_ENTER, FALSE);
Benno Schulenberg492e9f62014-06-20 10:48:26 +0000151 }
Benno Schulenbergc03ce952016-05-17 17:49:15 +0200152
153 continue;
Chris Allegrettac0b78722008-03-11 04:52:57 +0000154 }
David Lawrence Ramsey0ec909c2006-05-06 15:07:26 +0000155#endif /* !DISABLE_MOUSE */
Chris Allegrettac0b78722008-03-11 04:52:57 +0000156
Benno Schulenberg6418ffa2014-07-02 09:29:05 +0000157 func = parse_browser_input(&kbinput);
Chris Allegrettac0b78722008-03-11 04:52:57 +0000158
Benno Schulenberg6418ffa2014-07-02 09:29:05 +0000159 if (func == total_refresh) {
Benno Schulenberg492e9f62014-06-20 10:48:26 +0000160 total_redraw();
Benno Schulenbergeedec062016-05-17 12:48:47 +0200161#ifndef NANO_TINY
Benno Schulenbergd9aad922016-06-28 11:11:19 +0200162 /* Simulate a window resize to force a directory reread. */
Benno Schulenbergc6ec4352016-05-10 17:46:26 +0200163 kbinput = KEY_WINCH;
Benno Schulenbergeedec062016-05-17 12:48:47 +0200164#endif
Benno Schulenberg6418ffa2014-07-02 09:29:05 +0000165 } else if (func == do_help_void) {
David Lawrence Ramsey0ec909c2006-05-06 15:07:26 +0000166#ifndef DISABLE_HELP
Benno Schulenberg55543ad2014-03-26 20:48:51 +0000167 do_help_void();
Benno Schulenbergeedec062016-05-17 12:48:47 +0200168#ifndef NANO_TINY
Benno Schulenbergd9aad922016-06-28 11:11:19 +0200169 /* The window dimensions might have changed, so act as if. */
Benno Schulenberg813f9f72016-02-13 16:42:30 +0000170 kbinput = KEY_WINCH;
Benno Schulenbergeedec062016-05-17 12:48:47 +0200171#endif
David Lawrence Ramsey0ec909c2006-05-06 15:07:26 +0000172#else
Benno Schulenberg68476162015-07-30 18:10:16 +0000173 say_there_is_no_help();
David Lawrence Ramsey43fff012005-11-05 20:01:11 +0000174#endif
Benno Schulenberg6418ffa2014-07-02 09:29:05 +0000175 } else if (func == do_search) {
Benno Schulenberg492e9f62014-06-20 10:48:26 +0000176 /* Search for a filename. */
Benno Schulenberg492e9f62014-06-20 10:48:26 +0000177 do_filesearch();
Benno Schulenberg6418ffa2014-07-02 09:29:05 +0000178 } else if (func == do_research) {
Benno Schulenberg492e9f62014-06-20 10:48:26 +0000179 /* Search for another filename. */
180 do_fileresearch();
Benno Schulenberg8b636de2016-07-01 12:41:35 +0200181 } else if (func == do_left) {
182 if (selected > 0)
183 selected--;
184 } else if (func == do_right) {
185 if (selected < filelist_len - 1)
186 selected++;
187#ifndef NANO_TINY
188 } else if (func == do_prev_word_void) {
189 selected -= (selected % width);
190 } else if (func == do_next_word_void) {
191 selected += width - 1 - (selected % width);
192 if (selected >= filelist_len)
193 selected = filelist_len - 1;
194#endif
195 } else if (func == do_up_void) {
196 if (selected >= width)
197 selected -= width;
198 } else if (func == do_down_void) {
199 if (selected + width <= filelist_len - 1)
200 selected += width;
Benno Schulenberg6418ffa2014-07-02 09:29:05 +0000201 } else if (func == do_page_up) {
Benno Schulenberg58404e42016-05-11 13:29:38 +0200202 if (selected < width)
Benno Schulenberg492e9f62014-06-20 10:48:26 +0000203 selected = 0;
Benno Schulenberg58404e42016-05-11 13:29:38 +0200204 else if (selected < editwinrows * width)
205 selected = selected % width;
206 else
207 selected -= editwinrows * width;
Benno Schulenberg6418ffa2014-07-02 09:29:05 +0000208 } else if (func == do_page_down) {
Benno Schulenberg58404e42016-05-11 13:29:38 +0200209 if (selected + width >= filelist_len - 1)
Benno Schulenberg492e9f62014-06-20 10:48:26 +0000210 selected = filelist_len - 1;
Benno Schulenberg58404e42016-05-11 13:29:38 +0200211 else if (selected + editwinrows * width >= filelist_len)
212 selected = (selected + editwinrows * width - filelist_len) %
213 width + filelist_len - width;
214 else
215 selected += editwinrows * width;
Benno Schulenberg6418ffa2014-07-02 09:29:05 +0000216 } else if (func == do_first_file) {
Benno Schulenberg0c0fbd52014-06-25 09:05:55 +0000217 selected = 0;
Benno Schulenberg6418ffa2014-07-02 09:29:05 +0000218 } else if (func == do_last_file) {
Benno Schulenberg0c0fbd52014-06-25 09:05:55 +0000219 selected = filelist_len - 1;
Benno Schulenberg6418ffa2014-07-02 09:29:05 +0000220 } else if (func == goto_dir_void) {
Benno Schulenbergb7f11362016-05-11 10:00:09 +0200221 /* Ask for the directory to go to. */
Benno Schulenbergfd0589d2017-01-02 21:12:44 +0100222 int i = do_prompt(TRUE, FALSE, MGOTODIR, NULL,
Benno Schulenbergb341f292014-06-19 20:05:24 +0000223#ifndef DISABLE_HISTORIES
David Lawrence Ramsey43fff012005-11-05 20:01:11 +0000224 NULL,
225#endif
Benno Schulenberga2d74f72014-05-28 14:34:11 +0000226 /* TRANSLATORS: This is a prompt. */
Benno Schulenbergec173222014-05-28 13:27:33 +0000227 browser_refresh, _("Go To Directory"));
Benno Schulenbergf8c33e82016-07-01 12:22:44 +0200228
Benno Schulenberge6350aa2016-12-20 21:41:37 +0100229 if (i < 0) {
Benno Schulenberg492e9f62014-06-20 10:48:26 +0000230 statusbar(_("Cancelled"));
Benno Schulenberg492e9f62014-06-20 10:48:26 +0000231 continue;
Benno Schulenberg492e9f62014-06-20 10:48:26 +0000232 }
David Lawrence Ramseyc0dcdf42006-02-08 19:27:02 +0000233
Benno Schulenberg8b28de12016-07-13 15:04:40 +0200234 path = free_and_assign(path, real_dir_from_tilde(answer));
Benno Schulenberg492e9f62014-06-20 10:48:26 +0000235
Rishabh Dave8fa72fb2016-07-13 14:31:25 +0530236 /* If the given path is relative, join it with the current path. */
237 if (*path != '/') {
238 path = charealloc(path, strlen(present_path) +
239 strlen(answer) + 1);
240 sprintf(path, "%s%s", present_path, answer);
Benno Schulenberg492e9f62014-06-20 10:48:26 +0000241 }
David Lawrence Ramsey43fff012005-11-05 20:01:11 +0000242
243#ifndef DISABLE_OPERATINGDIR
Rishabh Dave8fa72fb2016-07-13 14:31:25 +0530244 if (check_operating_dir(path, FALSE)) {
Benno Schulenbergc0c30732016-06-24 09:19:23 +0200245 /* TRANSLATORS: This refers to the confining effect of the
246 * option --operatingdir, not of --restricted. */
247 statusline(ALERT, _("Can't go outside of %s"),
248 full_operating_dir);
Rishabh Dave8fa72fb2016-07-13 14:31:25 +0530249 path = mallocstrcpy(path, present_path);
Benno Schulenberg492e9f62014-06-20 10:48:26 +0000250 continue;
251 }
David Lawrence Ramsey43fff012005-11-05 20:01:11 +0000252#endif
Benno Schulenberg03a81aa2016-07-03 16:18:26 +0200253 /* Snip any trailing slashes, so the name can be compared. */
Rishabh Dave8fa72fb2016-07-13 14:31:25 +0530254 while (strlen(path) > 1 && path[strlen(path) - 1] == '/')
255 path[strlen(path) - 1] = '\0';
Rishabh Dave4957c112016-07-01 19:24:28 +0530256
Rishabh Dave559858a2016-06-21 19:11:40 +0200257 /* In case the specified directory cannot be entered, select it
258 * (if it is in the current list) so it will be highlighted. */
259 for (i = 0; i < filelist_len; i++)
Rishabh Dave8fa72fb2016-07-13 14:31:25 +0530260 if (strcmp(filelist[i], path) == 0)
Rishabh Dave559858a2016-06-21 19:11:40 +0200261 selected = i;
David Lawrence Ramsey43fff012005-11-05 20:01:11 +0000262
Rishabh Dave559858a2016-06-21 19:11:40 +0200263 /* Try opening and reading the specified directory. */
Benno Schulenbergc6ec4352016-05-10 17:46:26 +0200264 goto read_directory_contents;
Benno Schulenbergbde996d2015-11-11 19:56:35 +0000265 } else if (func == do_enter) {
Benno Schulenbergf8c33e82016-07-01 12:22:44 +0200266 struct stat st;
267
Benno Schulenbergaeab8002016-07-01 12:34:33 +0200268 /* It isn't possible to move up from the root directory. */
Benno Schulenberg492e9f62014-06-20 10:48:26 +0000269 if (strcmp(filelist[selected], "/..") == 0) {
Benno Schulenberg2535f512016-04-30 17:31:43 +0200270 statusline(ALERT, _("Can't move up a directory"));
Benno Schulenberg492e9f62014-06-20 10:48:26 +0000271 continue;
272 }
David Lawrence Ramsey0ec909c2006-05-06 15:07:26 +0000273
274#ifndef DISABLE_OPERATINGDIR
Benno Schulenberg492e9f62014-06-20 10:48:26 +0000275 /* Note: The selected file can be outside the operating
276 * directory if it's ".." or if it's a symlink to a
277 * directory outside the operating directory. */
278 if (check_operating_dir(filelist[selected], FALSE)) {
Benno Schulenbergc0c30732016-06-24 09:19:23 +0200279 statusline(ALERT, _("Can't go outside of %s"),
280 full_operating_dir);
Benno Schulenberg492e9f62014-06-20 10:48:26 +0000281 continue;
282 }
David Lawrence Ramsey0ec909c2006-05-06 15:07:26 +0000283#endif
Benno Schulenbergaeab8002016-07-01 12:34:33 +0200284 /* If for some reason the file is inaccessible, complain. */
Benno Schulenberg492e9f62014-06-20 10:48:26 +0000285 if (stat(filelist[selected], &st) == -1) {
Benno Schulenberg17cf8332016-05-30 09:09:36 +0200286 statusline(ALERT, _("Error reading %s: %s"),
Benno Schulenberg492e9f62014-06-20 10:48:26 +0000287 filelist[selected], strerror(errno));
Benno Schulenberg17cf8332016-05-30 09:09:36 +0200288 continue;
Benno Schulenberg492e9f62014-06-20 10:48:26 +0000289 }
David Lawrence Ramsey0ec909c2006-05-06 15:07:26 +0000290
Benno Schulenbergaeab8002016-07-01 12:34:33 +0200291 /* If it isn't a directory, a file was selected -- we're done. */
Benno Schulenberg492e9f62014-06-20 10:48:26 +0000292 if (!S_ISDIR(st.st_mode)) {
293 retval = mallocstrcpy(NULL, filelist[selected]);
Benno Schulenberg64039e92014-06-22 21:21:00 +0000294 break;
Benno Schulenberg3b8989b2016-02-25 14:08:47 +0000295 }
David Lawrence Ramsey0ec909c2006-05-06 15:07:26 +0000296
Benno Schulenberg956fead2016-06-03 12:12:45 +0200297 /* If we are moving up one level, remember where we came from, so
Benno Schulenberg3b8989b2016-02-25 14:08:47 +0000298 * this directory can be highlighted and easily reentered. */
299 if (strcmp(tail(filelist[selected]), "..") == 0)
Benno Schulenbergc6ec4352016-05-10 17:46:26 +0200300 present_name = striponedir(filelist[selected]);
Benno Schulenberg3b8989b2016-02-25 14:08:47 +0000301
Benno Schulenberg956fead2016-06-03 12:12:45 +0200302 /* Try opening and reading the selected directory. */
Rishabh Dave8fa72fb2016-07-13 14:31:25 +0530303 path = mallocstrcpy(path, filelist[selected]);
Benno Schulenbergc6ec4352016-05-10 17:46:26 +0200304 goto read_directory_contents;
Benno Schulenberg6418ffa2014-07-02 09:29:05 +0000305 } else if (func == do_exit) {
Benno Schulenberg64039e92014-06-22 21:21:00 +0000306 /* Exit from the file browser. */
307 break;
Benno Schulenbergd9d8eb22016-06-29 21:22:38 +0200308#ifndef NANO_TINY
309 } else if (kbinput == KEY_WINCH) {
310 ;
311#endif
Benno Schulenberge0c4f9c2016-04-27 14:37:31 +0200312 } else
313 unbound_key(kbinput);
Benno Schulenbergd9d8eb22016-06-29 21:22:38 +0200314
315#ifndef NANO_TINY
316 /* If the window resized, refresh the file list. */
317 if (kbinput == KEY_WINCH) {
318 /* Remember the selected file, to be able to reselect it. */
Benno Schulenberg62eeda32016-07-11 20:39:53 +0200319 present_name = mallocstrcpy(NULL, filelist[selected]);
Benno Schulenbergd9d8eb22016-06-29 21:22:38 +0200320 /* Reread the contents of the current directory. */
Benno Schulenbergd9d8eb22016-06-29 21:22:38 +0200321 goto read_directory_contents;
322 }
323#endif
David Lawrence Ramseya43b1082006-06-30 22:28:37 +0000324 }
Benno Schulenberg2cd21da2016-05-15 11:17:55 +0200325
David Lawrence Ramsey43fff012005-11-05 20:01:11 +0000326 titlebar(NULL);
327 edit_refresh();
David Lawrence Ramsey43fff012005-11-05 20:01:11 +0000328
David Lawrence Ramseyb08c9f02006-07-11 18:12:24 +0000329 free(path);
David Lawrence Ramseyb08c9f02006-07-11 18:12:24 +0000330
331 free_chararray(filelist, filelist_len);
332 filelist = NULL;
333 filelist_len = 0;
David Lawrence Ramsey43fff012005-11-05 20:01:11 +0000334
335 return retval;
336}
337
David Lawrence Ramsey31efe992006-05-10 16:03:59 +0000338/* The file browser front end. We check to see if inpath has a
339 * directory in it. If it does, we start do_browser() from there.
340 * Otherwise, we start do_browser() from the current directory. */
David Lawrence Ramsey43fff012005-11-05 20:01:11 +0000341char *do_browse_from(const char *inpath)
342{
343 struct stat st;
344 char *path;
345 /* This holds the tilde-expanded version of inpath. */
David Lawrence Ramsey43fff012005-11-05 20:01:11 +0000346
David Lawrence Ramsey43fff012005-11-05 20:01:11 +0000347 path = real_dir_from_tilde(inpath);
348
349 /* Perhaps path is a directory. If so, we'll pass it to
350 * do_browser(). Or perhaps path is a directory / a file. If so,
351 * we'll try stripping off the last path element and passing it to
352 * do_browser(). Or perhaps path doesn't have a directory portion
353 * at all. If so, we'll just pass the current directory to
354 * do_browser(). */
355 if (stat(path, &st) == -1 || !S_ISDIR(st.st_mode)) {
Benno Schulenberg8b28de12016-07-13 15:04:40 +0200356 path = free_and_assign(path, striponedir(path));
David Lawrence Ramsey2458b292006-06-30 14:21:29 +0000357
David Lawrence Ramsey43fff012005-11-05 20:01:11 +0000358 if (stat(path, &st) == -1 || !S_ISDIR(st.st_mode)) {
Benno Schulenberg2e75c222016-05-21 21:29:54 +0200359 char * currentdir = charalloc(PATH_MAX + 1);
David Lawrence Ramsey43fff012005-11-05 20:01:11 +0000360
Benno Schulenberg2e75c222016-05-21 21:29:54 +0200361 free(path);
362 path = getcwd(currentdir, PATH_MAX + 1);
David Lawrence Ramsey43fff012005-11-05 20:01:11 +0000363
Benno Schulenberg9576eb62016-05-21 21:23:49 +0200364 if (path == NULL) {
Benno Schulenberg2e75c222016-05-21 21:29:54 +0200365 free(currentdir);
David Lawrence Ramseyb5400ff2016-11-30 10:20:30 -0600366 statusline(MILD, _("The working directory has disappeared"));
Benno Schulenberg9576eb62016-05-21 21:23:49 +0200367 beep();
368 napms(1200);
369 return NULL;
Benno Schulenbergd7af5902016-12-16 21:28:28 +0100370 }
David Lawrence Ramsey43fff012005-11-05 20:01:11 +0000371 }
372 }
373
374#ifndef DISABLE_OPERATINGDIR
375 /* If the resulting path isn't in the operating directory, use
376 * the operating directory instead. */
David Lawrence Ramseya7bdd2e2006-07-05 03:38:49 +0000377 if (check_operating_dir(path, FALSE))
378 path = mallocstrcpy(path, operating_dir);
David Lawrence Ramsey43fff012005-11-05 20:01:11 +0000379#endif
380
Rishabh Davebd3f1562016-05-25 11:16:58 +0200381 return do_browser(path);
David Lawrence Ramsey43fff012005-11-05 20:01:11 +0000382}
383
David Lawrence Ramsey68160072006-02-18 21:32:29 +0000384/* Set filelist to the list of files contained in the directory path,
David Lawrence Ramsey2f7c1a02006-07-05 02:05:24 +0000385 * set filelist_len to the number of files in that list, set longest to
David Lawrence Ramsey4953e492006-07-05 03:24:39 +0000386 * the width in columns of the longest filename in that list (between 15
David Lawrence Ramseyf1a8e722006-07-05 05:14:11 +0000387 * and COLS), and set width to the number of files that we can display
Benno Schulenberg892762d2017-01-12 18:00:54 +0100388 * per screen row. And sort the list too. */
Benno Schulenbergd3bd8552016-05-25 12:09:22 +0200389void read_the_list(const char *path, DIR *dir)
David Lawrence Ramsey68160072006-02-18 21:32:29 +0000390{
391 const struct dirent *nextdir;
David Lawrence Ramseyf1a8e722006-07-05 05:14:11 +0000392 size_t i = 0, path_len = strlen(path);
David Lawrence Ramsey68160072006-02-18 21:32:29 +0000393
David Lawrence Ramsey2f7c1a02006-07-05 02:05:24 +0000394 assert(path != NULL && path[strlen(path) - 1] == '/' && dir != NULL);
David Lawrence Ramsey68160072006-02-18 21:32:29 +0000395
396 longest = 0;
397
Benno Schulenberg8a058d12015-08-13 18:22:29 +0000398 /* Find the length of the longest filename in the current folder. */
David Lawrence Ramsey68160072006-02-18 21:32:29 +0000399 while ((nextdir = readdir(dir)) != NULL) {
Benno Schulenberg8a058d12015-08-13 18:22:29 +0000400 size_t name_len = strlenpt(nextdir->d_name);
David Lawrence Ramsey68160072006-02-18 21:32:29 +0000401
Benno Schulenberg8a058d12015-08-13 18:22:29 +0000402 if (name_len > longest)
403 longest = name_len;
David Lawrence Ramsey2f7c1a02006-07-05 02:05:24 +0000404
405 i++;
David Lawrence Ramsey68160072006-02-18 21:32:29 +0000406 }
407
Benno Schulenberg8a058d12015-08-13 18:22:29 +0000408 /* Put 10 characters' worth of blank space between columns of filenames
David Lawrence Ramsey224b6d52006-07-02 20:56:34 +0000409 * in the list whenever possible, as Pico does. */
David Lawrence Ramsey5ee89c62006-07-02 20:41:21 +0000410 longest += 10;
David Lawrence Ramsey68160072006-02-18 21:32:29 +0000411
Benno Schulenbergd3bd8552016-05-25 12:09:22 +0200412 /* If needed, make room for ".. (parent dir)". */
Benno Schulenberg8a058d12015-08-13 18:22:29 +0000413 if (longest < 15)
414 longest = 15;
Benno Schulenbergd3bd8552016-05-25 12:09:22 +0200415 /* Make sure we're not wider than the window. */
Benno Schulenberg8a058d12015-08-13 18:22:29 +0000416 if (longest > COLS)
417 longest = COLS;
418
419 rewinddir(dir);
420
Benno Schulenberg42ac5362015-11-30 16:44:44 +0000421 free_chararray(filelist, filelist_len);
David Lawrence Ramsey87b37bd2006-07-11 17:25:12 +0000422
423 filelist_len = i;
424
David Lawrence Ramsey68160072006-02-18 21:32:29 +0000425 filelist = (char **)nmalloc(filelist_len * sizeof(char *));
426
David Lawrence Ramsey68160072006-02-18 21:32:29 +0000427 i = 0;
428
429 while ((nextdir = readdir(dir)) != NULL && i < filelist_len) {
430 /* Don't show the "." entry. */
431 if (strcmp(nextdir->d_name, ".") == 0)
Benno Schulenberga8a23ab2014-06-10 19:12:14 +0000432 continue;
David Lawrence Ramsey68160072006-02-18 21:32:29 +0000433
434 filelist[i] = charalloc(path_len + strlen(nextdir->d_name) + 1);
435 sprintf(filelist[i], "%s%s", path, nextdir->d_name);
David Lawrence Ramsey2f7c1a02006-07-05 02:05:24 +0000436
David Lawrence Ramsey68160072006-02-18 21:32:29 +0000437 i++;
438 }
439
440 /* Maybe the number of files in the directory changed between the
441 * first time we scanned and the second. i is the actual length of
442 * filelist, so record it. */
443 filelist_len = i;
David Lawrence Ramsey2f7c1a02006-07-05 02:05:24 +0000444
Benno Schulenbergd3bd8552016-05-25 12:09:22 +0200445 assert(filelist != NULL);
446
447 /* Sort the list of names. */
448 qsort(filelist, filelist_len, sizeof(char *), diralphasort);
449
Benno Schulenberg8ddc5e72016-05-10 21:04:33 +0200450 /* Calculate how many files fit on a line -- feigning room for two
451 * spaces beyond the right edge, and adding two spaces of padding
452 * between columns. */
453 width = (COLS + 2) / (longest + 2);
David Lawrence Ramsey68160072006-02-18 21:32:29 +0000454}
455
Benno Schulenberg6418ffa2014-07-02 09:29:05 +0000456/* Return the function that is bound to the given key, accepting certain
457 * plain characters too, for compatibility with Pico. */
458functionptrtype parse_browser_input(int *kbinput)
David Lawrence Ramsey91a2a692006-02-07 04:41:44 +0000459{
Benno Schulenberg7e5324d2014-06-30 18:04:33 +0000460 if (!meta_key) {
David Lawrence Ramsey91a2a692006-02-07 04:41:44 +0000461 switch (*kbinput) {
462 case ' ':
Benno Schulenberg6418ffa2014-07-02 09:29:05 +0000463 return do_page_down;
David Lawrence Ramsey91a2a692006-02-07 04:41:44 +0000464 case '-':
Benno Schulenberg6418ffa2014-07-02 09:29:05 +0000465 return do_page_up;
David Lawrence Ramsey91a2a692006-02-07 04:41:44 +0000466 case '?':
Benno Schulenberg6418ffa2014-07-02 09:29:05 +0000467 return do_help_void;
David Lawrence Ramsey91a2a692006-02-07 04:41:44 +0000468 case 'E':
469 case 'e':
Benno Schulenberg6418ffa2014-07-02 09:29:05 +0000470 return do_exit;
David Lawrence Ramsey91a2a692006-02-07 04:41:44 +0000471 case 'G':
472 case 'g':
Benno Schulenberg6418ffa2014-07-02 09:29:05 +0000473 return goto_dir_void;
David Lawrence Ramsey91a2a692006-02-07 04:41:44 +0000474 case 'S':
475 case 's':
Benno Schulenbergbde996d2015-11-11 19:56:35 +0000476 return do_enter;
David Lawrence Ramseye38b8082006-03-30 07:03:04 +0000477 case 'W':
478 case 'w':
Benno Schulenberg6418ffa2014-07-02 09:29:05 +0000479 return do_search;
David Lawrence Ramsey91a2a692006-02-07 04:41:44 +0000480 }
481 }
Benno Schulenberg6418ffa2014-07-02 09:29:05 +0000482 return func_from_key(kbinput);
David Lawrence Ramsey91a2a692006-02-07 04:41:44 +0000483}
484
Benno Schulenberg892762d2017-01-12 18:00:54 +0100485/* Set width to the number of files that we can display per screen row,
486 * if necessary, and display the list of files. */
David Lawrence Ramsey68160072006-02-18 21:32:29 +0000487void browser_refresh(void)
David Lawrence Ramseyd4bae132006-02-09 23:26:26 +0000488{
David Lawrence Ramseyb0c1bc22006-06-29 15:46:05 +0000489 size_t i;
Benno Schulenberg892762d2017-01-12 18:00:54 +0100490 int row = 0, col = 0;
491 /* The current row and column while the list is getting displayed. */
492 int the_row = 0, the_column = 0;
493 /* The row and column of the selected item. */
Benno Schulenbergb55e8d72016-03-21 20:49:29 +0000494 char *info;
Benno Schulenberga3387982015-05-03 13:56:51 +0000495 /* The additional information that we'll display about a file. */
David Lawrence Ramsey99c87cc2006-06-30 02:44:11 +0000496
Benno Schulenberge2556272016-04-30 21:22:16 +0200497 titlebar(present_path);
David Lawrence Ramseyd4bae132006-02-09 23:26:26 +0000498 blank_edit();
499
500 wmove(edit, 0, 0);
501
Benno Schulenbergd7d3a872016-05-17 18:22:42 +0200502 i = selected - selected % (editwinrows * width);
David Lawrence Ramseyb0c1bc22006-06-29 15:46:05 +0000503
Benno Schulenberg892762d2017-01-12 18:00:54 +0100504 for (; i < filelist_len && row < editwinrows; i++) {
David Lawrence Ramsey99c87cc2006-06-30 02:44:11 +0000505 struct stat st;
Benno Schulenbergb55e8d72016-03-21 20:49:29 +0000506 const char *thename = tail(filelist[i]);
David Lawrence Ramseya0283b72006-07-04 03:49:15 +0000507 /* The filename we display, minus the path. */
Benno Schulenbergb55e8d72016-03-21 20:49:29 +0000508 size_t namelen = strlenpt(thename);
David Lawrence Ramseya0283b72006-07-04 03:49:15 +0000509 /* The length of the filename in columns. */
Benno Schulenbergb55e8d72016-03-21 20:49:29 +0000510 size_t infolen;
David Lawrence Ramsey458d0042006-07-04 21:46:34 +0000511 /* The length of the file information in columns. */
Benno Schulenbergb55e8d72016-03-21 20:49:29 +0000512 int infomaxlen = 7;
Benno Schulenberg09e95b22016-09-11 11:26:09 +0200513 /* The maximum length of the file information in columns:
514 * normally seven, but will be twelve for "(parent dir)". */
Benno Schulenbergb55e8d72016-03-21 20:49:29 +0000515 bool dots = (COLS >= 15 && namelen >= longest - infomaxlen);
Benno Schulenberg09e95b22016-09-11 11:26:09 +0200516 /* Whether to put an ellipsis before the filename? We don't
517 * waste space on dots when there are fewer than 15 columns. */
518 char *disp = display_string(thename, dots ?
519 namelen + infomaxlen + 4 - longest : 0, longest, FALSE);
520 /* The filename (or a fragment of it) in displayable format.
521 * When a fragment, account for dots plus one space padding. */
David Lawrence Ramseyd4bae132006-02-09 23:26:26 +0000522
Benno Schulenbergb92d35d2016-09-11 09:41:09 +0200523 /* If this is the selected item, start its highlighting, and
524 * remember its location to be able to place the cursor on it. */
525 if (i == selected) {
Benno Schulenbergc9700352014-05-04 08:53:06 +0000526 wattron(edit, hilite_attribute);
Benno Schulenberg892762d2017-01-12 18:00:54 +0100527 the_row = row;
Benno Schulenbergb92d35d2016-09-11 09:41:09 +0200528 the_column = col;
529 }
David Lawrence Ramseyd4bae132006-02-09 23:26:26 +0000530
Benno Schulenberg892762d2017-01-12 18:00:54 +0100531 blank_row(edit, row, col, longest);
David Lawrence Ramsey85ffaee2006-07-02 18:29:49 +0000532
Benno Schulenberg09e95b22016-09-11 11:26:09 +0200533 /* If the name is too long, we display something like "...ename". */
David Lawrence Ramsey85ffaee2006-07-02 18:29:49 +0000534 if (dots)
Benno Schulenberg892762d2017-01-12 18:00:54 +0100535 mvwaddstr(edit, row, col, "...");
536 mvwaddstr(edit, row, dots ? col + 3 : col, disp);
David Lawrence Ramsey85ffaee2006-07-02 18:29:49 +0000537
David Lawrence Ramseyd4bae132006-02-09 23:26:26 +0000538 free(disp);
539
540 col += longest;
David Lawrence Ramseyd4bae132006-02-09 23:26:26 +0000541
Benno Schulenberg09e95b22016-09-11 11:26:09 +0200542 /* Show information about the file: "--" for symlinks (except when
543 * they point to a directory) and for files that have disappeared,
544 * "(dir)" for directories, and the file size for normal files. */
David Lawrence Ramseyd4bae132006-02-09 23:26:26 +0000545 if (lstat(filelist[i], &st) == -1 || S_ISLNK(st.st_mode)) {
David Lawrence Ramsey99c87cc2006-06-30 02:44:11 +0000546 if (stat(filelist[i], &st) == -1 || !S_ISDIR(st.st_mode))
Benno Schulenbergb55e8d72016-03-21 20:49:29 +0000547 info = mallocstrcpy(NULL, "--");
David Lawrence Ramsey99c87cc2006-06-30 02:44:11 +0000548 else
Benno Schulenberga3387982015-05-03 13:56:51 +0000549 /* TRANSLATORS: Try to keep this at most 7 characters. */
Benno Schulenbergb55e8d72016-03-21 20:49:29 +0000550 info = mallocstrcpy(NULL, _("(dir)"));
David Lawrence Ramsey458d0042006-07-04 21:46:34 +0000551 } else if (S_ISDIR(st.st_mode)) {
Benno Schulenbergb55e8d72016-03-21 20:49:29 +0000552 if (strcmp(thename, "..") == 0) {
Benno Schulenberga3387982015-05-03 13:56:51 +0000553 /* TRANSLATORS: Try to keep this at most 12 characters. */
Benno Schulenbergb55e8d72016-03-21 20:49:29 +0000554 info = mallocstrcpy(NULL, _("(parent dir)"));
555 infomaxlen = 12;
David Lawrence Ramsey458d0042006-07-04 21:46:34 +0000556 } else
Benno Schulenbergb55e8d72016-03-21 20:49:29 +0000557 info = mallocstrcpy(NULL, _("(dir)"));
David Lawrence Ramsey458d0042006-07-04 21:46:34 +0000558 } else {
Benno Schulenberg8cde95e2015-07-17 19:38:22 +0000559 off_t result = st.st_size;
David Lawrence Ramseyafa15422006-07-04 22:17:39 +0000560 char modifier;
561
Benno Schulenbergb55e8d72016-03-21 20:49:29 +0000562 info = charalloc(infomaxlen + 1);
David Lawrence Ramseyd4bae132006-02-09 23:26:26 +0000563
Benno Schulenberg09e95b22016-09-11 11:26:09 +0200564 /* Massage the file size into a human-readable form. */
David Lawrence Ramseyafa15422006-07-04 22:17:39 +0000565 if (st.st_size < (1 << 10))
Benno Schulenberg55d1e1a2015-05-08 19:35:47 +0000566 modifier = ' '; /* bytes */
David Lawrence Ramseyafa15422006-07-04 22:17:39 +0000567 else if (st.st_size < (1 << 20)) {
568 result >>= 10;
Benno Schulenberg55d1e1a2015-05-08 19:35:47 +0000569 modifier = 'K'; /* kilobytes */
David Lawrence Ramseyafa15422006-07-04 22:17:39 +0000570 } else if (st.st_size < (1 << 30)) {
571 result >>= 20;
Benno Schulenberg55d1e1a2015-05-08 19:35:47 +0000572 modifier = 'M'; /* megabytes */
David Lawrence Ramseyafa15422006-07-04 22:17:39 +0000573 } else {
574 result >>= 30;
Benno Schulenberg55d1e1a2015-05-08 19:35:47 +0000575 modifier = 'G'; /* gigabytes */
David Lawrence Ramseyafa15422006-07-04 22:17:39 +0000576 }
577
Benno Schulenberg09e95b22016-09-11 11:26:09 +0200578 /* Show the size if less than a terabyte, else show "(huge)". */
Benno Schulenberg8cde95e2015-07-17 19:38:22 +0000579 if (result < (1 << 10))
Benno Schulenbergb55e8d72016-03-21 20:49:29 +0000580 sprintf(info, "%4ju %cB", (intmax_t)result, modifier);
Benno Schulenberg55d1e1a2015-05-08 19:35:47 +0000581 else
582 /* TRANSLATORS: Try to keep this at most 7 characters.
583 * If necessary, you can leave out the parentheses. */
Benno Schulenbergb55e8d72016-03-21 20:49:29 +0000584 info = mallocstrcpy(info, _("(huge)"));
David Lawrence Ramsey99c87cc2006-06-30 02:44:11 +0000585 }
586
Benno Schulenbergb55e8d72016-03-21 20:49:29 +0000587 /* Make sure info takes up no more than infomaxlen columns. */
588 infolen = strlenpt(info);
589 if (infolen > infomaxlen) {
590 null_at(&info, actual_x(info, infomaxlen));
591 infolen = infomaxlen;
David Lawrence Ramsey458d0042006-07-04 21:46:34 +0000592 }
593
Benno Schulenberg892762d2017-01-12 18:00:54 +0100594 mvwaddstr(edit, row, col - infolen, info);
David Lawrence Ramseyd4bae132006-02-09 23:26:26 +0000595
Benno Schulenbergb92d35d2016-09-11 09:41:09 +0200596 /* If this is the selected item, finish its highlighting. */
David Lawrence Ramseyd4bae132006-02-09 23:26:26 +0000597 if (i == selected)
Benno Schulenbergc9700352014-05-04 08:53:06 +0000598 wattroff(edit, hilite_attribute);
David Lawrence Ramseyd4bae132006-02-09 23:26:26 +0000599
Benno Schulenbergb55e8d72016-03-21 20:49:29 +0000600 free(info);
David Lawrence Ramsey99c87cc2006-06-30 02:44:11 +0000601
David Lawrence Ramseyd4bae132006-02-09 23:26:26 +0000602 /* Add some space between the columns. */
603 col += 2;
604
Benno Schulenberg892762d2017-01-12 18:00:54 +0100605 /* If the next entry isn't going to fit on the current row,
606 * move to the next row. */
David Lawrence Ramseyd4bae132006-02-09 23:26:26 +0000607 if (col > COLS - longest) {
Benno Schulenberg892762d2017-01-12 18:00:54 +0100608 row++;
David Lawrence Ramseyd4bae132006-02-09 23:26:26 +0000609 col = 0;
David Lawrence Ramseyd4bae132006-02-09 23:26:26 +0000610 }
David Lawrence Ramseyd4bae132006-02-09 23:26:26 +0000611 }
612
Benno Schulenbergb92d35d2016-09-11 09:41:09 +0200613 /* If requested, put the cursor on the selected item and switch it on. */
614 if (ISSET(SHOW_CURSOR)) {
Benno Schulenberg892762d2017-01-12 18:00:54 +0100615 wmove(edit, the_row, the_column);
Benno Schulenbergb92d35d2016-09-11 09:41:09 +0200616 curs_set(1);
617 }
618
David Lawrence Ramseyd4bae132006-02-09 23:26:26 +0000619 wnoutrefresh(edit);
620}
621
Benno Schulenberg2dc9cbe2015-04-08 18:40:40 +0000622/* Look for needle. If we find it, set selected to its location.
623 * Note that needle must be an exact match for a file in the list. */
624void browser_select_dirname(const char *needle)
David Lawrence Ramseyd0403772006-06-30 06:41:31 +0000625{
Benno Schulenberg2dc9cbe2015-04-08 18:40:40 +0000626 size_t looking_at = 0;
David Lawrence Ramseyd0403772006-06-30 06:41:31 +0000627
Benno Schulenberg2dc9cbe2015-04-08 18:40:40 +0000628 for (; looking_at < filelist_len; looking_at++) {
629 if (strcmp(filelist[looking_at], needle) == 0) {
630 selected = looking_at;
David Lawrence Ramseyd0403772006-06-30 06:41:31 +0000631 break;
632 }
633 }
Rishabh Dave8a5b4f62016-05-10 16:40:01 +0200634
635 /* If the sought name isn't found, move the highlight so that the
636 * changed selection will be noticed. */
Benno Schulenbergc6ec4352016-05-10 17:46:26 +0200637 if (looking_at == filelist_len) {
Rishabh Dave8a5b4f62016-05-10 16:40:01 +0200638 --selected;
Benno Schulenbergc6ec4352016-05-10 17:46:26 +0200639
640 /* Make sure we stay within the available range. */
641 if (selected >= filelist_len)
642 selected = filelist_len - 1;
643 }
David Lawrence Ramseyd0403772006-06-30 06:41:31 +0000644}
645
Benno Schulenberg4e5ea182015-04-12 09:04:30 +0000646/* Set up the system variables for a filename search. Return -1 or -2 if
647 * the search should be canceled (due to Cancel or a blank search string),
648 * return 0 when we have a string, and return a positive value when some
649 * function was run. */
David Lawrence Ramseye38b8082006-03-30 07:03:04 +0000650int filesearch_init(void)
651{
Benno Schulenberg99d23582015-04-12 08:31:53 +0000652 int input;
David Lawrence Ramseye38b8082006-03-30 07:03:04 +0000653 char *buf;
David Lawrence Ramseye38b8082006-03-30 07:03:04 +0000654
Benno Schulenberg1d3d3072016-05-27 21:31:55 +0200655 if (*last_search != '\0') {
David Lawrence Ramseye38b8082006-03-30 07:03:04 +0000656 char *disp = display_string(last_search, 0, COLS / 3, FALSE);
657
658 buf = charalloc(strlen(disp) + 7);
Benno Schulenberg25ce5f62015-04-07 08:23:52 +0000659 /* We use (COLS / 3) here because we need to see more on the line. */
David Lawrence Ramseye38b8082006-03-30 07:03:04 +0000660 sprintf(buf, " [%s%s]", disp,
661 (strlenpt(last_search) > COLS / 3) ? "..." : "");
662 free(disp);
663 } else
664 buf = mallocstrcpy(NULL, "");
665
666 /* This is now one simple call. It just does a lot. */
Benno Schulenbergfd0589d2017-01-02 21:12:44 +0100667 input = do_prompt(FALSE, FALSE, MWHEREISFILE, NULL,
Benno Schulenbergb341f292014-06-19 20:05:24 +0000668#ifndef DISABLE_HISTORIES
Benno Schulenbergb77b1392016-08-26 12:18:04 +0200669 &search_history,
David Lawrence Ramseye38b8082006-03-30 07:03:04 +0000670#endif
Benno Schulenbergb77b1392016-08-26 12:18:04 +0200671 browser_refresh, "%s%s", _("Search"), buf);
David Lawrence Ramseye38b8082006-03-30 07:03:04 +0000672
673 /* Release buf now that we don't need it anymore. */
674 free(buf);
675
Benno Schulenberg99d23582015-04-12 08:31:53 +0000676 /* If only Enter was pressed but we have a previous string, it's okay. */
677 if (input == -2 && *last_search != '\0')
678 return 0;
David Lawrence Ramseye38b8082006-03-30 07:03:04 +0000679
Benno Schulenberg99d23582015-04-12 08:31:53 +0000680 /* Otherwise negative inputs are a bailout. */
681 if (input < 0)
682 statusbar(_("Cancelled"));
683
Benno Schulenberg99d23582015-04-12 08:31:53 +0000684 return input;
David Lawrence Ramseye38b8082006-03-30 07:03:04 +0000685}
686
Benno Schulenbergd5177052015-04-07 13:34:12 +0000687/* Look for the given needle in the list of files. */
688void findnextfile(const char *needle)
David Lawrence Ramseye38b8082006-03-30 07:03:04 +0000689{
Benno Schulenberg2dc9cbe2015-04-08 18:40:40 +0000690 size_t looking_at = selected;
Benno Schulenbergd5177052015-04-07 13:34:12 +0000691 /* The location in the file list of the filename we're looking at. */
Benno Schulenberg3ea8f422016-04-29 17:20:59 +0200692 const char *thename;
693 /* The plain filename, without the path. */
Benno Schulenberg6bdcc8f2015-04-21 17:27:33 +0000694 unsigned stash[sizeof(flags) / sizeof(flags[0])];
695 /* A storage place for the current flag settings. */
696
697 /* Save the settings of all flags. */
698 memcpy(stash, flags, sizeof(flags));
699
Benno Schulenberg588daf92016-12-25 13:11:17 +0100700 /* Search forward, case insensitive, and without regexes. */
Benno Schulenberg6bdcc8f2015-04-21 17:27:33 +0000701 UNSET(BACKWARDS_SEARCH);
702 UNSET(CASE_SENSITIVE);
703 UNSET(USE_REGEXP);
David Lawrence Ramseye38b8082006-03-30 07:03:04 +0000704
Benno Schulenbergd5177052015-04-07 13:34:12 +0000705 /* Step through each filename in the list until a match is found or
706 * we've come back to the point where we started. */
David Lawrence Ramseye38b8082006-03-30 07:03:04 +0000707 while (TRUE) {
Benno Schulenberg3ea8f422016-04-29 17:20:59 +0200708 /* Move to the next filename in the list, or back to the first. */
Benno Schulenberg2dc9cbe2015-04-08 18:40:40 +0000709 if (looking_at < filelist_len - 1)
710 looking_at++;
Benno Schulenbergb0957252014-07-01 16:24:01 +0000711 else {
Benno Schulenberg2dc9cbe2015-04-08 18:40:40 +0000712 looking_at = 0;
Benno Schulenbergb0957252014-07-01 16:24:01 +0000713 statusbar(_("Search Wrapped"));
David Lawrence Ramseye38b8082006-03-30 07:03:04 +0000714 }
David Lawrence Ramseye38b8082006-03-30 07:03:04 +0000715
Benno Schulenberg3ea8f422016-04-29 17:20:59 +0200716 /* Get the bare filename, without the path. */
Benno Schulenbergb55e8d72016-03-21 20:49:29 +0000717 thename = tail(filelist[looking_at]);
Benno Schulenberg3ea8f422016-04-29 17:20:59 +0200718
719 /* If the needle matches, we're done. And if we're back at the file
720 * where we started, it is the only occurrence. */
721 if (strstrwrapper(thename, needle, thename)) {
Benno Schulenbergcdcd3652016-05-17 11:33:21 +0200722 if (looking_at == selected)
Benno Schulenberg3ea8f422016-04-29 17:20:59 +0200723 statusbar(_("This is the only occurrence"));
Benno Schulenbergcdcd3652016-05-17 11:33:21 +0200724 break;
Benno Schulenberg3ea8f422016-04-29 17:20:59 +0200725 }
726
727 /* If we're back at the beginning and didn't find any match... */
728 if (looking_at == selected) {
729 not_found_msg(needle);
730 break;
731 }
David Lawrence Ramseye38b8082006-03-30 07:03:04 +0000732 }
733
Benno Schulenberg6bdcc8f2015-04-21 17:27:33 +0000734 /* Restore the settings of all flags. */
735 memcpy(flags, stash, sizeof(flags));
736
Benno Schulenbergd5177052015-04-07 13:34:12 +0000737 /* Select the one we've found. */
Benno Schulenberg2dc9cbe2015-04-08 18:40:40 +0000738 selected = looking_at;
David Lawrence Ramseye38b8082006-03-30 07:03:04 +0000739}
740
David Lawrence Ramseye38b8082006-03-30 07:03:04 +0000741/* Search for a filename. */
742void do_filesearch(void)
743{
Benno Schulenberg9106cc82016-05-08 12:01:33 +0200744 /* If the user cancelled or jumped to first or last file, don't search. */
745 if (filesearch_init() != 0)
David Lawrence Ramseye38b8082006-03-30 07:03:04 +0000746 return;
747
748 /* If answer is now "", copy last_search into answer. */
David Lawrence Ramsey1904f512007-07-01 21:33:17 +0000749 if (*answer == '\0')
David Lawrence Ramseye38b8082006-03-30 07:03:04 +0000750 answer = mallocstrcpy(answer, last_search);
751 else
752 last_search = mallocstrcpy(last_search, answer);
753
Benno Schulenbergb341f292014-06-19 20:05:24 +0000754#ifndef DISABLE_HISTORIES
Benno Schulenberg1d3d3072016-05-27 21:31:55 +0200755 /* If answer is not empty, add the string to the search history list. */
756 if (*answer != '\0')
David Lawrence Ramseye38b8082006-03-30 07:03:04 +0000757 update_history(&search_history, answer);
758#endif
759
Benno Schulenbergd5177052015-04-07 13:34:12 +0000760 findnextfile(answer);
David Lawrence Ramseye38b8082006-03-30 07:03:04 +0000761}
762
Benno Schulenberg1d3d3072016-05-27 21:31:55 +0200763/* Search again for the last given filename, without prompting. */
David Lawrence Ramseye38b8082006-03-30 07:03:04 +0000764void do_fileresearch(void)
765{
Benno Schulenberg1d3d3072016-05-27 21:31:55 +0200766 if (*last_search == '\0')
Benno Schulenbergd5177052015-04-07 13:34:12 +0000767 statusbar(_("No current search pattern"));
768 else
769 findnextfile(last_search);
David Lawrence Ramseye38b8082006-03-30 07:03:04 +0000770}
771
David Lawrence Ramseyef45a252006-06-30 05:12:53 +0000772/* Select the first file in the list. */
David Lawrence Ramseye38b8082006-03-30 07:03:04 +0000773void do_first_file(void)
774{
775 selected = 0;
776}
777
David Lawrence Ramseyef45a252006-06-30 05:12:53 +0000778/* Select the last file in the list. */
David Lawrence Ramseye38b8082006-03-30 07:03:04 +0000779void do_last_file(void)
780{
781 selected = filelist_len - 1;
782}
783
David Lawrence Ramseya4e92c82006-06-30 14:14:40 +0000784/* Strip one directory from the end of path, and return the stripped
785 * path. The returned string is dynamically allocated, and should be
786 * freed. */
787char *striponedir(const char *path)
David Lawrence Ramsey64844b62005-11-05 20:04:16 +0000788{
David Lawrence Ramseya4e92c82006-06-30 14:14:40 +0000789 char *retval, *tmp;
David Lawrence Ramsey64844b62005-11-05 20:04:16 +0000790
David Lawrence Ramseya4e92c82006-06-30 14:14:40 +0000791 retval = mallocstrcpy(NULL, path);
792
793 tmp = strrchr(retval, '/');
David Lawrence Ramsey64844b62005-11-05 20:04:16 +0000794
795 if (tmp != NULL)
Benno Schulenberga65ef422014-03-17 21:36:37 +0000796 null_at(&retval, tmp - retval);
David Lawrence Ramseya4e92c82006-06-30 14:14:40 +0000797
798 return retval;
David Lawrence Ramsey64844b62005-11-05 20:04:16 +0000799}
800
David Lawrence Ramsey43fff012005-11-05 20:01:11 +0000801#endif /* !DISABLE_BROWSER */