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