cex

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

cex.c (31327B)


      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 			mvwaddch(dirwin->window, y, x-1, ' ');
    553 			if (dirwin->winfiles[i].selected) {
    554 				wattron(dirwin->window, COLOR_PAIR(MARK_SELECTED_COLOR));
    555 				mvwaddch(dirwin->window, y, x-1, '|');
    556 				wattroff(dirwin->window, COLOR_PAIR(MARK_SELECTED_COLOR));
    557 			}
    558 			memcpy(name, dirwin->winfiles[i].d_name, size);
    559 			name[size-1] = '\0';
    560 			if (dirwin->usehighlight && dirwin->highlight == i) {
    561 				wattron(dirwin->window, A_REVERSE);
    562 				mvwaddstr(dirwin->window, y, x, name);
    563 				wattroff(dirwin->window, A_REVERSE);
    564 			} else if (dirwin->winfiles[i].d_type == DT_DIR) {
    565 				wattron(dirwin->window, COLOR_PAIR(DIR_COLOR));
    566 				wattron(dirwin->window, A_BOLD);
    567 				mvwaddstr(dirwin->window, y, x, name);
    568 				wattroff(dirwin->window, COLOR_PAIR(DIR_COLOR));
    569 				wattroff(dirwin->window, A_BOLD);
    570 			} else if (dirwin->winfiles[i].d_type == DT_LNK) {
    571 				if (access(get_fullpath(pathbuf, dirwin, i), F_OK) == 0) {
    572 					wattron(dirwin->window, COLOR_PAIR(LN_COLOR));
    573 					mvwaddstr(dirwin->window, y, x, name);
    574 					wattroff(dirwin->window, COLOR_PAIR(LN_COLOR));
    575 				} else {
    576 					wattron(dirwin->window, COLOR_PAIR(INVALID_LN_COLOR));
    577 					mvwaddstr(dirwin->window, y, x, name);
    578 					wattroff(dirwin->window, COLOR_PAIR(INVALID_LN_COLOR));
    579 				}
    580 			} else
    581 				mvwaddstr(dirwin->window, y, x, name);
    582 			if (dirwin->wintype == CURRENT && searchc > 0
    583 					&& ((subs = strstr(name, searchq)) != NULL)) {
    584 				sindex = (int) (subs - name);
    585 				wattron(dirwin->window, COLOR_PAIR(SEARCH_MATCH_COLOR));
    586 				if (dirwin->usehighlight && dirwin->highlight == i)
    587 					wattron(dirwin->window, A_REVERSE);
    588 				cplen = strlen(searchq);
    589 				memcpy(sbuf, name + sindex, cplen);
    590 				sbuf[cplen] = '\0';
    591 				mvwaddstr(dirwin->window, y, x+sindex, sbuf);
    592 				wattroff(dirwin->window, COLOR_PAIR(SEARCH_MATCH_COLOR));
    593 				if (dirwin->usehighlight && dirwin->highlight == i)
    594 					wattroff(dirwin->window, A_REVERSE);
    595 			}
    596 			y++;
    597 		}
    598 	}
    599 	wrefresh(dirwin->window);
    600 }
    601 
    602 void
    603 update_child_win(void)
    604 {
    605 	char pathbuf[PATH_MAX];
    606 
    607 	if (curwin.filecount <= 0) {
    608 		free_dirwin(&childwin);
    609 		return;
    610 	}
    611 
    612 	get_fullpath(childwin.path, &curwin, curwin.highlight);
    613 
    614 	switch (curwin.winfiles[curwin.highlight].d_type) {
    615 		case DT_LNK:
    616 			if (access(get_fullpath(pathbuf, &curwin, curwin.highlight), F_OK) != 0) {
    617 				set_win_message(&childwin, "Link no longer exists.");
    618 				break;
    619 			}
    620 			if (is_dir(&curwin, curwin.highlight))
    621 				goto dir;
    622 			else
    623 				goto reg;
    624 		case DT_DIR:
    625 	dir:
    626 			set_win_files(&childwin);
    627 			break;
    628 		case DT_REG:
    629 	reg:
    630 			print_content();
    631 			break;
    632 		default:
    633 			free_dirwin(&childwin);
    634 			break;
    635 	}
    636 }
    637 
    638 void
    639 print_top_title(void)
    640 {
    641 	move(0, 0);
    642 	clrtoeol();
    643 	attron(COLOR_PAIR(TOP_TITLE_COLOR));
    644 	printw("%s:%s/%s", username, curwin.path, curwin.winfiles[curwin.highlight].d_name);
    645 	attroff(COLOR_PAIR(TOP_TITLE_COLOR));
    646 }
    647 
    648 void
    649 print_bot_title(void)
    650 {
    651 	char pathbuf[PATH_MAX], fileinf[maxx];
    652 
    653 	get_fullpath(pathbuf, &curwin, curwin.highlight);
    654 	memcpy(fileinf, get_file_info(fileinf, pathbuf), maxx);
    655 	fileinf[maxx] = '\0';
    656 
    657 	move(maxy-1, 0);
    658 	clrtoeol();
    659 
    660 	attron(COLOR_PAIR(BOT_TITLE_COUNT_COLOR));
    661 	printw("(%d/%d) ", curwin.filecount == 0 ? 0 : curwin.highlight+1, curwin.filecount);
    662 	attroff(COLOR_PAIR(BOT_TITLE_COUNT_COLOR));
    663 
    664 	attron(COLOR_PAIR(BOT_TITLE_INFO_COLOR));
    665 	addstr(fileinf);
    666 	attroff(COLOR_PAIR(BOT_TITLE_INFO_COLOR));
    667 }
    668 
    669 char *
    670 get_file_info(char *buf, const char *filepath)
    671 {
    672 	struct stat statbuf;
    673 	struct passwd *usr;
    674 	struct tm *tm;
    675 	struct group *grp;
    676 	char mods[11], datestr[256], modfill = '-';
    677 
    678 	if ((stat(filepath, &statbuf)) != 0) {
    679 		switch (errno) {
    680 			case EACCES:
    681 				buf = "No permission.";
    682 				break;
    683 			default:
    684 				buf = "Retrieving filestats failed.";
    685 				break;
    686 		}
    687 		return buf;
    688 	}
    689 
    690 	if ((usr = getpwuid(statbuf.st_uid)) == NULL) {
    691 		buf = "Retrieving username failed.";
    692 		return buf;
    693 	}
    694 	if ((grp = getgrgid(statbuf.st_gid)) == NULL) {
    695 		buf = "Retrieving groupname failed.";
    696 		return buf;
    697 	}
    698 
    699 	mods[0] = S_ISDIR(statbuf.st_mode) ? 'd' : modfill;
    700 	mods[1] = statbuf.st_mode & S_IRUSR ? 'r' : modfill;
    701 	mods[2] = statbuf.st_mode & S_IWUSR ? 'w' : modfill;
    702 	mods[3] = statbuf.st_mode & S_IXUSR ? 'x' : modfill;
    703 	mods[4] = statbuf.st_mode & S_IRGRP ? 'r' : modfill;
    704 	mods[5] = statbuf.st_mode & S_IWGRP ? 'w' : modfill;
    705 	mods[6] = statbuf.st_mode & S_IXGRP ? 'x' : modfill;
    706 	mods[7] = statbuf.st_mode & S_IROTH ? 'r' : modfill;
    707 	mods[8] = statbuf.st_mode & S_IWOTH ? 'w' : modfill;
    708 	mods[9] = statbuf.st_mode & S_IXOTH ? 'x' : modfill;
    709 	mods[10] = '\0';
    710 
    711 	tm = localtime(&statbuf.st_mtime);
    712 	strftime(datestr, sizeof(datestr), nl_langinfo(D_T_FMT), tm);
    713 
    714 	sprintf(buf, "%10.10s %s %s %9jd %s",
    715 		mods,
    716 		usr->pw_name,
    717 		grp->gr_name,
    718 		(intmax_t)statbuf.st_size,
    719 		datestr);
    720 
    721 	return buf;
    722 }
    723 
    724 void
    725 print_content(void)
    726 {
    727 	FILE *fp = NULL;
    728 	size_t size = childwin.maxx;
    729 	char str[size+1], buf[PATH_MAX];
    730 	int y = 0, x = 0;
    731 
    732 	werase(childwin.window);
    733 	free_dirwin(&childwin);
    734 
    735 	if ((fp = fopen(get_fullpath(buf, &curwin, curwin.highlight), "r")) == NULL) {
    736 		switch (errno) {
    737 			case EACCES:
    738 				set_win_message(&childwin, "No permission.");
    739 				return;
    740 			default:
    741 				fatal("Error opening file \"%s\"\n", buf);
    742 		}
    743 	}
    744 
    745 	childwin.holdupdate = TRUE;
    746 
    747 	while (fgets(str, size, fp) != NULL && y <= childwin.maxy) {
    748 		mvwaddstr(childwin.window, y, x, str);
    749 		y++;
    750 	}
    751 
    752 	fclose(fp);
    753 	wrefresh(childwin.window);
    754 }
    755 
    756 void
    757 show_file_mime(void)
    758 {
    759 	char buf[MIME_MAX];
    760 
    761 	if (get_mime(buf, MIME_MAX, curwin.winfiles[curwin.highlight].d_name) != 0)
    762 		cpstr(buf, "Can't retrieve mime.");
    763 
    764 	move(1, 1);
    765 	clrtoeol();
    766 	addstr(buf);
    767 }
    768 
    769 void
    770 combo_key(int keypress)
    771 {
    772 	int c;
    773 
    774 	move(maxy-1, maxx-1);
    775 	clrtoeol();
    776 	addch(keypress);
    777 
    778 	halfdelay(10);
    779 
    780 	while ((c = getch()) != ERR) {
    781 		switch (keypress) {
    782 			case KEY_COMBO_GO:
    783 				combo_go(c);
    784 				break;
    785 			case KEY_COMBO_INF:
    786 				combo_inf(c);
    787 				break;
    788 			case KEY_COMBO_OPEN:
    789 				combo_open(c);
    790 				break;
    791 			case KEY_COMBO_MAKE:
    792 				combo_make(c);
    793 				break;
    794 			default:
    795 				break;
    796 		}
    797 		break;
    798 	}
    799 
    800 	cbreak();
    801 	move(maxy-1, maxx-1);
    802 	clrtoeol();
    803 }
    804 
    805 void
    806 combo_go(int key)
    807 {
    808 	char pathbuf[PATH_MAX];
    809 
    810 	switch (key) {
    811 		case KEY_COMBO_GO_TOP:
    812 			move_top(&curwin);
    813 			update_child_win();
    814 			break;
    815 		case KEY_COMBO_GO_HOME:
    816 			change_dir(userhome, OTHER, FALSE);
    817 			break;
    818 		case KEY_COMBO_GO_ACCESS:
    819 			change_dir(replace_home(pathbuf, LN_ACCESS_DIR), OTHER, FALSE);
    820 			break;
    821 		default:
    822 			break;
    823 	}
    824 }
    825 
    826 void
    827 combo_inf(int key)
    828 {
    829 	switch (key) {
    830 		case KEY_COMBO_INF_OPEN:
    831 			show_file_mime();
    832 			break;
    833 		default:
    834 			break;
    835 	}
    836 }
    837 
    838 void
    839 combo_open(int key)
    840 {
    841 	char chdname[PATH_MAX];
    842 
    843 	switch (key) {
    844 		case KEY_COMBO_OPEN_NOHUP_XDG:
    845 			if (is_dir(&curwin, curwin.highlight))
    846 				change_dir(get_fullpath(chdname, &curwin, curwin.highlight), RIGHT, FALSE);
    847 			else
    848 				open_nohup_xdg();
    849 			break;
    850 		case KEY_COMBO_OPEN_EXEC:
    851 			if (is_dir(&curwin, curwin.highlight))
    852 				change_dir(get_fullpath(chdname, &curwin, curwin.highlight), RIGHT, FALSE);
    853 			else
    854 				open_child(TRUE);
    855 			break;
    856 		default:
    857 			break;
    858 	}
    859 }
    860 
    861 void
    862 combo_make(int key)
    863 {
    864 	switch (key) {
    865 		case KEY_COMBO_MAKE_FILE:
    866 			make_file(curwin.path);
    867 			set_win_files(&curwin);
    868 			update_child_win();
    869 			break;
    870 		case KEY_COMBO_MAKE_DIR:
    871 			make_dir(curwin.path);
    872 			set_win_files(&curwin);
    873 			update_child_win();
    874 			break;
    875 		case KEY_COMBO_MAKE_MOD:
    876 			make_mod();
    877 			break;
    878 		case KEY_COMBO_MAKE_ACCESS:
    879 			make_access();
    880 			break;
    881 		default:
    882 			break;
    883 	}
    884 }
    885 
    886 void
    887 change_dir(const char *chdname, Direction direction, bool abspath)
    888 {
    889 	if (chdir(chdname) != 0)
    890 		return;
    891 
    892 	if (!abspath)
    893 		cpstr(curwin.path, chdname);
    894 	else if (getcwd(curwin.path, PATH_MAX) == NULL)
    895 		fatal("getcwd() error");
    896 
    897 	switch (direction) {
    898 		case RIGHT:
    899 			curwin.highlight = 0;
    900 			break;
    901 		case LEFT:
    902 			curwin.highlight = parwin.highlight;
    903 			break;
    904 		default:
    905 			curwin.highlight = 0;
    906 			break;
    907 
    908 	}
    909 
    910 	set_win_files(&curwin);
    911 
    912 	get_dirname(parwin.path, curwin.path);
    913 	set_win_files(&parwin);
    914 
    915 	update_child_win();
    916 }
    917 
    918 void
    919 change_parent_dir(Direction direction)
    920 {
    921 	char pp[PATH_MAX];
    922 
    923 	switch (direction) {
    924 		case UP:
    925 			move_up(&parwin);
    926 			break;
    927 		case DOWN:
    928 			move_down(&parwin);
    929 			break;
    930 		default:
    931 			return;
    932 	}
    933 
    934 	parwin.usehighlight = TRUE;
    935 	curwin.highlight = 0;
    936 	change_dir(get_fullpath(pp, &parwin, parwin.highlight), direction, FALSE);
    937 }
    938 
    939 void
    940 open_child(bool exec)
    941 {
    942 	sigset_t set;
    943 	char mime[MIME_MAX], mimedefault[MIME_APP_MAX], pathbuf[PATH_MAX];
    944 	bool istext;
    945 
    946 	if (curwin.message != NULL)
    947 		return;
    948 
    949 	if (!exec) {
    950 		if (get_mime(mime, MIME_MAX, curwin.winfiles[curwin.highlight].d_name) != 0) {
    951 			move(1, 1);
    952 			clrtoeol();
    953 			printw("Can\'t open %s", curwin.winfiles[curwin.highlight].d_name);
    954 			return;
    955 		}
    956 
    957 		istext = strncmp(mime, "text/", 5) == 0;
    958 
    959 		if (!istext && get_mime_default(mimedefault, MIME_APP_MAX, mime) != 0) {
    960 			move(1, 1);
    961 			clrtoeol();
    962 			printw("Can\'t open for mime %s", mime);
    963 			return;
    964 		}
    965 	}
    966 
    967 	endwin();
    968 
    969 	if (sigemptyset (&set) == -1)
    970 		fatal("Sigemptyset failed.");
    971 	if (sigaddset(&set, SIGWINCH) == -1)
    972 		fatal("Sigaddset failed.");
    973 	if (sigprocmask(SIG_BLOCK, &set, NULL) != 0)
    974 		fatal("Blocking sigprocmask failed.");
    975 
    976 	if (exec)
    977 		run_command(PATH_MAX + 4, "\"./%s\"", curwin.winfiles[curwin.highlight].d_name);
    978 	else
    979 		run_command(PATH_MAX + 12, "%s \"%s\"", istext ? editor : "xdg-open",
    980 			get_fullpath(pathbuf, &curwin, curwin.highlight));
    981 
    982 	if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0)
    983 		fatal("Unblocking sigprocmask failed.");
    984 
    985 	clear();
    986 	noecho();
    987 	cbreak();
    988 
    989 	refresh();
    990 	wrefresh(parwin.window);
    991 	wrefresh(curwin.window);
    992 	wrefresh(childwin.window);
    993 	update_child_win();
    994 }
    995 
    996 void
    997 open_nohup_xdg(void)
    998 {
    999 	char *cmdfmt = "nohup xdg-open \"%s\" > /dev/null 2>&1 &";
   1000 	size_t size = PATH_MAX + strlen(cmdfmt);
   1001 
   1002 	run_command(size, cmdfmt, curwin.winfiles[curwin.highlight].d_name);
   1003 }
   1004 
   1005 void
   1006 search(void)
   1007 {
   1008 	int c, y, x;
   1009 
   1010 	clear_search();
   1011 
   1012 	move(maxy-1, 0);
   1013 	clrtoeol();
   1014 
   1015 	attron(COLOR_PAIR(PROMPT_COLOR));
   1016 	addstr("Search name? ");
   1017 	attroff(COLOR_PAIR(PROMPT_COLOR));
   1018 
   1019 	echo();
   1020 	curs_set(1);
   1021 
   1022 	getyx(stdscr, y, x);
   1023 
   1024 	while ((c = mvwgetch(stdscr, y, x)) != 10) {
   1025 		switch (c) {
   1026 			case KEY_BACKSPACE:
   1027 				if (searchc > 0) {
   1028 					searchq[--(searchc)] = '\0';
   1029 					move(y, --x);
   1030 					clrtoeol();
   1031 				}
   1032 				break;
   1033 			default:
   1034 				if (searchc < SEARCHLEN) {
   1035 					searchq[(searchc)++] = c;
   1036 					move(y, ++x);
   1037 				}
   1038 				break;
   1039 		}
   1040 		print_win(&curwin);
   1041 	}
   1042 	noecho();
   1043 	curs_set(0);
   1044 }
   1045 
   1046 void
   1047 move_top(DirWin *dirwin)
   1048 {
   1049 	dirwin->highlight = 0;
   1050 }
   1051 
   1052 void
   1053 move_bot(DirWin *dirwin)
   1054 {
   1055 	dirwin->highlight = MAX(dirwin->filecount-1, 0);
   1056 }
   1057 
   1058 void
   1059 move_page_up(DirWin *dirwin)
   1060 {
   1061 	dirwin->highlight = MAX(dirwin->highlight - dirwin->maxy/2, 0);
   1062 }
   1063 
   1064 void
   1065 move_page_down(DirWin *dirwin)
   1066 {
   1067 	dirwin->highlight = MIN(dirwin->highlight + dirwin->maxy/2,
   1068 		MAX(dirwin->filecount-1, 0));
   1069 }
   1070 
   1071 void
   1072 move_up(DirWin *dirwin)
   1073 {
   1074 	if (dirwin->highlight > 0)
   1075 		dirwin->highlight--;
   1076 }
   1077 
   1078 void
   1079 move_down(DirWin *dirwin)
   1080 {
   1081 	if (dirwin->highlight < dirwin->filecount-1)
   1082 		dirwin->highlight++;
   1083 }
   1084 
   1085 void
   1086 next_search(void)
   1087 {
   1088 	if (searchc == 0)
   1089 		return;
   1090 
   1091 	for (size_t i = curwin.highlight; i < MAX(curwin.filecount-1, 0); i++) {
   1092 		if ((strstr(curwin.winfiles[i+1].d_name, searchq) != NULL)) {
   1093 			curwin.highlight = i+1;
   1094 			return;
   1095 		}
   1096 	}
   1097 }
   1098 
   1099 void
   1100 prev_search(void)
   1101 {
   1102 	if (searchc == 0)
   1103 		return;
   1104 
   1105 	for (size_t i = curwin.highlight; i > 0; i--) {
   1106 		if ((strstr(curwin.winfiles[i-1].d_name, searchq) != NULL)) {
   1107 			curwin.highlight = i-1;
   1108 			return;
   1109 		}
   1110 	}
   1111 }
   1112 
   1113 void
   1114 free_dirwin(DirWin *dirwin)
   1115 {
   1116 	free(dirwin->winfiles);
   1117 	dirwin->filecount = 0;
   1118 	dirwin->winfiles = NULL;
   1119 	dirwin->message = NULL;
   1120 }
   1121 
   1122 void
   1123 clean(void)
   1124 {
   1125 	wclear(curwin.window);
   1126 	wclear(parwin.window);
   1127 	wclear(childwin.window);
   1128 
   1129 	free(username);
   1130 	free(userhome);
   1131 
   1132 	clear();
   1133 
   1134 	delwin(parwin.window);
   1135 	delwin(curwin.window);
   1136 	delwin(childwin.window);
   1137 	delwin(stdscr);
   1138 
   1139 	free_dirwin(&curwin);
   1140 	free_dirwin(&parwin);
   1141 	free_dirwin(&childwin);
   1142 
   1143 	clear_selected();
   1144 
   1145 	endwin();
   1146 }
   1147 
   1148 void
   1149 fatal(const char *fmt, ...)
   1150 {
   1151 	va_list ap;
   1152 
   1153 	va_start(ap, fmt);
   1154 	vfprintf(stderr, fmt, ap);
   1155 	va_end(ap);
   1156 
   1157 	clean();
   1158 
   1159 	exit(EXIT_FAILURE);
   1160 }
   1161 
   1162 bool
   1163 is_dir(DirWin *dirwin, size_t index)
   1164 {
   1165 	struct stat statbuf;
   1166 	char linkpath[PATH_MAX], abspath[PATH_MAX];
   1167 
   1168 	if (dirwin->filecount <= 0)
   1169 		return FALSE;
   1170 
   1171 	if (dirwin->winfiles[index].d_type == DT_DIR)
   1172 		return TRUE;
   1173 
   1174 	get_fullpath(linkpath, dirwin, index);
   1175 
   1176 	if (dirwin->winfiles[index].d_type == DT_LNK)
   1177 		return (lstat(realpath(linkpath, abspath), &statbuf) >= 0) && S_ISDIR(statbuf.st_mode);
   1178 
   1179 	return FALSE;
   1180 }
   1181 
   1182 char *
   1183 prompt_answer(char *buf, size_t size, const char *question)
   1184 {
   1185 	move(maxy-1, 0);
   1186 	clrtoeol();
   1187 
   1188 	attron(COLOR_PAIR(PROMPT_COLOR));
   1189 	addstr(question);
   1190 	attroff(COLOR_PAIR(PROMPT_COLOR));
   1191 
   1192 	echo();
   1193 	curs_set(1);
   1194 	getnstr(buf, size);
   1195 	noecho();
   1196 	curs_set(0);
   1197 
   1198 	return buf;
   1199 }
   1200 
   1201 bool
   1202 prompt_confirm(size_t size, const char *fmt, ...)
   1203 {
   1204 	char response[1], question[size];
   1205 	va_list ap;
   1206 
   1207 	va_start(ap, fmt);
   1208 	vsnprintf(question, size, fmt, ap);
   1209 	va_end(ap);
   1210 
   1211 	prompt_answer(response, 1, question);
   1212 
   1213 	return strcasecmp(response, "y") == 0;
   1214 }
   1215 
   1216 int
   1217 compare_file(const void *a, const void *b)
   1218 {
   1219 	int typecompare;
   1220 	const WinFile *ma = a;
   1221 	const WinFile *mb = b;
   1222 
   1223 	if ((typecompare = ma->d_type - mb->d_type) == 0)
   1224 		return strcmp(ma->d_name, mb->d_name);
   1225 
   1226 	return typecompare;
   1227 }
   1228 
   1229 int
   1230 get_mime(char *buf, size_t bufsize, const char *path)
   1231 {
   1232 	char *cmdfmt = "xdg-mime query filetype \"%s\"";
   1233 	size_t size = PATH_MAX + strlen(cmdfmt);
   1234 	char cmd[size];
   1235 
   1236 	snprintf(cmd, size, cmdfmt, path);
   1237 
   1238 	return read_command(buf, bufsize, cmd);
   1239 }
   1240 
   1241 int
   1242 get_mime_default(char *buf, size_t bufsize, const char *mime)
   1243 {
   1244 	char *cmdfmt = "xdg-mime query default \"%s\"";
   1245 	size_t cmdsize = MIME_MAX + strlen(cmdfmt);
   1246 	char cmd[cmdsize];
   1247 
   1248 	snprintf(cmd, cmdsize, cmdfmt, mime);
   1249 
   1250 	return read_command(buf, bufsize, cmd);
   1251 }
   1252 
   1253 int
   1254 read_command(char *buf, size_t bufsize, const char *cmd)
   1255 {
   1256 	FILE *file;
   1257 
   1258 	if ((file = popen(cmd, "r")) == NULL) {
   1259 		buf = NULL;
   1260 		return 1;
   1261 	}
   1262 
   1263 	if (fgets(buf, bufsize, file) == NULL) {
   1264 		buf = NULL;
   1265 		pclose(file);
   1266 		return 2;
   1267 	}
   1268 
   1269 	buf[strlen(buf)-1] = '\0';
   1270 
   1271 	pclose(file);
   1272 
   1273 	return 0;
   1274 }
   1275 
   1276 void
   1277 run_command(size_t size, const char *fmt, ...)
   1278 {
   1279 	va_list ap;
   1280 	char cmd[size];
   1281 
   1282 	va_start(ap, fmt);
   1283 	vsnprintf(cmd, size, fmt, ap);
   1284 	va_end(ap);
   1285 
   1286 	system(cmd);
   1287 }
   1288 
   1289 int
   1290 make_dir(const char *path)
   1291 {
   1292 	char name[PATH_MAX];
   1293 
   1294 	prompt_answer(name, PATH_MAX, "Name of directory? ");
   1295 	return mkdir(name, 0755);
   1296 }
   1297 
   1298 int
   1299 make_file(const char *path)
   1300 {
   1301 	FILE *fptr;
   1302 	char name[PATH_MAX];
   1303 
   1304 	prompt_answer(name, PATH_MAX, "Name of file? ");
   1305 
   1306 	if (strlen(name) > 0) {
   1307 		if ((fptr = fopen(name, "w")) == NULL)
   1308 			fatal("Error opening file \"%s\"\n", name);
   1309 		fclose(fptr);
   1310 	}
   1311 }
   1312 
   1313 int
   1314 rm_file(const char *fname)
   1315 {
   1316 	char *msg, *rmdirfmt = "rm -rf \"%s\"", *pfmt = "Remove %s? (y/N) ";
   1317 	int res = 0;
   1318 	size_t cmdsize = PATH_MAX + strlen(rmdirfmt), psize = strlen(pfmt) + strlen(fname);
   1319 
   1320 	if (prompt_confirm(psize, pfmt, fname)) {
   1321 		if (is_dir(&curwin, curwin.highlight))
   1322 			run_command(cmdsize, rmdirfmt, fname);
   1323 		else
   1324 			res = remove(fname);
   1325 		curwin.highlight = MAX(curwin.highlight-1, 0);
   1326 	}
   1327 
   1328 	if (res != 0) {
   1329 		switch (errno) {
   1330 			case EACCES:
   1331 				msg = "No permission.";
   1332 				break;
   1333 			case EEXIST:
   1334 			case ENOTEMPTY:
   1335 				msg = "Directory is not empty.";
   1336 				break;
   1337 			default:
   1338 				msg = "Could not remove file.";
   1339 				break;
   1340 		}
   1341 		move(maxy-1, 0);
   1342 		clrtoeol();
   1343 		addstr(msg);
   1344 		print_bot = FALSE;
   1345 	}
   1346 
   1347 	return res;
   1348 }
   1349 
   1350 int
   1351 rename_file(const char *fname)
   1352 {
   1353 	char name[PATH_MAX];
   1354 
   1355 	prompt_answer(name, PATH_MAX, "New name? ");
   1356 
   1357 	if (strlen(name) > 0)
   1358 		return rename(fname, name);
   1359 
   1360 	return 0;
   1361 }
   1362 
   1363 int
   1364 make_mod(void)
   1365 {
   1366 	char name[3];
   1367 	bool isvalid;
   1368 
   1369 	prompt_answer(name, 3, "File mods (numeric)? ");
   1370 
   1371 	if (strlen(name) == 3) {
   1372 		isvalid = TRUE;
   1373 		for (size_t i = 0; i < 3; i++) {
   1374 			if (!isdigit(name[i]))
   1375 				isvalid = FALSE;
   1376 		}
   1377 		if (isvalid)
   1378 			chmod(curwin.winfiles[curwin.highlight].d_name, strtol(name, NULL, 8));
   1379 		else
   1380 			return -1;
   1381 	}
   1382 
   1383 	return 0;
   1384 }
   1385 
   1386 int
   1387 make_access(void)
   1388 {
   1389 	char pathbuf[PATH_MAX], *cmdfmt = "ln -s \"%s\" \"%s\"", *pfmt = "Make access \"%s\"? (y/N) ";
   1390 	size_t cmdsize = PATH_MAX + strlen(cmdfmt), psize = strlen(pfmt) + PATH_MAX;
   1391 
   1392 	if (prompt_confirm(psize, pfmt, curwin.winfiles[curwin.highlight].d_name))
   1393 		run_command(cmdsize, cmdfmt, get_fullpath(pathbuf, &curwin, curwin.highlight), LN_ACCESS_DIR);
   1394 }
   1395 
   1396 void
   1397 exe_selection(SelAction action, const char *askn)
   1398 {
   1399 	char *pfmt = "%s selection (%d files) ? (y/N) ";
   1400 	size_t cmdsize = PATH_MAX*2 + 20;
   1401 
   1402 	if (selected == NULL || selc == 0)
   1403 		return;
   1404 
   1405 	if (askn != NULL && !prompt_confirm(strlen(pfmt) + strlen(askn) + 5, pfmt, askn, selc))
   1406 		return;
   1407 
   1408 	switch (action) {
   1409 		case COPY:
   1410 			for (size_t i = 0; i <selc; i++)
   1411 				run_command(cmdsize, "cp -rf \"%s\" \"%s\"", selected[i], curwin.path);
   1412 			break;
   1413 		case REMOVE:
   1414 			for (size_t i = 0; i <selc; i++)
   1415 				run_command(cmdsize, "rm -rf \"%s\"", selected[i]);
   1416 			break;
   1417 		case MOVE:
   1418 			for (size_t i = 0; i <selc; i++)
   1419 				run_command(cmdsize, "mv \"%s\" \"%s\"", selected[i], curwin.path);
   1420 			break;
   1421 		default:
   1422 			break;
   1423 	}
   1424 
   1425 	clear_selected();
   1426 	curwin.highlight = 0;
   1427 	set_win_files(&curwin);
   1428 	set_win_files(&parwin);
   1429 	update_child_win();
   1430 }
   1431 
   1432 void
   1433 select_file(const char *path)
   1434 {
   1435 	if (selc == 0 && ((selected = (char**) malloc(sizeof(char *)))) == NULL)
   1436 		fatal("Fatal: failed to malloc selected.\n");
   1437 
   1438 	if (remove_selected(path))
   1439 		return;
   1440 
   1441 	if ((selected = (char**) realloc(selected, (selc+1) * sizeof(char *))) == NULL)
   1442 		fatal("Fatal: failed to realloc.\n");
   1443 
   1444 	selected[selc] = strdup(path);
   1445 	(selc)++;
   1446 }
   1447 
   1448 void
   1449 clear_selected(void)
   1450 {
   1451 	for (size_t i = 0; i < selc; i++)
   1452 		free(selected[i]);
   1453 
   1454 	free(selected);
   1455 	selected = NULL;
   1456 	selc = 0;
   1457 }
   1458 
   1459 void
   1460 clear_search(void)
   1461 {
   1462 	for (size_t i = 0; i < SEARCHLEN; i++)
   1463 		searchq[i] = '\0';
   1464 	searchc = 0;
   1465 }
   1466 
   1467 char *
   1468 cpstr(char *dest, const char *src)
   1469 {
   1470 	size_t cplen = strlen(src);
   1471 	memcpy(dest, src, cplen);
   1472 	dest[cplen] = '\0';
   1473 	return dest;
   1474 }
   1475 
   1476 char *
   1477 get_fullpath(char *buf, DirWin *dirwin, size_t index)
   1478 {
   1479 	index = MIN(index, dirwin->filecount-1);
   1480 	index = MAX(index, 0);
   1481 
   1482 	if (strcmp(dirwin->path, "/") == 0)
   1483 		snprintf(buf, PATH_MAX, "/%s", dirwin->winfiles[index].d_name);
   1484 	else
   1485 		snprintf(buf, PATH_MAX, "%s/%s", dirwin->path, dirwin->winfiles[index].d_name);
   1486 
   1487 	return buf;
   1488 }
   1489 
   1490 char *
   1491 replace_home(char *buf, const char *path)
   1492 {
   1493 	char *envv = "$HOME";
   1494 
   1495 	if ((strstr(path, envv) != NULL)) {
   1496 		cpstr(buf, userhome);
   1497 		strcat(buf, path + strlen(envv));
   1498 	}
   1499 	return buf;
   1500 }
   1501 
   1502 char *
   1503 get_dirname(char *buf, const char *path)
   1504 {
   1505 	cpstr(buf, path);
   1506 	dirname(buf);
   1507 	return buf;
   1508 }