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