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