nano

nano with my custom patches
git clone git://bsandro.tech/nano
Log | Files | Refs | README | LICENSE

browser.c (22254B)


      1 /**************************************************************************
      2  *   browser.c  --  This file is part of GNU nano.                        *
      3  *                                                                        *
      4  *   Copyright (C) 2001-2011, 2013-2025 Free Software Foundation, Inc.    *
      5  *   Copyright (C) 2015-2016, 2020, 2022 Benno Schulenberg                *
      6  *                                                                        *
      7  *   GNU nano is free software: you can redistribute it and/or modify     *
      8  *   it under the terms of the GNU General Public License as published    *
      9  *   by the Free Software Foundation, either version 3 of the License,    *
     10  *   or (at your option) any later version.                               *
     11  *                                                                        *
     12  *   GNU nano is distributed in the hope that it will be useful,          *
     13  *   but WITHOUT ANY WARRANTY; without even the implied warranty          *
     14  *   of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.              *
     15  *   See the GNU General Public License for more details.                 *
     16  *                                                                        *
     17  *   You should have received a copy of the GNU General Public License    *
     18  *   along with this program.  If not, see https://gnu.org/licenses/.     *
     19  *                                                                        *
     20  **************************************************************************/
     21 
     22 #include "prototypes.h"
     23 
     24 #ifdef ENABLE_BROWSER
     25 
     26 #include <errno.h>
     27 #include <stdint.h>
     28 #include <string.h>
     29 #include <unistd.h>
     30 
     31 static char **filelist = NULL;
     32 		/* The list of files to display in the file browser. */
     33 static size_t list_length = 0;
     34 		/* The number of files in the list. */
     35 static size_t usable_rows = 0;
     36 		/* The number of screen rows we can use to display the list. */
     37 static int piles = 0;
     38 		/* The number of files that we can display per screen row. */
     39 static int gauge = 0;
     40 		/* The width of a 'pile' -- the widest filename plus ten. */
     41 static size_t selected = 0;
     42 		/* The currently selected filename in the list; zero-based. */
     43 
     44 /* Fill 'filelist' with the names of the files in the given directory, set
     45  * 'list_length' to the number of names in that list, set 'gauge' to the
     46  * width of the widest filename plus ten, and set 'piles' to the number of
     47  * files that can be displayed per screen row.  And sort the list too. */
     48 void read_the_list(const char *path, DIR *dir)
     49 {
     50 	size_t path_len = strlen(path);
     51 	const struct dirent *entry;
     52 	size_t widest = 0;
     53 	size_t index = 0;
     54 
     55 	/* Find the width of the widest filename in the current folder. */
     56 	while ((entry = readdir(dir)) != NULL) {
     57 		size_t span = breadth(entry->d_name);
     58 
     59 		if (span > widest)
     60 			widest = span;
     61 
     62 		index++;
     63 	}
     64 
     65 	/* Reserve ten columns for blanks plus file size. */
     66 	gauge = widest + 10;
     67 
     68 	/* If needed, make room for ".. (parent dir)". */
     69 	if (gauge < 15)
     70 		gauge = 15;
     71 	/* Make sure we're not wider than the window. */
     72 	if (gauge > COLS)
     73 		gauge = COLS;
     74 
     75 	rewinddir(dir);
     76 
     77 	free_chararray(filelist, list_length);
     78 
     79 	list_length = index;
     80 	index = 0;
     81 
     82 	filelist = nmalloc(list_length * sizeof(char *));
     83 
     84 	while ((entry = readdir(dir)) != NULL && index < list_length) {
     85 		/* Don't show the useless dot item. */
     86 		if (strcmp(entry->d_name, ".") == 0)
     87 			continue;
     88 
     89 		filelist[index] = nmalloc(path_len + strlen(entry->d_name) + 1);
     90 		sprintf(filelist[index], "%s%s", path, entry->d_name);
     91 
     92 		index++;
     93 	}
     94 
     95 	/* Maybe the number of files in the directory decreased between
     96 	 * the first time we scanned and the second time. */
     97 	list_length = index;
     98 
     99 	/* Sort the list of names. */
    100 	qsort(filelist, list_length, sizeof(char *), diralphasort);
    101 
    102 	/* Calculate how many files fit on a line -- feigning room for two
    103 	 * spaces beyond the right edge, and adding two spaces of padding
    104 	 * between columns. */
    105 	piles = (COLS + 2) / (gauge + 2);
    106 
    107 	usable_rows = editwinrows - (ISSET(ZERO) && LINES > 1 ? 1 : 0);
    108 }
    109 
    110 /* Reselect the given file or directory name, if it still exists. */
    111 void reselect(const char *name)
    112 {
    113 	size_t looking_at = 0;
    114 
    115 	while (looking_at < list_length && strcmp(filelist[looking_at], name) != 0)
    116 		looking_at++;
    117 
    118 	/* If the sought name was found, select it; otherwise, just move
    119 	 * the highlight so that the changed selection will be noticed,
    120 	 * but make sure to stay within the current available range. */
    121 	if (looking_at < list_length)
    122 		selected = looking_at;
    123 	else if (selected > list_length)
    124 		selected = list_length - 1;
    125 	else
    126 		--selected;
    127 }
    128 
    129 /* Display at most a screenful of filenames from the gleaned filelist. */
    130 void browser_refresh(void)
    131 {
    132 	int row = 0, col = 0;
    133 		/* The current row and column while the list is getting displayed. */
    134 	int the_row = 0, the_column = 0;
    135 		/* The row and column of the selected item. */
    136 	char *info;
    137 		/* The additional information that we'll display about a file. */
    138 
    139 	titlebar(present_path);
    140 	blank_edit();
    141 
    142 	for (size_t index = selected - selected % (usable_rows * piles);
    143 					index < list_length && row < usable_rows; index++) {
    144 		const char *thename = tail(filelist[index]);
    145 				/* The filename we display, minus the path. */
    146 		size_t namelen = breadth(thename);
    147 				/* The length of the filename in columns. */
    148 		size_t infolen;
    149 				/* The length of the file information in columns. */
    150 		size_t infomaxlen = 7;
    151 				/* The maximum length of the file information in columns:
    152 				 * normally seven, but will be twelve for "(parent dir)". */
    153 		bool dots = (COLS >= 15 && namelen >= gauge - infomaxlen);
    154 				/* Whether to put an ellipsis before the filename?  We don't
    155 				 * waste space on dots when there are fewer than 15 columns. */
    156 		char *disp = display_string(thename, dots ?
    157 				namelen + infomaxlen + 4 - gauge : 0, gauge, FALSE, FALSE);
    158 				/* The filename (or a fragment of it) in displayable format.
    159 				 * When a fragment, account for dots plus one space padding. */
    160 		struct stat state;
    161 
    162 		/* If this is the selected item, draw its highlighted bar upfront, and
    163 		 * remember its location to be able to place the cursor on it. */
    164 		if (index == selected) {
    165 			wattron(midwin, interface_color_pair[SELECTED_TEXT]);
    166 			mvwprintw(midwin, row, col, "%*s", gauge, " ");
    167 			the_row = row;
    168 			the_column = col;
    169 		}
    170 
    171 		/* If the name is too long, we display something like "...ename". */
    172 		if (dots)
    173 			mvwaddstr(midwin, row, col, "...");
    174 		mvwaddstr(midwin, row, dots ? col + 3 : col, disp);
    175 
    176 		col += gauge;
    177 
    178 		/* Show information about the file: "--" for symlinks (except when
    179 		 * they point to a directory) and for files that have disappeared,
    180 		 * "(dir)" for directories, and the file size for normal files. */
    181 		if (lstat(filelist[index], &state) == -1 || S_ISLNK(state.st_mode)) {
    182 			if (stat(filelist[index], &state) == -1 || !S_ISDIR(state.st_mode))
    183 				info = copy_of("--");
    184 			else
    185 				/* TRANSLATORS: Anything more than 7 cells gets clipped. */
    186 				info = copy_of(_("(dir)"));
    187 		} else if (S_ISDIR(state.st_mode)) {
    188 			if (strcmp(thename, "..") == 0) {
    189 				/* TRANSLATORS: Anything more than 12 cells gets clipped. */
    190 				info = copy_of(_("(parent dir)"));
    191 				infomaxlen = 12;
    192 			} else
    193 				info = copy_of(_("(dir)"));
    194 		} else {
    195 			off_t result = state.st_size;
    196 			char modifier;
    197 
    198 			info = nmalloc(infomaxlen + 1);
    199 
    200 			/* Massage the file size into a human-readable form. */
    201 			if (state.st_size < (1 << 10))
    202 				modifier = ' ';  /* bytes */
    203 			else if (state.st_size < (1 << 20)) {
    204 				result >>= 10;
    205 				modifier = 'K';  /* kilobytes */
    206 			} else if (state.st_size < (1 << 30)) {
    207 				result >>= 20;
    208 				modifier = 'M';  /* megabytes */
    209 			} else {
    210 				result >>= 30;
    211 				modifier = 'G';  /* gigabytes */
    212 			}
    213 
    214 			/* Show the size if less than a terabyte, else show "(huge)". */
    215 			if (result < (1 << 10))
    216 				sprintf(info, "%4ju %cB", (intmax_t)result, modifier);
    217 			else
    218 				/* TRANSLATORS: Anything more than 7 cells gets clipped.
    219 				 * If necessary, you can leave out the parentheses. */
    220 				info = mallocstrcpy(info, _("(huge)"));
    221 		}
    222 
    223 		/* Make sure info takes up no more than infomaxlen columns. */
    224 		infolen = breadth(info);
    225 		if (infolen > infomaxlen) {
    226 			info[actual_x(info, infomaxlen)] = '\0';
    227 			infolen = infomaxlen;
    228 		}
    229 
    230 		mvwaddstr(midwin, row, col - infolen, info);
    231 
    232 		/* If this is the selected item, finish its highlighting. */
    233 		if (index == selected)
    234 			wattroff(midwin, interface_color_pair[SELECTED_TEXT]);
    235 
    236 		free(disp);
    237 		free(info);
    238 
    239 		/* Add some space between the columns. */
    240 		col += 2;
    241 
    242 		/* If the next item will not fit on this row, move to next row. */
    243 		if (col > COLS - gauge) {
    244 			row++;
    245 			col = 0;
    246 		}
    247 	}
    248 
    249 	/* If requested, put the cursor on the selected item and switch it on. */
    250 	if (ISSET(SHOW_CURSOR)) {
    251 		wmove(midwin, the_row, the_column);
    252 		curs_set(1);
    253 	}
    254 
    255 	wnoutrefresh(midwin);
    256 }
    257 
    258 /* Look for the given needle in the list of files, forwards or backwards. */
    259 void findfile(const char *needle, bool forwards)
    260 {
    261 	size_t began_at = selected;
    262 
    263 	/* Iterate through the list of filenames, until a match is found or
    264 	 * we've come back to the point where we started. */
    265 	while (TRUE) {
    266 		if (forwards) {
    267 			if (selected++ == list_length - 1) {
    268 				selected = 0;
    269 				statusbar(_("Search Wrapped"));
    270 			}
    271 		} else {
    272 			if (selected-- == 0) {
    273 				selected = list_length - 1;
    274 				statusbar(_("Search Wrapped"));
    275 			}
    276 		}
    277 
    278 		/* When the needle occurs in the basename of the file, we have a match. */
    279 		if (mbstrcasestr(tail(filelist[selected]), needle)) {
    280 			if (selected == began_at)
    281 				statusbar(_("This is the only occurrence"));
    282 			return;
    283 		}
    284 
    285 		/* When we're back at the starting point without any match... */
    286 		if (selected == began_at) {
    287 			not_found_msg(needle);
    288 			return;
    289 		}
    290 	}
    291 }
    292 
    293 /* Prepare the prompt and ask the user what to search for; then search for it.
    294  * If forwards is TRUE, search forward in the list; otherwise, search backward. */
    295 void search_filename(bool forwards)
    296 {
    297 	char *thedefault;
    298 	int response;
    299 
    300 	/* If something was searched for before, show it between square brackets. */
    301 	if (*last_search != '\0') {
    302 		char *disp = display_string(last_search, 0, COLS / 3, FALSE, FALSE);
    303 
    304 		thedefault = nmalloc(strlen(disp) + 7);
    305 		/* We use (COLS / 3) here because we need to see more on the line. */
    306 		sprintf(thedefault, " [%s%s]", disp,
    307 				(breadth(last_search) > COLS / 3) ? "..." : "");
    308 		free(disp);
    309 	} else
    310 		thedefault = copy_of("");
    311 
    312 	/* Now ask what to search for. */
    313 	response = do_prompt(MWHEREISFILE, "", &search_history,
    314 						browser_refresh, "%s%s%s", _("Search"),
    315 						/* TRANSLATORS: A modifier of the Search prompt. */
    316 						!forwards ? _(" [Backwards]") : "", thedefault);
    317 	free(thedefault);
    318 
    319 	/* If the user cancelled, or typed <Enter> on a blank answer and
    320 	 * nothing was searched for yet during this session, get out. */
    321 	if (response == -1 || (response == -2 && *last_search == '\0')) {
    322 		statusbar(_("Cancelled"));
    323 		return;
    324 	}
    325 
    326 	/* If the user typed an answer, remember it. */
    327 	if (*answer != '\0') {
    328 		last_search = mallocstrcpy(last_search, answer);
    329 #ifdef ENABLE_HISTORIES
    330 		update_history(&search_history, answer, PRUNE_DUPLICATE);
    331 #endif
    332 	}
    333 
    334 	if (response == 0 || response == -2)
    335 		findfile(last_search, forwards);
    336 }
    337 
    338 /* Search again without prompting for the last given search string,
    339  * either forwards or backwards. */
    340 void research_filename(bool forwards)
    341 {
    342 #ifdef ENABLE_HISTORIES
    343 	/* If nothing was searched for yet, take the last item from history. */
    344 	if (*last_search == '\0' && searchbot->prev != NULL)
    345 		last_search = mallocstrcpy(last_search, searchbot->prev->data);
    346 #endif
    347 
    348 	if (*last_search == '\0')
    349 		statusbar(_("No current search pattern"));
    350 	else {
    351 		wipe_statusbar();
    352 		findfile(last_search, forwards);
    353 	}
    354 }
    355 
    356 /* Select the first file in the list -- called directly by ^W^Y. */
    357 void to_first_file(void)
    358 {
    359 	selected = 0;
    360 }
    361 
    362 /* Select the last file in the list -- called directly by ^W^V. */
    363 void to_last_file(void)
    364 {
    365 	selected = list_length - 1;
    366 }
    367 
    368 /* Strip one element from the end of path, and return the stripped path.
    369  * The returned string is dynamically allocated, and should be freed. */
    370 char *strip_last_component(const char *path)
    371 {
    372 	char *copy = copy_of(path);
    373 	char *last_slash = strrchr(copy, '/');
    374 
    375 	if (last_slash != NULL)
    376 		*last_slash = '\0';
    377 
    378 	return copy;
    379 }
    380 
    381 /* Allow the user to browse through the directories in the filesystem,
    382  * starting at the given path. */
    383 char *browse(char *path)
    384 {
    385 	char *present_name = NULL;
    386 		/* The name of the currently selected file, or of the directory we
    387 		 * were in before backing up to "..". */
    388 	size_t old_selected;
    389 		/* The number of the selected file before the current selected file. */
    390 	DIR *dir;
    391 		/* The directory whose contents we are showing. */
    392 	char *chosen = NULL;
    393 		/* The name of the file that the user picked, or NULL if none. */
    394 
    395   read_directory_contents:
    396 		/* We come here when the user refreshes or selects a new directory. */
    397 
    398 	path = free_and_assign(path, get_full_path(path));
    399 
    400 	if (path != NULL)
    401 		dir = opendir(path);
    402 
    403 	if (path == NULL || dir == NULL) {
    404 		statusline(ALERT, _("Cannot open directory: %s"), strerror(errno));
    405 		/* If we don't have a file list, there is nothing to show. */
    406 		if (filelist == NULL) {
    407 			lastmessage = VACUUM;
    408 			free(present_name);
    409 			free(path);
    410 			napms(1200);
    411 			return NULL;
    412 		}
    413 		path = mallocstrcpy(path, present_path);
    414 		present_name = mallocstrcpy(present_name, filelist[selected]);
    415 	}
    416 
    417 	if (dir != NULL) {
    418 		/* Get the file list, and set gauge and piles in the process. */
    419 		read_the_list(path, dir);
    420 		closedir(dir);
    421 		dir = NULL;
    422 	}
    423 
    424 	/* If something was selected before, reselect it;
    425 	 * otherwise, just select the first item (..). */
    426 	if (present_name != NULL) {
    427 		reselect(present_name);
    428 		free(present_name);
    429 		present_name = NULL;
    430 	} else
    431 		selected = 0;
    432 
    433 	old_selected = (size_t)-1;
    434 
    435 	present_path = mallocstrcpy(present_path, path);
    436 
    437 	titlebar(path);
    438 
    439 	if (list_length == 0) {
    440 		statusline(ALERT, _("No entries"));
    441 		napms(1200);
    442 	} else while (TRUE) {
    443 		functionptrtype function;
    444 		int kbinput;
    445 
    446 		lastmessage = VACUUM;
    447 
    448 		bottombars(MBROWSER);
    449 
    450 		/* Display (or redisplay) the file list if the list itself or
    451 		 * the selected file has changed. */
    452 		if (old_selected != selected || ISSET(SHOW_CURSOR))
    453 			browser_refresh();
    454 
    455 		old_selected = selected;
    456 
    457 		kbinput = get_kbinput(midwin, ISSET(SHOW_CURSOR));
    458 
    459 #ifdef ENABLE_MOUSE
    460 		if (kbinput == KEY_MOUSE) {
    461 			int mouse_x, mouse_y;
    462 
    463 			/* When the user clicked in the file list, select a filename. */
    464 			if (get_mouseinput(&mouse_y, &mouse_x, TRUE) == 0 &&
    465 						wmouse_trafo(midwin, &mouse_y, &mouse_x, FALSE)) {
    466 				selected = selected - selected % (usable_rows * piles) +
    467 								(mouse_y * piles) + (mouse_x / (gauge + 2));
    468 
    469 				/* When beyond end-of-row, select the preceding filename. */
    470 				if (mouse_x > piles * (gauge + 2))
    471 					selected--;
    472 
    473 				/* When beyond end-of-list, select the last filename. */
    474 				if (selected > list_length - 1)
    475 					selected = list_length - 1;
    476 
    477 				/* When a filename is clicked a second time, choose it. */
    478 				if (old_selected == selected)
    479 					kbinput = KEY_ENTER;
    480 			}
    481 
    482 			if (kbinput == KEY_MOUSE)
    483 				continue;
    484 		}
    485 #endif /* ENABLE_MOUSE */
    486 
    487 		function = interpret(kbinput);
    488 
    489 		if (function == do_help || function == full_refresh) {
    490 			function();
    491 #ifndef NANO_TINY
    492 			/* Simulate a terminal resize to force a directory reread,
    493 			 * or because the terminal dimensions might have changed. */
    494 			kbinput = THE_WINDOW_RESIZED;
    495 		} else if (function == do_toggle && get_shortcut(kbinput)->toggle == NO_HELP) {
    496 			TOGGLE(NO_HELP);
    497 			window_init();
    498 			kbinput = THE_WINDOW_RESIZED;
    499 #endif
    500 		} else if (function == do_search_backward) {
    501 			search_filename(BACKWARD);
    502 		} else if (function == do_search_forward) {
    503 			search_filename(FORWARD);
    504 		} else if (function == do_findprevious) {
    505 			research_filename(BACKWARD);
    506 		} else if (function == do_findnext) {
    507 			research_filename(FORWARD);
    508 		} else if (function == do_left) {
    509 			if (selected > 0)
    510 				selected--;
    511 		} else if (function == do_right) {
    512 			if (selected < list_length - 1)
    513 				selected++;
    514 		} else if (function == to_prev_word) {
    515 			selected -= (selected % piles);
    516 		} else if (function == to_next_word) {
    517 			selected += piles - 1 - (selected % piles);
    518 			if (selected >= list_length)
    519 				selected = list_length - 1;
    520 		} else if (function == do_up) {
    521 			if (selected >= piles)
    522 				selected -= piles;
    523 		} else if (function == do_down) {
    524 			if (selected + piles <= list_length - 1)
    525 				selected += piles;
    526 		} else if (function == to_prev_block) {
    527 			selected = ((selected / (usable_rows * piles)) * usable_rows * piles) +
    528 								 selected % piles;
    529 		} else if (function == to_next_block) {
    530 			selected = ((selected / (usable_rows * piles)) * usable_rows * piles) +
    531 								selected % piles + usable_rows * piles - piles;
    532 			if (selected >= list_length)
    533 				selected = (list_length / piles) * piles + selected % piles;
    534 			if (selected >= list_length)
    535 				selected -= piles;
    536 		} else if (function == do_page_up) {
    537 			if (selected < piles)
    538 				selected = 0;
    539 			else if (selected < usable_rows * piles)
    540 				selected = selected % piles;
    541 			else
    542 				selected -= usable_rows * piles;
    543 		} else if (function == do_page_down) {
    544 			if (selected + piles >= list_length - 1)
    545 				selected = list_length - 1;
    546 			else if (selected + usable_rows * piles >= list_length)
    547 				selected = (selected + usable_rows * piles - list_length) % piles +
    548 								list_length - piles;
    549 			else
    550 				selected += usable_rows * piles;
    551 		} else if (function == to_first_file || function == to_last_file) {
    552 			function();
    553 		} else if (function == goto_dir) {
    554 			/* Ask for the directory to go to. */
    555 			if (do_prompt(MGOTODIR, "", NULL,
    556 							/* TRANSLATORS: This is a prompt. */
    557 							browser_refresh, _("Go To Directory")) < 0) {
    558 				statusbar(_("Cancelled"));
    559 				continue;
    560 			}
    561 
    562 			path = free_and_assign(path, real_dir_from_tilde(answer));
    563 
    564 			/* If the given path is relative, join it with the current path. */
    565 			if (*path != '/') {
    566 				path = nrealloc(path, strlen(present_path) + strlen(answer) + 1);
    567 				sprintf(path, "%s%s", present_path, answer);
    568 			}
    569 
    570 #ifdef ENABLE_OPERATINGDIR
    571 			if (outside_of_confinement(path, FALSE)) {
    572 				/* TRANSLATORS: This refers to the confining effect of
    573 				 * the option --operatingdir, not of --restricted. */
    574 				statusline(ALERT, _("Can't go outside of %s"), operating_dir);
    575 				path = mallocstrcpy(path, present_path);
    576 				continue;
    577 			}
    578 #endif
    579 			/* Snip any trailing slashes, so the name can be compared. */
    580 			while (strlen(path) > 1 && path[strlen(path) - 1] == '/')
    581 				path[strlen(path) - 1] = '\0';
    582 
    583 			/* In case the specified directory cannot be entered, select it
    584 			 * (if it is in the current list) so it will be highlighted. */
    585 			for (size_t j = 0; j < list_length; j++)
    586 				if (strcmp(filelist[j], path) == 0)
    587 					selected = j;
    588 
    589 			/* Try opening and reading the specified directory. */
    590 			goto read_directory_contents;
    591 		} else if (function == do_enter) {
    592 			struct stat st;
    593 
    594 			/* It isn't possible to move up from the root directory. */
    595 			if (strcmp(filelist[selected], "/..") == 0) {
    596 				statusline(ALERT, _("Can't move up a directory"));
    597 				continue;
    598 			}
    599 
    600 #ifdef ENABLE_OPERATINGDIR
    601 			/* Note: The selected file can be outside the operating
    602 			 * directory if it's ".." or if it's a symlink to a
    603 			 * directory outside the operating directory. */
    604 			if (outside_of_confinement(filelist[selected], FALSE)) {
    605 				statusline(ALERT, _("Can't go outside of %s"), operating_dir);
    606 				continue;
    607 			}
    608 #endif
    609 			/* If for some reason the file is inaccessible, complain. */
    610 			if (stat(filelist[selected], &st) == -1) {
    611 				statusline(ALERT, _("Error reading %s: %s"),
    612 								filelist[selected], strerror(errno));
    613 				continue;
    614 			}
    615 
    616 			/* If it isn't a directory, a file was selected -- we're done. */
    617 			if (!S_ISDIR(st.st_mode)) {
    618 				chosen = copy_of(filelist[selected]);
    619 				break;
    620 			}
    621 
    622 			/* If we are moving up one level, remember where we came from, so
    623 			 * this directory can be highlighted and easily reentered. */
    624 			if (strcmp(tail(filelist[selected]), "..") == 0)
    625 				present_name = strip_last_component(filelist[selected]);
    626 
    627 			/* Try opening and reading the selected directory. */
    628 			path = mallocstrcpy(path, filelist[selected]);
    629 			goto read_directory_contents;
    630 #ifdef ENABLE_NANORC
    631 		} else if (function == (functionptrtype)implant) {
    632 			implant(first_sc_for(MBROWSER, function)->expansion);
    633 #endif
    634 #ifndef NANO_TINY
    635 		} else if (kbinput == START_OF_PASTE) {
    636 			while (get_kbinput(midwin, BLIND) != END_OF_PASTE)
    637 				;
    638 			statusline(AHEM, _("Paste is ignored"));
    639 		} else if (kbinput == THE_WINDOW_RESIZED) {
    640 			;  /* Gets handled below. */
    641 #endif
    642 		} else if (function == do_exit) {
    643 			break;
    644 		} else
    645 			unbound_key(kbinput);
    646 
    647 #ifndef NANO_TINY
    648 		/* If the terminal resized (or might have), refresh the file list. */
    649 		if (kbinput == THE_WINDOW_RESIZED) {
    650 			/* Remember the selected file, to be able to reselect it. */
    651 			present_name = copy_of(filelist[selected]);
    652 			goto read_directory_contents;
    653 		}
    654 #endif
    655 	}
    656 
    657 	titlebar(NULL);
    658 	edit_refresh();
    659 
    660 	free(path);
    661 
    662 	free_chararray(filelist, list_length);
    663 	filelist = NULL;
    664 	list_length = 0;
    665 
    666 	return chosen;
    667 }
    668 
    669 /* Prepare to start browsing.  If the given path has a directory part,
    670  * start browsing in that directory, otherwise in the current directory. */
    671 char *browse_in(const char *inpath)
    672 {
    673 	char *path = real_dir_from_tilde(inpath);
    674 	struct stat fileinfo;
    675 
    676 	/* If path is not a directory, try to strip a filename from it; if then
    677 	 * still not a directory, use the current working directory instead. */
    678 	if (stat(path, &fileinfo) == -1 || !S_ISDIR(fileinfo.st_mode)) {
    679 		path = free_and_assign(path, strip_last_component(path));
    680 
    681 		if (stat(path, &fileinfo) == -1 || !S_ISDIR(fileinfo.st_mode)) {
    682 			path = free_and_assign(path, realpath(".", NULL));
    683 
    684 			if (path == NULL) {
    685 				statusline(ALERT, _("The working directory has disappeared"));
    686 				napms(1200);
    687 				return NULL;
    688 			}
    689 		}
    690 	}
    691 
    692 #ifdef ENABLE_OPERATINGDIR
    693 	/* If the resulting path isn't in the operating directory,
    694 	 * use the operating directory instead. */
    695 	if (outside_of_confinement(path, FALSE))
    696 		path = mallocstrcpy(path, operating_dir);
    697 #endif
    698 
    699 	return browse(path);
    700 }
    701 
    702 #endif /* ENABLE_BROWSER */