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