cex

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

cex.c (36650B)


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