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