cex

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

cex.c (32957B)


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