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