cex

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

cex.c (35753B)


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