cex

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

cex.c (32906B)


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