cex

C/Curses file EXplorer
git clone git://git.wimdupont.com/cex.git
Log | Files | Refs | LICENSE

cex.c (31285B)


      1 #include <sys/stat.h>
      2 #include <sys/param.h>
      3 #include <ctype.h>
      4 #include <unistd.h>
      5 #include <stdlib.h>
      6 #include <libgen.h>
      7 #include <string.h>
      8 #include <time.h>
      9 #include <langinfo.h>
     10 #include <pwd.h>
     11 #include <grp.h>
     12 #include <dirent.h>
     13 #include <errno.h>
     14 #include <curses.h>
     15 
     16 #include "config.h"
     17 
     18 #define ctrl(x)		((x) & 0x1f)
     19 
     20 typedef enum {OTHER, LEFT, RIGHT, UP, DOWN} Direction;
     21 typedef enum {CURRENT, PARENT, CHILD} WinType;
     22 typedef enum {COPY, REMOVE, MOVE} SelAction;
     23 
     24 typedef struct _win_file {
     25 	char d_name[256];
     26 	unsigned char d_type;
     27 	bool selected;
     28 } WinFile;
     29 
     30 typedef struct _dir_win {
     31 	WinType wintype;
     32 	WINDOW *window;
     33 	WinFile *winfiles;
     34 	char path[PATH_MAX];
     35 	char *message;
     36 	int maxx;
     37 	int maxy;
     38 	int startpr;
     39 	int highlight;
     40 	int filecount;
     41 	bool holdupdate;
     42 	bool usehighlight;
     43 } DirWin;
     44 
     45 static void init_dirwins(void);
     46 static void init_screen(void);
     47 static void resize(void);
     48 static void start(void);
     49 static void wpath(const char *filename);
     50 static void reset_flags(void);
     51 static void set_startpr(DirWin *dirwin);
     52 static void set_win_files(DirWin *dirwin);
     53 static void set_win_message(DirWin *dirwin, char *message);
     54 static void print_win(DirWin *dirwin);
     55 static void update_child_win(void);
     56 static void print_top_title(void);
     57 static void print_bot_title(void);
     58 static void print_content(void);
     59 static void show_file_mime(void);
     60 static void select_file(const char *path);
     61 static void exe_selection(SelAction action, const char *askn);
     62 static void clear_selected(void);
     63 static void clear_search(void);
     64 static void combo_key(int keypress);
     65 static void combo_go(int keypress);
     66 static void combo_inf(int keypress);
     67 static void combo_open(int keypress);
     68 static void combo_make(int keypress);
     69 static void change_dir(const char *chdname, Direction direction, bool abspath);
     70 static void change_parent_dir(Direction direction);
     71 static void open_child(bool exec);
     72 static void open_nohup_xdg(void);
     73 static void search(void);
     74 static void move_top(DirWin *dirwin);
     75 static void move_bot(DirWin *dirwin);
     76 static void move_page_up(DirWin *dirwin);
     77 static void move_page_down(DirWin *dirwin);
     78 static void move_up(DirWin *dirwin);
     79 static void move_down(DirWin *dirwin);
     80 static void next_search(void);
     81 static void prev_search(void);
     82 static void free_dirwin(DirWin *dirwin);
     83 static void run_command(size_t size, const char *fmt, ...);
     84 static void clean(void);
     85 static void fatal(const char *fmt, ...);
     86 static bool is_dir(DirWin *dirwin, size_t index);
     87 static bool is_selected(DirWin *dirwin, size_t count);
     88 static bool remove_selected(const char *sel);
     89 static bool prompt_confirm(size_t size, const char *fmt, ...);
     90 static int compare_file(const void *a, const void *b);
     91 static int get_mime(char *buf, size_t bufsize, const char *path);
     92 static int get_mime_default(char *buf, size_t bufsize, const char *mime);
     93 static int read_command(char *buf, size_t bufsize, const char *cmd);
     94 static int make_file(const char *path);
     95 static int make_dir(const char *path);
     96 static int make_mod(void);
     97 static int make_access(void);
     98 static int rm_file(const char *fname);
     99 static int rename_file(const char *fname);
    100 static char *prompt_answer(char *buf, size_t size, const char *question);
    101 static char *cpstr(char *dest, const char *src);
    102 static char *get_file_info(char *buf, const char *filepath);
    103 static char *get_fullpath(char *buf, DirWin *dirwin, size_t index);
    104 static char *get_dirname(char *buf, const char *path);
    105 static char *replace_home(char *buf, const char *path);
    106 
    107 static DirWin curwin, parwin, childwin;
    108 static char *userhome, *username, *editor, **selected, searchq[SEARCHLEN];
    109 static int maxy, maxx;
    110 static size_t selc, searchc;
    111 static bool print_bot, hide = DEFAULT_HIDE;
    112 
    113 int
    114 main(int argc, char **argv)
    115 {
    116 	struct passwd userinf;
    117 	char filename[PATH_MAX];
    118 	int opt;
    119 	bool writepath = FALSE;
    120 
    121 	while ((opt = getopt(argc, argv, "f:")) != -1) {
    122 		switch (opt) {
    123 			case 'f':
    124 				cpstr(filename, optarg);
    125 				writepath = TRUE;
    126 				break;
    127 			default:
    128 				fatal("Unknown option: %c\n", optopt);
    129 				break;
    130 		}
    131 	}
    132 
    133 	editor = getenv("EDITOR");
    134 	
    135 	userinf = *getpwuid(getuid());
    136 	username = strdup(userinf.pw_name);
    137 	userhome = strdup(userinf.pw_dir);
    138 
    139 	init_dirwins();
    140 	init_screen();
    141 
    142 	start();
    143 
    144 	if (writepath)
    145 		wpath(filename);
    146 
    147 	clean();
    148 }
    149 
    150 void
    151 init_dirwins(void)
    152 {
    153 	curwin.wintype = CURRENT;
    154 	parwin.wintype = PARENT;
    155 	childwin.wintype = CHILD;
    156 
    157 	reset_flags();
    158 
    159 	childwin.highlight = 0;
    160 
    161 	if (getcwd(curwin.path, PATH_MAX) == NULL)
    162 		fatal("getcwd() error");
    163 
    164 	get_dirname(parwin.path, curwin.path);
    165 
    166 	set_win_files(&curwin);
    167 	set_win_files(&parwin);
    168 
    169 	if (curwin.winfiles != NULL && curwin.winfiles[0].d_type == DT_DIR) {
    170 		cpstr(childwin.path, curwin.path);
    171 		update_child_win();
    172 	}
    173 }
    174 
    175 void
    176 init_screen(void)
    177 {
    178 	initscr();
    179 	noecho();
    180 	start_color();
    181 	use_default_colors();
    182 	keypad(stdscr, TRUE);
    183 	curs_set(0);
    184 
    185 	clear();
    186 	cbreak();
    187 
    188 	init_pair(COLOR_BLACK, COLOR_BLACK, -1);
    189 	init_pair(COLOR_RED, COLOR_RED, -1);
    190 	init_pair(COLOR_GREEN,COLOR_GREEN, -1);
    191 	init_pair(COLOR_YELLOW, COLOR_YELLOW, -1);
    192 	init_pair(COLOR_BLUE, COLOR_BLUE, -1);
    193 	init_pair(COLOR_MAGENTA, COLOR_MAGENTA, -1);
    194 	init_pair(COLOR_CYAN, COLOR_CYAN, -1);
    195 	init_pair(COLOR_WHITE, COLOR_WHITE, -1);
    196 
    197 	curwin.highlight = 0;
    198 	curwin.startpr = 0;
    199 
    200 	parwin.highlight = 0;
    201 	parwin.startpr = 0;
    202 
    203 	childwin.highlight = 0;
    204 	childwin.startpr = 0;
    205 
    206 	resize();
    207 
    208 	print_top_title();
    209 	print_bot_title();
    210 
    211 	print_win(&parwin);
    212 	print_win(&curwin);
    213 	print_win(&childwin);
    214 }
    215 
    216 void
    217 resize(void)
    218 {
    219 	int startx;
    220 
    221 	getmaxyx(stdscr, maxy, maxx);
    222 
    223 	parwin.maxy = maxy-BORDER_SPACE_SIZE;
    224 	parwin.maxx = maxx/4-BORDER_SPACE_SIZE > 0 ? maxx/4-BORDER_SPACE_SIZE : 1;
    225 	startx = maxx > BORDER_SPACE_SIZE ? BORDER_SPACE_SIZE : 0;
    226 	parwin.window = newwin(parwin.maxy, parwin.maxx, BORDER_SPACE_SIZE, startx);
    227 
    228 	curwin.maxy = maxy-BORDER_SPACE_SIZE;
    229 	curwin.maxx = maxx/4-BORDER_SPACE_SIZE > 0 ? maxx/4-BORDER_SPACE_SIZE : 1;
    230 	startx = maxx > curwin.maxx+BORDER_SPACE_SIZE*2 ? curwin.maxx+BORDER_SPACE_SIZE*2 : 0;
    231 	curwin.window = newwin(curwin.maxy, curwin.maxx, BORDER_SPACE_SIZE, startx);
    232 
    233 	childwin.maxy = maxy-BORDER_SPACE_SIZE;
    234 	childwin.maxx = maxx/2-BORDER_SPACE_SIZE > 0 ? maxx/2-BORDER_SPACE_SIZE : 1;
    235 	startx = maxx > childwin.maxx+BORDER_SPACE_SIZE*2 ? childwin.maxx+BORDER_SPACE_SIZE*2 : 0;
    236 	childwin.window = newwin(childwin.maxy, childwin.maxx, BORDER_SPACE_SIZE, startx);
    237 
    238 	clear();
    239 	refresh();
    240 
    241 	update_child_win();
    242 }
    243 
    244 void
    245 start(void)
    246 {
    247 	int c;
    248 	char chdname[PATH_MAX];
    249 
    250 	while ((c = getch()) != KEY_QUIT) {
    251 		move(1, 1);
    252 		clrtoeol();
    253 		reset_flags();
    254 		switch (c) {
    255 			case KEY_UP:
    256 			case KEY_VUP:
    257 				move_up(&curwin);
    258 				update_child_win();
    259 				break;
    260 			case KEY_DOWN:
    261 			case KEY_VDOWN:
    262 				move_down(&curwin);
    263 				update_child_win();
    264 				break;
    265 			case KEY_VPUP:
    266 				if (is_dir(&parwin, MAX(parwin.highlight-1, 0)))
    267 					change_parent_dir(UP);
    268 				break;
    269 			case KEY_VPDOWN:
    270 				if (is_dir(&parwin, MIN(parwin.highlight+1, parwin.filecount-1)))
    271 					change_parent_dir(DOWN);
    272 				break;
    273 			case KEY_PPAGE:
    274 			case ctrl('u'):
    275 				move_page_up(&curwin);
    276 				update_child_win();
    277 				break;
    278 			case KEY_NPAGE:
    279 			case ctrl('d'):
    280 				move_page_down(&curwin);
    281 				update_child_win();
    282 				break;
    283 			case KEY_BOT:
    284 				move_bot(&curwin);
    285 				update_child_win();
    286 				break;
    287 			case 10:
    288 			case KEY_VRIGHT:
    289 			case KEY_VRIGHT_ABS:
    290 				if (is_dir(&curwin, curwin.highlight))
    291 					change_dir(get_fullpath(chdname, &curwin, curwin.highlight),
    292 						RIGHT, c == KEY_VRIGHT_ABS);
    293 				else
    294 					open_child(FALSE);
    295 				break;
    296 			case KEY_LEFT:
    297 			case KEY_VLEFT:
    298 				change_dir(get_dirname(chdname, curwin.path), LEFT, FALSE);
    299 				break;
    300 			case KEY_SEARCH:
    301 				search();
    302 				update_child_win();
    303 				break;
    304 			case KEY_NEXT_SEARCH:
    305 				next_search();
    306 				update_child_win();
    307 				break;
    308 			case KEY_PREV_SEARCH:
    309 				prev_search();
    310 				update_child_win();
    311 				break;
    312 			case KEY_SEL_FILE:
    313 				select_file(get_fullpath(chdname, &curwin, curwin.highlight));
    314 				set_win_files(&curwin);
    315 				update_child_win();
    316 				break;
    317 			case KEY_CLEAR_SEL:
    318 				clear_selected();
    319 				set_win_files(&curwin);
    320 				set_win_files(&parwin);
    321 				update_child_win();
    322 				break;
    323 			case KEY_CLEAR_SEARCH:
    324 				clear_search();
    325 				set_win_files(&curwin);
    326 				break;
    327 			case KEY_CP_SEL:
    328 				exe_selection(COPY, NULL);
    329 				break;
    330 			case KEY_RM_SEL:
    331 				exe_selection(REMOVE, "Remove");
    332 				break;
    333 			case KEY_MV_SEL:
    334 				exe_selection(MOVE, "Move");
    335 				break;
    336 			case KEY_RM_FILE:
    337 				rm_file(curwin.winfiles[curwin.highlight].d_name);
    338 				set_win_files(&curwin);
    339 				update_child_win();
    340 				break;
    341 			case KEY_RENAME_FILE:
    342 				rename_file(curwin.winfiles[curwin.highlight].d_name);
    343 				set_win_files(&curwin);
    344 				update_child_win();
    345 				break;
    346 			case KEY_HIDE:
    347 				hide = hide ? FALSE : TRUE;
    348 				curwin.highlight = 0;
    349 				curwin.startpr = 0;
    350 				set_win_files(&curwin);
    351 				set_win_files(&parwin);
    352 				update_child_win();
    353 				break;
    354 			case KEY_COMBO_GO:
    355 			case KEY_COMBO_INF:
    356 			case KEY_COMBO_OPEN:
    357 			case KEY_COMBO_MAKE:
    358 				combo_key(c);
    359 				break;
    360 			case KEY_RESIZE:
    361 				resize();
    362 				break;
    363 			default:
    364 				break;
    365 		}
    366 
    367 		print_top_title();
    368 		if (print_bot)
    369 			print_bot_title();
    370 		print_win(&parwin);
    371 		print_win(&curwin);
    372 		print_win(&childwin);
    373 	}
    374 }
    375 
    376 void
    377 wpath(const char *filename)
    378 {
    379 	FILE *fptr;
    380 
    381 	if ((fptr = fopen(filename, "w")) == NULL)
    382 		fatal("Error opening file \"%s\"\n", filename);
    383 	fprintf(fptr, "%s\n", curwin.path);
    384 	fclose(fptr);
    385 }
    386 
    387 void
    388 reset_flags(void)
    389 {
    390 	parwin.usehighlight = FALSE;
    391 	curwin.usehighlight = TRUE;
    392 	childwin.usehighlight = TRUE;
    393 
    394 	parwin.holdupdate = FALSE;
    395 	curwin.holdupdate = FALSE;
    396 	childwin.holdupdate = FALSE;
    397 
    398 	print_bot = TRUE;
    399 }
    400 
    401 void
    402 set_startpr(DirWin *dirwin)
    403 {
    404 	if (dirwin->winfiles == NULL) {
    405 		dirwin->startpr = 0;
    406 		return;
    407 	}
    408 
    409 	dirwin->startpr = MAX(dirwin->highlight - dirwin->maxy/2, 0);
    410 }
    411 
    412 void
    413 set_win_files(DirWin *dirwin)
    414 {
    415 	struct dirent *ent;
    416 	size_t count = 0;
    417 	DIR *dir;
    418 
    419 	free_dirwin(dirwin);
    420 
    421 	if (dirwin->wintype == PARENT && strcmp(curwin.path, "/") == 0)
    422 		return;
    423 
    424 	if ((dir = opendir(dirwin->path)) == NULL) {
    425 		switch (errno) {
    426 			case EACCES:
    427 				set_win_message(dirwin, "No permission.");
    428 				return;
    429 			default:
    430 				fatal("Could not open directory: %s", dirwin->path);
    431 		}
    432 	}
    433 
    434 	if ((dirwin->winfiles = (WinFile*) malloc(sizeof(WinFile))) == NULL)
    435 		fatal("Fatal: failed to malloc.\n");
    436 
    437 	while ((ent = readdir(dir)) != NULL) {
    438 		if (strcmp(ent->d_name, ".") == 0
    439 				|| strcmp(ent->d_name, "..") == 0
    440 				|| (hide && ent->d_name[0] == '.'))
    441 			continue;
    442 		if ((dirwin->winfiles = (WinFile*) realloc(dirwin->winfiles, (count+1)*(sizeof(WinFile)))) == NULL)
    443 			fatal("Fatal: failed to realloc.\n");
    444 		cpstr(dirwin->winfiles[count].d_name, ent->d_name);
    445 		dirwin->winfiles[count].d_type = ent->d_type;
    446 		dirwin->winfiles[count].selected = is_selected(dirwin, count);
    447 		(count)++;
    448 	}
    449 	closedir(dir);
    450 
    451 	dirwin->filecount = count;
    452 
    453 	if (count == 0)
    454 		set_win_message(dirwin, "empty");
    455 	else
    456 		qsort(dirwin->winfiles, count, sizeof(WinFile), compare_file);
    457 }
    458 
    459 bool
    460 is_selected(DirWin *dirwin, size_t index)
    461 {
    462 	char buf[PATH_MAX];
    463 
    464 	if (selc == 0 || selected == NULL)
    465 		return FALSE;
    466 
    467 	get_fullpath(buf, dirwin, index);
    468 
    469 	for (size_t i = 0; i < selc; i++) {
    470 		if (strcmp(buf, selected[i]) == 0)
    471 			return TRUE;
    472 	}
    473 
    474 	return FALSE;
    475 }
    476 
    477 bool
    478 remove_selected(const char *sel)
    479 {
    480 	bool res = FALSE;
    481 
    482 	if (selc == 0)
    483 		return res;
    484 
    485 	for (size_t i = 0; i < selc; i++) {
    486 		if (strcmp(selected[i], sel) == 0) {
    487 			free(selected[i]);
    488 			selected[i] = selected[selc-1];
    489 			res = TRUE;
    490 			break;
    491 		}
    492 	}
    493 
    494 	if (!res)
    495 		return res;
    496 
    497 	if (--(selc) == 0) {
    498 		free(selected);
    499 		selected = NULL;
    500 	} else if ((selected = (char**) realloc(selected, selc * sizeof(char *))) == NULL)
    501 		fatal("Fatal: failed to realloc.\n");
    502 
    503 	return res;
    504 }
    505 
    506 void
    507 set_win_message(DirWin *dirwin, char *message)
    508 {
    509 	free_dirwin(dirwin);
    510 	dirwin->message = message;
    511 }
    512 
    513 void
    514 print_win(DirWin *dirwin)
    515 {
    516 	if (dirwin->holdupdate)
    517 		return;
    518 
    519 	size_t cplen, size = dirwin->maxx;
    520 	char *subs, name[size+1], sbuf[size+1], pathbuf[PATH_MAX];
    521 	int sindex, y = 0, x = 1;
    522 
    523 	wclear(dirwin->window);
    524 
    525 	set_startpr(dirwin);
    526 
    527 	if (dirwin->message != NULL) {
    528 		if (dirwin->wintype == CURRENT) {
    529 			wattron(dirwin->window, A_REVERSE);
    530 			mvwaddstr(dirwin->window, y, x, dirwin->message);
    531 			wattroff(dirwin->window, A_REVERSE);
    532 		} else {
    533 			wattron(dirwin->window, COLOR_PAIR(CHILDWIN_MESSAGE_COLOR));
    534 			mvwaddstr(dirwin->window, y, x, dirwin->message);
    535 			wattroff(dirwin->window, COLOR_PAIR(CHILDWIN_MESSAGE_COLOR));
    536 		}
    537 	} else if (dirwin->winfiles != NULL) {
    538 		while (!dirwin->usehighlight) {
    539 			for (size_t i = 0; i < dirwin->filecount; ++i) {
    540 				memcpy(name, dirwin->winfiles[i].d_name, size);
    541 				name[size] = '\0';
    542 				if (strcmp(basename(curwin.path), dirwin->winfiles[i].d_name) == 0) {
    543 					dirwin->usehighlight = TRUE;
    544 					dirwin->highlight = i;
    545 					set_startpr(dirwin);
    546 					break;
    547 				}
    548 			}
    549 			break;
    550 		}
    551 		for (size_t i = dirwin->startpr; i < dirwin->filecount; ++i) {
    552 			if (dirwin->winfiles[i].selected) {
    553 				wattron(dirwin->window, COLOR_PAIR(MARK_SELECTED_COLOR));
    554 				mvwaddch(dirwin->window, y, x-1, '|');
    555 				wattroff(dirwin->window, COLOR_PAIR(MARK_SELECTED_COLOR));
    556 			}
    557 			memcpy(name, dirwin->winfiles[i].d_name, size);
    558 			name[size-1] = '\0';
    559 			if (dirwin->usehighlight && dirwin->highlight == i) {
    560 				wattron(dirwin->window, A_REVERSE);
    561 				mvwaddstr(dirwin->window, y, x, name);
    562 				wattroff(dirwin->window, A_REVERSE);
    563 			} else if (dirwin->winfiles[i].d_type == DT_DIR) {
    564 				wattron(dirwin->window, COLOR_PAIR(DIR_COLOR));
    565 				wattron(dirwin->window, A_BOLD);
    566 				mvwaddstr(dirwin->window, y, x, name);
    567 				wattroff(dirwin->window, COLOR_PAIR(DIR_COLOR));
    568 				wattroff(dirwin->window, A_BOLD);
    569 			} else if (dirwin->winfiles[i].d_type == DT_LNK) {
    570 				if (access(get_fullpath(pathbuf, dirwin, i), F_OK) == 0) {
    571 					wattron(dirwin->window, COLOR_PAIR(LN_COLOR));
    572 					mvwaddstr(dirwin->window, y, x, name);
    573 					wattroff(dirwin->window, COLOR_PAIR(LN_COLOR));
    574 				} else {
    575 					wattron(dirwin->window, COLOR_PAIR(INVALID_LN_COLOR));
    576 					mvwaddstr(dirwin->window, y, x, name);
    577 					wattroff(dirwin->window, COLOR_PAIR(INVALID_LN_COLOR));
    578 				}
    579 			} else
    580 				mvwaddstr(dirwin->window, y, x, name);
    581 			if (dirwin->wintype == CURRENT && searchc > 0
    582 					&& ((subs = strstr(name, searchq)) != NULL)) {
    583 				sindex = (int) (subs - name);
    584 				wattron(dirwin->window, COLOR_PAIR(SEARCH_MATCH_COLOR));
    585 				if (dirwin->usehighlight && dirwin->highlight == i)
    586 					wattron(dirwin->window, A_REVERSE);
    587 				cplen = strlen(searchq);
    588 				memcpy(sbuf, name + sindex, cplen);
    589 				sbuf[cplen] = '\0';
    590 				mvwaddstr(dirwin->window, y, x+sindex, sbuf);
    591 				wattroff(dirwin->window, COLOR_PAIR(SEARCH_MATCH_COLOR));
    592 				if (dirwin->usehighlight && dirwin->highlight == i)
    593 					wattroff(dirwin->window, A_REVERSE);
    594 			}
    595 			y++;
    596 		}
    597 	}
    598 	wrefresh(dirwin->window);
    599 }
    600 
    601 void
    602 update_child_win(void)
    603 {
    604 	char pathbuf[PATH_MAX];
    605 
    606 	if (curwin.filecount <= 0) {
    607 		free_dirwin(&childwin);
    608 		return;
    609 	}
    610 
    611 	get_fullpath(childwin.path, &curwin, curwin.highlight);
    612 
    613 	switch (curwin.winfiles[curwin.highlight].d_type) {
    614 		case DT_LNK:
    615 			if (access(get_fullpath(pathbuf, &curwin, curwin.highlight), F_OK) != 0) {
    616 				set_win_message(&childwin, "Link no longer exists.");
    617 				break;
    618 			}
    619 			if (is_dir(&curwin, curwin.highlight))
    620 				goto dir;
    621 			else
    622 				goto reg;
    623 		case DT_DIR:
    624 	dir:
    625 			set_win_files(&childwin);
    626 			break;
    627 		case DT_REG:
    628 	reg:
    629 			print_content();
    630 			break;
    631 		default:
    632 			free_dirwin(&childwin);
    633 			break;
    634 	}
    635 }
    636 
    637 void
    638 print_top_title(void)
    639 {
    640 	move(0, 0);
    641 	clrtoeol();
    642 	attron(COLOR_PAIR(TOP_TITLE_COLOR));
    643 	printw("%s:%s/%s", username, curwin.path, curwin.winfiles[curwin.highlight].d_name);
    644 	attroff(COLOR_PAIR(TOP_TITLE_COLOR));
    645 }
    646 
    647 void
    648 print_bot_title(void)
    649 {
    650 	char pathbuf[PATH_MAX], fileinf[maxx];
    651 
    652 	get_fullpath(pathbuf, &curwin, curwin.highlight);
    653 	memcpy(fileinf, get_file_info(fileinf, pathbuf), maxx);
    654 	fileinf[maxx] = '\0';
    655 
    656 	move(maxy-1, 0);
    657 	clrtoeol();
    658 
    659 	attron(COLOR_PAIR(BOT_TITLE_COUNT_COLOR));
    660 	printw("(%d/%d) ", curwin.filecount == 0 ? 0 : curwin.highlight+1, curwin.filecount);
    661 	attroff(COLOR_PAIR(BOT_TITLE_COUNT_COLOR));
    662 
    663 	attron(COLOR_PAIR(BOT_TITLE_INFO_COLOR));
    664 	addstr(fileinf);
    665 	attroff(COLOR_PAIR(BOT_TITLE_INFO_COLOR));
    666 }
    667 
    668 char *
    669 get_file_info(char *buf, const char *filepath)
    670 {
    671 	struct stat statbuf;
    672 	struct passwd *usr;
    673 	struct tm *tm;
    674 	struct group *grp;
    675 	char mods[11], datestr[256], modfill = '-';
    676 
    677 	if ((stat(filepath, &statbuf)) != 0) {
    678 		switch (errno) {
    679 			case EACCES:
    680 				buf = "No permission.";
    681 				break;
    682 			default:
    683 				buf = "Retrieving filestats failed.";
    684 				break;
    685 		}
    686 		return buf;
    687 	}
    688 
    689 	if ((usr = getpwuid(statbuf.st_uid)) == NULL) {
    690 		buf = "Retrieving username failed.";
    691 		return buf;
    692 	}
    693 	if ((grp = getgrgid(statbuf.st_gid)) == NULL) {
    694 		buf = "Retrieving groupname failed.";
    695 		return buf;
    696 	}
    697 
    698 	mods[0] = S_ISDIR(statbuf.st_mode) ? 'd' : modfill;
    699 	mods[1] = statbuf.st_mode & S_IRUSR ? 'r' : modfill;
    700 	mods[2] = statbuf.st_mode & S_IWUSR ? 'w' : modfill;
    701 	mods[3] = statbuf.st_mode & S_IXUSR ? 'x' : modfill;
    702 	mods[4] = statbuf.st_mode & S_IRGRP ? 'r' : modfill;
    703 	mods[5] = statbuf.st_mode & S_IWGRP ? 'w' : modfill;
    704 	mods[6] = statbuf.st_mode & S_IXGRP ? 'x' : modfill;
    705 	mods[7] = statbuf.st_mode & S_IROTH ? 'r' : modfill;
    706 	mods[8] = statbuf.st_mode & S_IWOTH ? 'w' : modfill;
    707 	mods[9] = statbuf.st_mode & S_IXOTH ? 'x' : modfill;
    708 	mods[10] = '\0';
    709 
    710 	tm = localtime(&statbuf.st_mtime);
    711 	strftime(datestr, sizeof(datestr), nl_langinfo(D_T_FMT), tm);
    712 
    713 	sprintf(buf, "%10.10s %s %s %9jd %s",
    714 		mods,
    715 		usr->pw_name,
    716 		grp->gr_name,
    717 		(intmax_t)statbuf.st_size,
    718 		datestr);
    719 
    720 	return buf;
    721 }
    722 
    723 void
    724 print_content(void)
    725 {
    726 	FILE *fp = NULL;
    727 	size_t size = childwin.maxx;
    728 	char str[size+1], buf[PATH_MAX];
    729 	int y = 0, x = 0;
    730 
    731 	werase(childwin.window);
    732 	free_dirwin(&childwin);
    733 
    734 	if ((fp = fopen(get_fullpath(buf, &curwin, curwin.highlight), "r")) == NULL) {
    735 		switch (errno) {
    736 			case EACCES:
    737 				set_win_message(&childwin, "No permission.");
    738 				return;
    739 			default:
    740 				fatal("Error opening file \"%s\"\n", buf);
    741 		}
    742 	}
    743 
    744 	childwin.holdupdate = TRUE;
    745 
    746 	while (fgets(str, size, fp) != NULL && y <= childwin.maxy) {
    747 		mvwaddstr(childwin.window, y, x, str);
    748 		y++;
    749 	}
    750 
    751 	fclose(fp);
    752 	wrefresh(childwin.window);
    753 }
    754 
    755 void
    756 show_file_mime(void)
    757 {
    758 	char buf[MIME_MAX];
    759 
    760 	if (get_mime(buf, MIME_MAX, curwin.winfiles[curwin.highlight].d_name) != 0)
    761 		cpstr(buf, "Can't retrieve mime.");
    762 
    763 	move(1, 1);
    764 	clrtoeol();
    765 	addstr(buf);
    766 }
    767 
    768 void
    769 combo_key(int keypress)
    770 {
    771 	int c;
    772 
    773 	move(maxy-1, maxx-1);
    774 	clrtoeol();
    775 	addch(keypress);
    776 
    777 	halfdelay(10);
    778 
    779 	while ((c = getch()) != ERR) {
    780 		switch (keypress) {
    781 			case KEY_COMBO_GO:
    782 				combo_go(c);
    783 				break;
    784 			case KEY_COMBO_INF:
    785 				combo_inf(c);
    786 				break;
    787 			case KEY_COMBO_OPEN:
    788 				combo_open(c);
    789 				break;
    790 			case KEY_COMBO_MAKE:
    791 				combo_make(c);
    792 				break;
    793 			default:
    794 				break;
    795 		}
    796 		break;
    797 	}
    798 
    799 	cbreak();
    800 	move(maxy-1, maxx-1);
    801 	clrtoeol();
    802 }
    803 
    804 void
    805 combo_go(int key)
    806 {
    807 	char pathbuf[PATH_MAX];
    808 
    809 	switch (key) {
    810 		case KEY_COMBO_GO_TOP:
    811 			move_top(&curwin);
    812 			update_child_win();
    813 			break;
    814 		case KEY_COMBO_GO_HOME:
    815 			change_dir(userhome, OTHER, FALSE);
    816 			break;
    817 		case KEY_COMBO_GO_ACCESS:
    818 			change_dir(replace_home(pathbuf, LN_ACCESS_DIR), OTHER, FALSE);
    819 			break;
    820 		default:
    821 			break;
    822 	}
    823 }
    824 
    825 void
    826 combo_inf(int key)
    827 {
    828 	switch (key) {
    829 		case KEY_COMBO_INF_OPEN:
    830 			show_file_mime();
    831 			break;
    832 		default:
    833 			break;
    834 	}
    835 }
    836 
    837 void
    838 combo_open(int key)
    839 {
    840 	char chdname[PATH_MAX];
    841 
    842 	switch (key) {
    843 		case KEY_COMBO_OPEN_NOHUP_XDG:
    844 			if (is_dir(&curwin, curwin.highlight))
    845 				change_dir(get_fullpath(chdname, &curwin, curwin.highlight), RIGHT, FALSE);
    846 			else
    847 				open_nohup_xdg();
    848 			break;
    849 		case KEY_COMBO_OPEN_EXEC:
    850 			if (is_dir(&curwin, curwin.highlight))
    851 				change_dir(get_fullpath(chdname, &curwin, curwin.highlight), RIGHT, FALSE);
    852 			else
    853 				open_child(TRUE);
    854 			break;
    855 		default:
    856 			break;
    857 	}
    858 }
    859 
    860 void
    861 combo_make(int key)
    862 {
    863 	switch (key) {
    864 		case KEY_COMBO_MAKE_FILE:
    865 			make_file(curwin.path);
    866 			set_win_files(&curwin);
    867 			update_child_win();
    868 			break;
    869 		case KEY_COMBO_MAKE_DIR:
    870 			make_dir(curwin.path);
    871 			set_win_files(&curwin);
    872 			update_child_win();
    873 			break;
    874 		case KEY_COMBO_MAKE_MOD:
    875 			make_mod();
    876 			break;
    877 		case KEY_COMBO_MAKE_ACCESS:
    878 			make_access();
    879 			break;
    880 		default:
    881 			break;
    882 	}
    883 }
    884 
    885 void
    886 change_dir(const char *chdname, Direction direction, bool abspath)
    887 {
    888 	if (chdir(chdname) != 0)
    889 		return;
    890 
    891 	if (!abspath)
    892 		cpstr(curwin.path, chdname);
    893 	else if (getcwd(curwin.path, PATH_MAX) == NULL)
    894 		fatal("getcwd() error");
    895 
    896 	switch (direction) {
    897 		case RIGHT:
    898 			curwin.highlight = 0;
    899 			break;
    900 		case LEFT:
    901 			curwin.highlight = parwin.highlight;
    902 			break;
    903 		default:
    904 			curwin.highlight = 0;
    905 			break;
    906 
    907 	}
    908 
    909 	set_win_files(&curwin);
    910 
    911 	get_dirname(parwin.path, curwin.path);
    912 	set_win_files(&parwin);
    913 
    914 	update_child_win();
    915 }
    916 
    917 void
    918 change_parent_dir(Direction direction)
    919 {
    920 	char pp[PATH_MAX];
    921 
    922 	switch (direction) {
    923 		case UP:
    924 			move_up(&parwin);
    925 			break;
    926 		case DOWN:
    927 			move_down(&parwin);
    928 			break;
    929 		default:
    930 			return;
    931 	}
    932 
    933 	parwin.usehighlight = TRUE;
    934 	curwin.highlight = 0;
    935 	change_dir(get_fullpath(pp, &parwin, parwin.highlight), direction, FALSE);
    936 }
    937 
    938 void
    939 open_child(bool exec)
    940 {
    941 	sigset_t set;
    942 	char mime[MIME_MAX], mimedefault[MIME_APP_MAX], pathbuf[PATH_MAX];
    943 	bool istext;
    944 
    945 	if (curwin.message != NULL)
    946 		return;
    947 
    948 	if (!exec) {
    949 		if (get_mime(mime, MIME_MAX, curwin.winfiles[curwin.highlight].d_name) != 0) {
    950 			move(1, 1);
    951 			clrtoeol();
    952 			printw("Can\'t open %s", curwin.winfiles[curwin.highlight].d_name);
    953 			return;
    954 		}
    955 
    956 		istext = strncmp(mime, "text/", 5) == 0;
    957 
    958 		if (!istext && get_mime_default(mimedefault, MIME_APP_MAX, mime) != 0) {
    959 			move(1, 1);
    960 			clrtoeol();
    961 			printw("Can\'t open for mime %s", mime);
    962 			return;
    963 		}
    964 	}
    965 
    966 	endwin();
    967 
    968 	if (sigemptyset (&set) == -1)
    969 		fatal("Sigemptyset failed.");
    970 	if (sigaddset(&set, SIGWINCH) == -1)
    971 		fatal("Sigaddset failed.");
    972 	if (sigprocmask(SIG_BLOCK, &set, NULL) != 0)
    973 		fatal("Blocking sigprocmask failed.");
    974 
    975 	if (exec)
    976 		run_command(PATH_MAX + 4, "\"./%s\"", curwin.winfiles[curwin.highlight].d_name);
    977 	else
    978 		run_command(PATH_MAX + 12, "%s \"%s\"", istext ? editor : "xdg-open",
    979 			get_fullpath(pathbuf, &curwin, curwin.highlight));
    980 
    981 	if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0)
    982 		fatal("Unblocking sigprocmask failed.");
    983 
    984 	clear();
    985 	noecho();
    986 	cbreak();
    987 
    988 	refresh();
    989 	wrefresh(parwin.window);
    990 	wrefresh(curwin.window);
    991 	wrefresh(childwin.window);
    992 	update_child_win();
    993 }
    994 
    995 void
    996 open_nohup_xdg(void)
    997 {
    998 	char *cmdfmt = "nohup xdg-open \"%s\" > /dev/null 2>&1 &";
    999 	size_t size = PATH_MAX + strlen(cmdfmt);
   1000 
   1001 	run_command(size, cmdfmt, curwin.winfiles[curwin.highlight].d_name);
   1002 }
   1003 
   1004 void
   1005 search(void)
   1006 {
   1007 	int c, y, x;
   1008 
   1009 	clear_search();
   1010 
   1011 	move(maxy-1, 0);
   1012 	clrtoeol();
   1013 
   1014 	attron(COLOR_PAIR(PROMPT_COLOR));
   1015 	addstr("Search name? ");
   1016 	attroff(COLOR_PAIR(PROMPT_COLOR));
   1017 
   1018 	echo();
   1019 	curs_set(1);
   1020 
   1021 	getyx(stdscr, y, x);
   1022 
   1023 	while ((c = mvwgetch(stdscr, y, x)) != 10) {
   1024 		switch (c) {
   1025 			case KEY_BACKSPACE:
   1026 				if (searchc > 0) {
   1027 					searchq[--(searchc)] = '\0';
   1028 					move(y, --x);
   1029 					clrtoeol();
   1030 				}
   1031 				break;
   1032 			default:
   1033 				if (searchc < SEARCHLEN) {
   1034 					searchq[(searchc)++] = c;
   1035 					move(y, ++x);
   1036 				}
   1037 				break;
   1038 		}
   1039 		print_win(&curwin);
   1040 	}
   1041 	noecho();
   1042 	curs_set(0);
   1043 }
   1044 
   1045 void
   1046 move_top(DirWin *dirwin)
   1047 {
   1048 	dirwin->highlight = 0;
   1049 }
   1050 
   1051 void
   1052 move_bot(DirWin *dirwin)
   1053 {
   1054 	dirwin->highlight = MAX(dirwin->filecount-1, 0);
   1055 }
   1056 
   1057 void
   1058 move_page_up(DirWin *dirwin)
   1059 {
   1060 	dirwin->highlight = MAX(dirwin->highlight - dirwin->maxy/2, 0);
   1061 }
   1062 
   1063 void
   1064 move_page_down(DirWin *dirwin)
   1065 {
   1066 	dirwin->highlight = MIN(dirwin->highlight + dirwin->maxy/2,
   1067 		MAX(dirwin->filecount-1, 0));
   1068 }
   1069 
   1070 void
   1071 move_up(DirWin *dirwin)
   1072 {
   1073 	if (dirwin->highlight > 0)
   1074 		dirwin->highlight--;
   1075 }
   1076 
   1077 void
   1078 move_down(DirWin *dirwin)
   1079 {
   1080 	if (dirwin->highlight < dirwin->filecount-1)
   1081 		dirwin->highlight++;
   1082 }
   1083 
   1084 void
   1085 next_search(void)
   1086 {
   1087 	if (searchc == 0)
   1088 		return;
   1089 
   1090 	for (size_t i = curwin.highlight; i < MAX(curwin.filecount-1, 0); i++) {
   1091 		if ((strstr(curwin.winfiles[i+1].d_name, searchq) != NULL)) {
   1092 			curwin.highlight = i+1;
   1093 			return;
   1094 		}
   1095 	}
   1096 }
   1097 
   1098 void
   1099 prev_search(void)
   1100 {
   1101 	if (searchc == 0)
   1102 		return;
   1103 
   1104 	for (size_t i = curwin.highlight; i > 0; i--) {
   1105 		if ((strstr(curwin.winfiles[i-1].d_name, searchq) != NULL)) {
   1106 			curwin.highlight = i-1;
   1107 			return;
   1108 		}
   1109 	}
   1110 }
   1111 
   1112 void
   1113 free_dirwin(DirWin *dirwin)
   1114 {
   1115 	free(dirwin->winfiles);
   1116 	dirwin->filecount = 0;
   1117 	dirwin->winfiles = NULL;
   1118 	dirwin->message = NULL;
   1119 }
   1120 
   1121 void
   1122 clean(void)
   1123 {
   1124 	wclear(curwin.window);
   1125 	wclear(parwin.window);
   1126 	wclear(childwin.window);
   1127 
   1128 	free(username);
   1129 	free(userhome);
   1130 
   1131 	clear();
   1132 
   1133 	delwin(parwin.window);
   1134 	delwin(curwin.window);
   1135 	delwin(childwin.window);
   1136 	delwin(stdscr);
   1137 
   1138 	free_dirwin(&curwin);
   1139 	free_dirwin(&parwin);
   1140 	free_dirwin(&childwin);
   1141 
   1142 	clear_selected();
   1143 
   1144 	endwin();
   1145 }
   1146 
   1147 void
   1148 fatal(const char *fmt, ...)
   1149 {
   1150 	va_list ap;
   1151 
   1152 	va_start(ap, fmt);
   1153 	vfprintf(stderr, fmt, ap);
   1154 	va_end(ap);
   1155 
   1156 	clean();
   1157 
   1158 	exit(EXIT_FAILURE);
   1159 }
   1160 
   1161 bool
   1162 is_dir(DirWin *dirwin, size_t index)
   1163 {
   1164 	struct stat statbuf;
   1165 	char linkpath[PATH_MAX], abspath[PATH_MAX];
   1166 
   1167 	if (dirwin->filecount <= 0)
   1168 		return FALSE;
   1169 
   1170 	if (dirwin->winfiles[index].d_type == DT_DIR)
   1171 		return TRUE;
   1172 
   1173 	get_fullpath(linkpath, dirwin, index);
   1174 
   1175 	if (dirwin->winfiles[index].d_type == DT_LNK)
   1176 		return (lstat(realpath(linkpath, abspath), &statbuf) >= 0) && S_ISDIR(statbuf.st_mode);
   1177 
   1178 	return FALSE;
   1179 }
   1180 
   1181 char *
   1182 prompt_answer(char *buf, size_t size, const char *question)
   1183 {
   1184 	move(maxy-1, 0);
   1185 	clrtoeol();
   1186 
   1187 	attron(COLOR_PAIR(PROMPT_COLOR));
   1188 	addstr(question);
   1189 	attroff(COLOR_PAIR(PROMPT_COLOR));
   1190 
   1191 	echo();
   1192 	curs_set(1);
   1193 	getnstr(buf, size);
   1194 	noecho();
   1195 	curs_set(0);
   1196 
   1197 	return buf;
   1198 }
   1199 
   1200 bool
   1201 prompt_confirm(size_t size, const char *fmt, ...)
   1202 {
   1203 	char response[1], question[size];
   1204 	va_list ap;
   1205 
   1206 	va_start(ap, fmt);
   1207 	vsnprintf(question, size, fmt, ap);
   1208 	va_end(ap);
   1209 
   1210 	prompt_answer(response, 1, question);
   1211 
   1212 	return strcasecmp(response, "y") == 0;
   1213 }
   1214 
   1215 int
   1216 compare_file(const void *a, const void *b)
   1217 {
   1218 	int typecompare;
   1219 	const WinFile *ma = a;
   1220 	const WinFile *mb = b;
   1221 
   1222 	if ((typecompare = ma->d_type - mb->d_type) == 0)
   1223 		return strcmp(ma->d_name, mb->d_name);
   1224 
   1225 	return typecompare;
   1226 }
   1227 
   1228 int
   1229 get_mime(char *buf, size_t bufsize, const char *path)
   1230 {
   1231 	char *cmdfmt = "xdg-mime query filetype \"%s\"";
   1232 	size_t size = PATH_MAX + strlen(cmdfmt);
   1233 	char cmd[size];
   1234 
   1235 	snprintf(cmd, size, cmdfmt, path);
   1236 
   1237 	return read_command(buf, bufsize, cmd);
   1238 }
   1239 
   1240 int
   1241 get_mime_default(char *buf, size_t bufsize, const char *mime)
   1242 {
   1243 	char *cmdfmt = "xdg-mime query default \"%s\"";
   1244 	size_t cmdsize = MIME_MAX + strlen(cmdfmt);
   1245 	char cmd[cmdsize];
   1246 
   1247 	snprintf(cmd, cmdsize, cmdfmt, mime);
   1248 
   1249 	return read_command(buf, bufsize, cmd);
   1250 }
   1251 
   1252 int
   1253 read_command(char *buf, size_t bufsize, const char *cmd)
   1254 {
   1255 	FILE *file;
   1256 
   1257 	if ((file = popen(cmd, "r")) == NULL) {
   1258 		buf = NULL;
   1259 		return 1;
   1260 	}
   1261 
   1262 	if (fgets(buf, bufsize, file) == NULL) {
   1263 		buf = NULL;
   1264 		pclose(file);
   1265 		return 2;
   1266 	}
   1267 
   1268 	buf[strlen(buf)-1] = '\0';
   1269 
   1270 	pclose(file);
   1271 
   1272 	return 0;
   1273 }
   1274 
   1275 void
   1276 run_command(size_t size, const char *fmt, ...)
   1277 {
   1278 	va_list ap;
   1279 	char cmd[size];
   1280 
   1281 	va_start(ap, fmt);
   1282 	vsnprintf(cmd, size, fmt, ap);
   1283 	va_end(ap);
   1284 
   1285 	system(cmd);
   1286 }
   1287 
   1288 int
   1289 make_dir(const char *path)
   1290 {
   1291 	char name[PATH_MAX];
   1292 
   1293 	prompt_answer(name, PATH_MAX, "Name of directory? ");
   1294 	return mkdir(name, 0755);
   1295 }
   1296 
   1297 int
   1298 make_file(const char *path)
   1299 {
   1300 	FILE *fptr;
   1301 	char name[PATH_MAX];
   1302 
   1303 	prompt_answer(name, PATH_MAX, "Name of file? ");
   1304 
   1305 	if (strlen(name) > 0) {
   1306 		if ((fptr = fopen(name, "w")) == NULL)
   1307 			fatal("Error opening file \"%s\"\n", name);
   1308 		fclose(fptr);
   1309 	}
   1310 }
   1311 
   1312 int
   1313 rm_file(const char *fname)
   1314 {
   1315 	char *msg, *rmdirfmt = "rm -rf \"%s\"", *pfmt = "Remove %s? (y/N) ";
   1316 	int res = 0;
   1317 	size_t cmdsize = PATH_MAX + strlen(rmdirfmt), psize = strlen(pfmt) + strlen(fname);
   1318 
   1319 	if (prompt_confirm(psize, pfmt, fname)) {
   1320 		if (is_dir(&curwin, curwin.highlight))
   1321 			run_command(cmdsize, rmdirfmt, fname);
   1322 		else
   1323 			res = remove(fname);
   1324 		curwin.highlight = MAX(curwin.highlight-1, 0);
   1325 	}
   1326 
   1327 	if (res != 0) {
   1328 		switch (errno) {
   1329 			case EACCES:
   1330 				msg = "No permission.";
   1331 				break;
   1332 			case EEXIST:
   1333 			case ENOTEMPTY:
   1334 				msg = "Directory is not empty.";
   1335 				break;
   1336 			default:
   1337 				msg = "Could not remove file.";
   1338 				break;
   1339 		}
   1340 		move(maxy-1, 0);
   1341 		clrtoeol();
   1342 		addstr(msg);
   1343 		print_bot = FALSE;
   1344 	}
   1345 
   1346 	return res;
   1347 }
   1348 
   1349 int
   1350 rename_file(const char *fname)
   1351 {
   1352 	char name[PATH_MAX];
   1353 
   1354 	prompt_answer(name, PATH_MAX, "New name? ");
   1355 
   1356 	if (strlen(name) > 0)
   1357 		return rename(fname, name);
   1358 
   1359 	return 0;
   1360 }
   1361 
   1362 int
   1363 make_mod(void)
   1364 {
   1365 	char name[3];
   1366 	bool isvalid;
   1367 
   1368 	prompt_answer(name, 3, "File mods (numeric)? ");
   1369 
   1370 	if (strlen(name) == 3) {
   1371 		isvalid = TRUE;
   1372 		for (size_t i = 0; i < 3; i++) {
   1373 			if (!isdigit(name[i]))
   1374 				isvalid = FALSE;
   1375 		}
   1376 		if (isvalid)
   1377 			chmod(curwin.winfiles[curwin.highlight].d_name, strtol(name, NULL, 8));
   1378 		else
   1379 			return -1;
   1380 	}
   1381 
   1382 	return 0;
   1383 }
   1384 
   1385 int
   1386 make_access(void)
   1387 {
   1388 	char pathbuf[PATH_MAX], *cmdfmt = "ln -s \"%s\" \"%s\"", *pfmt = "Make access \"%s\"? (y/N) ";
   1389 	size_t cmdsize = PATH_MAX + strlen(cmdfmt), psize = strlen(pfmt) + PATH_MAX;
   1390 
   1391 	if (prompt_confirm(psize, pfmt, curwin.winfiles[curwin.highlight].d_name))
   1392 		run_command(cmdsize, cmdfmt, get_fullpath(pathbuf, &curwin, curwin.highlight), LN_ACCESS_DIR);
   1393 }
   1394 
   1395 void
   1396 exe_selection(SelAction action, const char *askn)
   1397 {
   1398 	char *pfmt = "%s selection (%d files) ? (y/N) ";
   1399 	size_t cmdsize = PATH_MAX*2 + 20;
   1400 
   1401 	if (selected == NULL || selc == 0)
   1402 		return;
   1403 
   1404 	if (askn != NULL && !prompt_confirm(strlen(pfmt) + strlen(askn) + 5, pfmt, askn, selc))
   1405 		return;
   1406 
   1407 	switch (action) {
   1408 		case COPY:
   1409 			for (size_t i = 0; i <selc; i++)
   1410 				run_command(cmdsize, "cp -rf \"%s\" \"%s\"", selected[i], curwin.path);
   1411 			break;
   1412 		case REMOVE:
   1413 			for (size_t i = 0; i <selc; i++)
   1414 				run_command(cmdsize, "rm -rf \"%s\"", selected[i]);
   1415 			break;
   1416 		case MOVE:
   1417 			for (size_t i = 0; i <selc; i++)
   1418 				run_command(cmdsize, "mv \"%s\" \"%s\"", selected[i], curwin.path);
   1419 			break;
   1420 		default:
   1421 			break;
   1422 	}
   1423 
   1424 	clear_selected();
   1425 	curwin.highlight = 0;
   1426 	set_win_files(&curwin);
   1427 	set_win_files(&parwin);
   1428 	update_child_win();
   1429 }
   1430 
   1431 void
   1432 select_file(const char *path)
   1433 {
   1434 	if (selc == 0 && ((selected = (char**) malloc(sizeof(char *)))) == NULL)
   1435 		fatal("Fatal: failed to malloc selected.\n");
   1436 
   1437 	if (remove_selected(path))
   1438 		return;
   1439 
   1440 	if ((selected = (char**) realloc(selected, (selc+1) * sizeof(char *))) == NULL)
   1441 		fatal("Fatal: failed to realloc.\n");
   1442 
   1443 	selected[selc] = strdup(path);
   1444 	(selc)++;
   1445 }
   1446 
   1447 void
   1448 clear_selected(void)
   1449 {
   1450 	for (size_t i = 0; i < selc; i++)
   1451 		free(selected[i]);
   1452 
   1453 	free(selected);
   1454 	selected = NULL;
   1455 	selc = 0;
   1456 }
   1457 
   1458 void
   1459 clear_search(void)
   1460 {
   1461 	for (size_t i = 0; i < SEARCHLEN; i++)
   1462 		searchq[i] = '\0';
   1463 	searchc = 0;
   1464 }
   1465 
   1466 char *
   1467 cpstr(char *dest, const char *src)
   1468 {
   1469 	size_t cplen = strlen(src);
   1470 	memcpy(dest, src, cplen);
   1471 	dest[cplen] = '\0';
   1472 	return dest;
   1473 }
   1474 
   1475 char *
   1476 get_fullpath(char *buf, DirWin *dirwin, size_t index)
   1477 {
   1478 	index = MIN(index, dirwin->filecount-1);
   1479 	index = MAX(index, 0);
   1480 
   1481 	if (strcmp(dirwin->path, "/") == 0)
   1482 		snprintf(buf, PATH_MAX, "/%s", dirwin->winfiles[index].d_name);
   1483 	else
   1484 		snprintf(buf, PATH_MAX, "%s/%s", dirwin->path, dirwin->winfiles[index].d_name);
   1485 
   1486 	return buf;
   1487 }
   1488 
   1489 char *
   1490 replace_home(char *buf, const char *path)
   1491 {
   1492 	char *envv = "$HOME";
   1493 
   1494 	if ((strstr(path, envv) != NULL)) {
   1495 		cpstr(buf, userhome);
   1496 		strcat(buf, path + strlen(envv));
   1497 	}
   1498 	return buf;
   1499 }
   1500 
   1501 char *
   1502 get_dirname(char *buf, const char *path)
   1503 {
   1504 	cpstr(buf, path);
   1505 	dirname(buf);
   1506 	return buf;
   1507 }