cex.c (31327B)
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 mvwaddch(dirwin->window, y, x-1, ' '); 553 if (dirwin->winfiles[i].selected) { 554 wattron(dirwin->window, COLOR_PAIR(MARK_SELECTED_COLOR)); 555 mvwaddch(dirwin->window, y, x-1, '|'); 556 wattroff(dirwin->window, COLOR_PAIR(MARK_SELECTED_COLOR)); 557 } 558 memcpy(name, dirwin->winfiles[i].d_name, size); 559 name[size-1] = '\0'; 560 if (dirwin->usehighlight && dirwin->highlight == i) { 561 wattron(dirwin->window, A_REVERSE); 562 mvwaddstr(dirwin->window, y, x, name); 563 wattroff(dirwin->window, A_REVERSE); 564 } else if (dirwin->winfiles[i].d_type == DT_DIR) { 565 wattron(dirwin->window, COLOR_PAIR(DIR_COLOR)); 566 wattron(dirwin->window, A_BOLD); 567 mvwaddstr(dirwin->window, y, x, name); 568 wattroff(dirwin->window, COLOR_PAIR(DIR_COLOR)); 569 wattroff(dirwin->window, A_BOLD); 570 } else if (dirwin->winfiles[i].d_type == DT_LNK) { 571 if (access(get_fullpath(pathbuf, dirwin, i), F_OK) == 0) { 572 wattron(dirwin->window, COLOR_PAIR(LN_COLOR)); 573 mvwaddstr(dirwin->window, y, x, name); 574 wattroff(dirwin->window, COLOR_PAIR(LN_COLOR)); 575 } else { 576 wattron(dirwin->window, COLOR_PAIR(INVALID_LN_COLOR)); 577 mvwaddstr(dirwin->window, y, x, name); 578 wattroff(dirwin->window, COLOR_PAIR(INVALID_LN_COLOR)); 579 } 580 } else 581 mvwaddstr(dirwin->window, y, x, name); 582 if (dirwin->wintype == CURRENT && searchc > 0 583 && ((subs = strstr(name, searchq)) != NULL)) { 584 sindex = (int) (subs - name); 585 wattron(dirwin->window, COLOR_PAIR(SEARCH_MATCH_COLOR)); 586 if (dirwin->usehighlight && dirwin->highlight == i) 587 wattron(dirwin->window, A_REVERSE); 588 cplen = strlen(searchq); 589 memcpy(sbuf, name + sindex, cplen); 590 sbuf[cplen] = '\0'; 591 mvwaddstr(dirwin->window, y, x+sindex, sbuf); 592 wattroff(dirwin->window, COLOR_PAIR(SEARCH_MATCH_COLOR)); 593 if (dirwin->usehighlight && dirwin->highlight == i) 594 wattroff(dirwin->window, A_REVERSE); 595 } 596 y++; 597 } 598 } 599 wrefresh(dirwin->window); 600 } 601 602 void 603 update_child_win(void) 604 { 605 char pathbuf[PATH_MAX]; 606 607 if (curwin.filecount <= 0) { 608 free_dirwin(&childwin); 609 return; 610 } 611 612 get_fullpath(childwin.path, &curwin, curwin.highlight); 613 614 switch (curwin.winfiles[curwin.highlight].d_type) { 615 case DT_LNK: 616 if (access(get_fullpath(pathbuf, &curwin, curwin.highlight), F_OK) != 0) { 617 set_win_message(&childwin, "Link no longer exists."); 618 break; 619 } 620 if (is_dir(&curwin, curwin.highlight)) 621 goto dir; 622 else 623 goto reg; 624 case DT_DIR: 625 dir: 626 set_win_files(&childwin); 627 break; 628 case DT_REG: 629 reg: 630 print_content(); 631 break; 632 default: 633 free_dirwin(&childwin); 634 break; 635 } 636 } 637 638 void 639 print_top_title(void) 640 { 641 move(0, 0); 642 clrtoeol(); 643 attron(COLOR_PAIR(TOP_TITLE_COLOR)); 644 printw("%s:%s/%s", username, curwin.path, curwin.winfiles[curwin.highlight].d_name); 645 attroff(COLOR_PAIR(TOP_TITLE_COLOR)); 646 } 647 648 void 649 print_bot_title(void) 650 { 651 char pathbuf[PATH_MAX], fileinf[maxx]; 652 653 get_fullpath(pathbuf, &curwin, curwin.highlight); 654 memcpy(fileinf, get_file_info(fileinf, pathbuf), maxx); 655 fileinf[maxx] = '\0'; 656 657 move(maxy-1, 0); 658 clrtoeol(); 659 660 attron(COLOR_PAIR(BOT_TITLE_COUNT_COLOR)); 661 printw("(%d/%d) ", curwin.filecount == 0 ? 0 : curwin.highlight+1, curwin.filecount); 662 attroff(COLOR_PAIR(BOT_TITLE_COUNT_COLOR)); 663 664 attron(COLOR_PAIR(BOT_TITLE_INFO_COLOR)); 665 addstr(fileinf); 666 attroff(COLOR_PAIR(BOT_TITLE_INFO_COLOR)); 667 } 668 669 char * 670 get_file_info(char *buf, const char *filepath) 671 { 672 struct stat statbuf; 673 struct passwd *usr; 674 struct tm *tm; 675 struct group *grp; 676 char mods[11], datestr[256], modfill = '-'; 677 678 if ((stat(filepath, &statbuf)) != 0) { 679 switch (errno) { 680 case EACCES: 681 buf = "No permission."; 682 break; 683 default: 684 buf = "Retrieving filestats failed."; 685 break; 686 } 687 return buf; 688 } 689 690 if ((usr = getpwuid(statbuf.st_uid)) == NULL) { 691 buf = "Retrieving username failed."; 692 return buf; 693 } 694 if ((grp = getgrgid(statbuf.st_gid)) == NULL) { 695 buf = "Retrieving groupname failed."; 696 return buf; 697 } 698 699 mods[0] = S_ISDIR(statbuf.st_mode) ? 'd' : modfill; 700 mods[1] = statbuf.st_mode & S_IRUSR ? 'r' : modfill; 701 mods[2] = statbuf.st_mode & S_IWUSR ? 'w' : modfill; 702 mods[3] = statbuf.st_mode & S_IXUSR ? 'x' : modfill; 703 mods[4] = statbuf.st_mode & S_IRGRP ? 'r' : modfill; 704 mods[5] = statbuf.st_mode & S_IWGRP ? 'w' : modfill; 705 mods[6] = statbuf.st_mode & S_IXGRP ? 'x' : modfill; 706 mods[7] = statbuf.st_mode & S_IROTH ? 'r' : modfill; 707 mods[8] = statbuf.st_mode & S_IWOTH ? 'w' : modfill; 708 mods[9] = statbuf.st_mode & S_IXOTH ? 'x' : modfill; 709 mods[10] = '\0'; 710 711 tm = localtime(&statbuf.st_mtime); 712 strftime(datestr, sizeof(datestr), nl_langinfo(D_T_FMT), tm); 713 714 sprintf(buf, "%10.10s %s %s %9jd %s", 715 mods, 716 usr->pw_name, 717 grp->gr_name, 718 (intmax_t)statbuf.st_size, 719 datestr); 720 721 return buf; 722 } 723 724 void 725 print_content(void) 726 { 727 FILE *fp = NULL; 728 size_t size = childwin.maxx; 729 char str[size+1], buf[PATH_MAX]; 730 int y = 0, x = 0; 731 732 werase(childwin.window); 733 free_dirwin(&childwin); 734 735 if ((fp = fopen(get_fullpath(buf, &curwin, curwin.highlight), "r")) == NULL) { 736 switch (errno) { 737 case EACCES: 738 set_win_message(&childwin, "No permission."); 739 return; 740 default: 741 fatal("Error opening file \"%s\"\n", buf); 742 } 743 } 744 745 childwin.holdupdate = TRUE; 746 747 while (fgets(str, size, fp) != NULL && y <= childwin.maxy) { 748 mvwaddstr(childwin.window, y, x, str); 749 y++; 750 } 751 752 fclose(fp); 753 wrefresh(childwin.window); 754 } 755 756 void 757 show_file_mime(void) 758 { 759 char buf[MIME_MAX]; 760 761 if (get_mime(buf, MIME_MAX, curwin.winfiles[curwin.highlight].d_name) != 0) 762 cpstr(buf, "Can't retrieve mime."); 763 764 move(1, 1); 765 clrtoeol(); 766 addstr(buf); 767 } 768 769 void 770 combo_key(int keypress) 771 { 772 int c; 773 774 move(maxy-1, maxx-1); 775 clrtoeol(); 776 addch(keypress); 777 778 halfdelay(10); 779 780 while ((c = getch()) != ERR) { 781 switch (keypress) { 782 case KEY_COMBO_GO: 783 combo_go(c); 784 break; 785 case KEY_COMBO_INF: 786 combo_inf(c); 787 break; 788 case KEY_COMBO_OPEN: 789 combo_open(c); 790 break; 791 case KEY_COMBO_MAKE: 792 combo_make(c); 793 break; 794 default: 795 break; 796 } 797 break; 798 } 799 800 cbreak(); 801 move(maxy-1, maxx-1); 802 clrtoeol(); 803 } 804 805 void 806 combo_go(int key) 807 { 808 char pathbuf[PATH_MAX]; 809 810 switch (key) { 811 case KEY_COMBO_GO_TOP: 812 move_top(&curwin); 813 update_child_win(); 814 break; 815 case KEY_COMBO_GO_HOME: 816 change_dir(userhome, OTHER, FALSE); 817 break; 818 case KEY_COMBO_GO_ACCESS: 819 change_dir(replace_home(pathbuf, LN_ACCESS_DIR), OTHER, FALSE); 820 break; 821 default: 822 break; 823 } 824 } 825 826 void 827 combo_inf(int key) 828 { 829 switch (key) { 830 case KEY_COMBO_INF_OPEN: 831 show_file_mime(); 832 break; 833 default: 834 break; 835 } 836 } 837 838 void 839 combo_open(int key) 840 { 841 char chdname[PATH_MAX]; 842 843 switch (key) { 844 case KEY_COMBO_OPEN_NOHUP_XDG: 845 if (is_dir(&curwin, curwin.highlight)) 846 change_dir(get_fullpath(chdname, &curwin, curwin.highlight), RIGHT, FALSE); 847 else 848 open_nohup_xdg(); 849 break; 850 case KEY_COMBO_OPEN_EXEC: 851 if (is_dir(&curwin, curwin.highlight)) 852 change_dir(get_fullpath(chdname, &curwin, curwin.highlight), RIGHT, FALSE); 853 else 854 open_child(TRUE); 855 break; 856 default: 857 break; 858 } 859 } 860 861 void 862 combo_make(int key) 863 { 864 switch (key) { 865 case KEY_COMBO_MAKE_FILE: 866 make_file(curwin.path); 867 set_win_files(&curwin); 868 update_child_win(); 869 break; 870 case KEY_COMBO_MAKE_DIR: 871 make_dir(curwin.path); 872 set_win_files(&curwin); 873 update_child_win(); 874 break; 875 case KEY_COMBO_MAKE_MOD: 876 make_mod(); 877 break; 878 case KEY_COMBO_MAKE_ACCESS: 879 make_access(); 880 break; 881 default: 882 break; 883 } 884 } 885 886 void 887 change_dir(const char *chdname, Direction direction, bool abspath) 888 { 889 if (chdir(chdname) != 0) 890 return; 891 892 if (!abspath) 893 cpstr(curwin.path, chdname); 894 else if (getcwd(curwin.path, PATH_MAX) == NULL) 895 fatal("getcwd() error"); 896 897 switch (direction) { 898 case RIGHT: 899 curwin.highlight = 0; 900 break; 901 case LEFT: 902 curwin.highlight = parwin.highlight; 903 break; 904 default: 905 curwin.highlight = 0; 906 break; 907 908 } 909 910 set_win_files(&curwin); 911 912 get_dirname(parwin.path, curwin.path); 913 set_win_files(&parwin); 914 915 update_child_win(); 916 } 917 918 void 919 change_parent_dir(Direction direction) 920 { 921 char pp[PATH_MAX]; 922 923 switch (direction) { 924 case UP: 925 move_up(&parwin); 926 break; 927 case DOWN: 928 move_down(&parwin); 929 break; 930 default: 931 return; 932 } 933 934 parwin.usehighlight = TRUE; 935 curwin.highlight = 0; 936 change_dir(get_fullpath(pp, &parwin, parwin.highlight), direction, FALSE); 937 } 938 939 void 940 open_child(bool exec) 941 { 942 sigset_t set; 943 char mime[MIME_MAX], mimedefault[MIME_APP_MAX], pathbuf[PATH_MAX]; 944 bool istext; 945 946 if (curwin.message != NULL) 947 return; 948 949 if (!exec) { 950 if (get_mime(mime, MIME_MAX, curwin.winfiles[curwin.highlight].d_name) != 0) { 951 move(1, 1); 952 clrtoeol(); 953 printw("Can\'t open %s", curwin.winfiles[curwin.highlight].d_name); 954 return; 955 } 956 957 istext = strncmp(mime, "text/", 5) == 0; 958 959 if (!istext && get_mime_default(mimedefault, MIME_APP_MAX, mime) != 0) { 960 move(1, 1); 961 clrtoeol(); 962 printw("Can\'t open for mime %s", mime); 963 return; 964 } 965 } 966 967 endwin(); 968 969 if (sigemptyset (&set) == -1) 970 fatal("Sigemptyset failed."); 971 if (sigaddset(&set, SIGWINCH) == -1) 972 fatal("Sigaddset failed."); 973 if (sigprocmask(SIG_BLOCK, &set, NULL) != 0) 974 fatal("Blocking sigprocmask failed."); 975 976 if (exec) 977 run_command(PATH_MAX + 4, "\"./%s\"", curwin.winfiles[curwin.highlight].d_name); 978 else 979 run_command(PATH_MAX + 12, "%s \"%s\"", istext ? editor : "xdg-open", 980 get_fullpath(pathbuf, &curwin, curwin.highlight)); 981 982 if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) 983 fatal("Unblocking sigprocmask failed."); 984 985 clear(); 986 noecho(); 987 cbreak(); 988 989 refresh(); 990 wrefresh(parwin.window); 991 wrefresh(curwin.window); 992 wrefresh(childwin.window); 993 update_child_win(); 994 } 995 996 void 997 open_nohup_xdg(void) 998 { 999 char *cmdfmt = "nohup xdg-open \"%s\" > /dev/null 2>&1 &"; 1000 size_t size = PATH_MAX + strlen(cmdfmt); 1001 1002 run_command(size, cmdfmt, curwin.winfiles[curwin.highlight].d_name); 1003 } 1004 1005 void 1006 search(void) 1007 { 1008 int c, y, x; 1009 1010 clear_search(); 1011 1012 move(maxy-1, 0); 1013 clrtoeol(); 1014 1015 attron(COLOR_PAIR(PROMPT_COLOR)); 1016 addstr("Search name? "); 1017 attroff(COLOR_PAIR(PROMPT_COLOR)); 1018 1019 echo(); 1020 curs_set(1); 1021 1022 getyx(stdscr, y, x); 1023 1024 while ((c = mvwgetch(stdscr, y, x)) != 10) { 1025 switch (c) { 1026 case KEY_BACKSPACE: 1027 if (searchc > 0) { 1028 searchq[--(searchc)] = '\0'; 1029 move(y, --x); 1030 clrtoeol(); 1031 } 1032 break; 1033 default: 1034 if (searchc < SEARCHLEN) { 1035 searchq[(searchc)++] = c; 1036 move(y, ++x); 1037 } 1038 break; 1039 } 1040 print_win(&curwin); 1041 } 1042 noecho(); 1043 curs_set(0); 1044 } 1045 1046 void 1047 move_top(DirWin *dirwin) 1048 { 1049 dirwin->highlight = 0; 1050 } 1051 1052 void 1053 move_bot(DirWin *dirwin) 1054 { 1055 dirwin->highlight = MAX(dirwin->filecount-1, 0); 1056 } 1057 1058 void 1059 move_page_up(DirWin *dirwin) 1060 { 1061 dirwin->highlight = MAX(dirwin->highlight - dirwin->maxy/2, 0); 1062 } 1063 1064 void 1065 move_page_down(DirWin *dirwin) 1066 { 1067 dirwin->highlight = MIN(dirwin->highlight + dirwin->maxy/2, 1068 MAX(dirwin->filecount-1, 0)); 1069 } 1070 1071 void 1072 move_up(DirWin *dirwin) 1073 { 1074 if (dirwin->highlight > 0) 1075 dirwin->highlight--; 1076 } 1077 1078 void 1079 move_down(DirWin *dirwin) 1080 { 1081 if (dirwin->highlight < dirwin->filecount-1) 1082 dirwin->highlight++; 1083 } 1084 1085 void 1086 next_search(void) 1087 { 1088 if (searchc == 0) 1089 return; 1090 1091 for (size_t i = curwin.highlight; i < MAX(curwin.filecount-1, 0); i++) { 1092 if ((strstr(curwin.winfiles[i+1].d_name, searchq) != NULL)) { 1093 curwin.highlight = i+1; 1094 return; 1095 } 1096 } 1097 } 1098 1099 void 1100 prev_search(void) 1101 { 1102 if (searchc == 0) 1103 return; 1104 1105 for (size_t i = curwin.highlight; i > 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 free_dirwin(DirWin *dirwin) 1115 { 1116 free(dirwin->winfiles); 1117 dirwin->filecount = 0; 1118 dirwin->winfiles = NULL; 1119 dirwin->message = NULL; 1120 } 1121 1122 void 1123 clean(void) 1124 { 1125 wclear(curwin.window); 1126 wclear(parwin.window); 1127 wclear(childwin.window); 1128 1129 free(username); 1130 free(userhome); 1131 1132 clear(); 1133 1134 delwin(parwin.window); 1135 delwin(curwin.window); 1136 delwin(childwin.window); 1137 delwin(stdscr); 1138 1139 free_dirwin(&curwin); 1140 free_dirwin(&parwin); 1141 free_dirwin(&childwin); 1142 1143 clear_selected(); 1144 1145 endwin(); 1146 } 1147 1148 void 1149 fatal(const char *fmt, ...) 1150 { 1151 va_list ap; 1152 1153 va_start(ap, fmt); 1154 vfprintf(stderr, fmt, ap); 1155 va_end(ap); 1156 1157 clean(); 1158 1159 exit(EXIT_FAILURE); 1160 } 1161 1162 bool 1163 is_dir(DirWin *dirwin, size_t index) 1164 { 1165 struct stat statbuf; 1166 char linkpath[PATH_MAX], abspath[PATH_MAX]; 1167 1168 if (dirwin->filecount <= 0) 1169 return FALSE; 1170 1171 if (dirwin->winfiles[index].d_type == DT_DIR) 1172 return TRUE; 1173 1174 get_fullpath(linkpath, dirwin, index); 1175 1176 if (dirwin->winfiles[index].d_type == DT_LNK) 1177 return (lstat(realpath(linkpath, abspath), &statbuf) >= 0) && S_ISDIR(statbuf.st_mode); 1178 1179 return FALSE; 1180 } 1181 1182 char * 1183 prompt_answer(char *buf, size_t size, const char *question) 1184 { 1185 move(maxy-1, 0); 1186 clrtoeol(); 1187 1188 attron(COLOR_PAIR(PROMPT_COLOR)); 1189 addstr(question); 1190 attroff(COLOR_PAIR(PROMPT_COLOR)); 1191 1192 echo(); 1193 curs_set(1); 1194 getnstr(buf, size); 1195 noecho(); 1196 curs_set(0); 1197 1198 return buf; 1199 } 1200 1201 bool 1202 prompt_confirm(size_t size, const char *fmt, ...) 1203 { 1204 char response[1], question[size]; 1205 va_list ap; 1206 1207 va_start(ap, fmt); 1208 vsnprintf(question, size, fmt, ap); 1209 va_end(ap); 1210 1211 prompt_answer(response, 1, question); 1212 1213 return strcasecmp(response, "y") == 0; 1214 } 1215 1216 int 1217 compare_file(const void *a, const void *b) 1218 { 1219 int typecompare; 1220 const WinFile *ma = a; 1221 const WinFile *mb = b; 1222 1223 if ((typecompare = ma->d_type - mb->d_type) == 0) 1224 return strcmp(ma->d_name, mb->d_name); 1225 1226 return typecompare; 1227 } 1228 1229 int 1230 get_mime(char *buf, size_t bufsize, const char *path) 1231 { 1232 char *cmdfmt = "xdg-mime query filetype \"%s\""; 1233 size_t size = PATH_MAX + strlen(cmdfmt); 1234 char cmd[size]; 1235 1236 snprintf(cmd, size, cmdfmt, path); 1237 1238 return read_command(buf, bufsize, cmd); 1239 } 1240 1241 int 1242 get_mime_default(char *buf, size_t bufsize, const char *mime) 1243 { 1244 char *cmdfmt = "xdg-mime query default \"%s\""; 1245 size_t cmdsize = MIME_MAX + strlen(cmdfmt); 1246 char cmd[cmdsize]; 1247 1248 snprintf(cmd, cmdsize, cmdfmt, mime); 1249 1250 return read_command(buf, bufsize, cmd); 1251 } 1252 1253 int 1254 read_command(char *buf, size_t bufsize, const char *cmd) 1255 { 1256 FILE *file; 1257 1258 if ((file = popen(cmd, "r")) == NULL) { 1259 buf = NULL; 1260 return 1; 1261 } 1262 1263 if (fgets(buf, bufsize, file) == NULL) { 1264 buf = NULL; 1265 pclose(file); 1266 return 2; 1267 } 1268 1269 buf[strlen(buf)-1] = '\0'; 1270 1271 pclose(file); 1272 1273 return 0; 1274 } 1275 1276 void 1277 run_command(size_t size, const char *fmt, ...) 1278 { 1279 va_list ap; 1280 char cmd[size]; 1281 1282 va_start(ap, fmt); 1283 vsnprintf(cmd, size, fmt, ap); 1284 va_end(ap); 1285 1286 system(cmd); 1287 } 1288 1289 int 1290 make_dir(const char *path) 1291 { 1292 char name[PATH_MAX]; 1293 1294 prompt_answer(name, PATH_MAX, "Name of directory? "); 1295 return mkdir(name, 0755); 1296 } 1297 1298 int 1299 make_file(const char *path) 1300 { 1301 FILE *fptr; 1302 char name[PATH_MAX]; 1303 1304 prompt_answer(name, PATH_MAX, "Name of file? "); 1305 1306 if (strlen(name) > 0) { 1307 if ((fptr = fopen(name, "w")) == NULL) 1308 fatal("Error opening file \"%s\"\n", name); 1309 fclose(fptr); 1310 } 1311 } 1312 1313 int 1314 rm_file(const char *fname) 1315 { 1316 char *msg, *rmdirfmt = "rm -rf \"%s\"", *pfmt = "Remove %s? (y/N) "; 1317 int res = 0; 1318 size_t cmdsize = PATH_MAX + strlen(rmdirfmt), psize = strlen(pfmt) + strlen(fname); 1319 1320 if (prompt_confirm(psize, pfmt, fname)) { 1321 if (is_dir(&curwin, curwin.highlight)) 1322 run_command(cmdsize, rmdirfmt, fname); 1323 else 1324 res = remove(fname); 1325 curwin.highlight = MAX(curwin.highlight-1, 0); 1326 } 1327 1328 if (res != 0) { 1329 switch (errno) { 1330 case EACCES: 1331 msg = "No permission."; 1332 break; 1333 case EEXIST: 1334 case ENOTEMPTY: 1335 msg = "Directory is not empty."; 1336 break; 1337 default: 1338 msg = "Could not remove file."; 1339 break; 1340 } 1341 move(maxy-1, 0); 1342 clrtoeol(); 1343 addstr(msg); 1344 print_bot = FALSE; 1345 } 1346 1347 return res; 1348 } 1349 1350 int 1351 rename_file(const char *fname) 1352 { 1353 char name[PATH_MAX]; 1354 1355 prompt_answer(name, PATH_MAX, "New name? "); 1356 1357 if (strlen(name) > 0) 1358 return rename(fname, name); 1359 1360 return 0; 1361 } 1362 1363 int 1364 make_mod(void) 1365 { 1366 char name[3]; 1367 bool isvalid; 1368 1369 prompt_answer(name, 3, "File mods (numeric)? "); 1370 1371 if (strlen(name) == 3) { 1372 isvalid = TRUE; 1373 for (size_t i = 0; i < 3; i++) { 1374 if (!isdigit(name[i])) 1375 isvalid = FALSE; 1376 } 1377 if (isvalid) 1378 chmod(curwin.winfiles[curwin.highlight].d_name, strtol(name, NULL, 8)); 1379 else 1380 return -1; 1381 } 1382 1383 return 0; 1384 } 1385 1386 int 1387 make_access(void) 1388 { 1389 char pathbuf[PATH_MAX], *cmdfmt = "ln -s \"%s\" \"%s\"", *pfmt = "Make access \"%s\"? (y/N) "; 1390 size_t cmdsize = PATH_MAX + strlen(cmdfmt), psize = strlen(pfmt) + PATH_MAX; 1391 1392 if (prompt_confirm(psize, pfmt, curwin.winfiles[curwin.highlight].d_name)) 1393 run_command(cmdsize, cmdfmt, get_fullpath(pathbuf, &curwin, curwin.highlight), LN_ACCESS_DIR); 1394 } 1395 1396 void 1397 exe_selection(SelAction action, const char *askn) 1398 { 1399 char *pfmt = "%s selection (%d files) ? (y/N) "; 1400 size_t cmdsize = PATH_MAX*2 + 20; 1401 1402 if (selected == NULL || selc == 0) 1403 return; 1404 1405 if (askn != NULL && !prompt_confirm(strlen(pfmt) + strlen(askn) + 5, pfmt, askn, selc)) 1406 return; 1407 1408 switch (action) { 1409 case COPY: 1410 for (size_t i = 0; i <selc; i++) 1411 run_command(cmdsize, "cp -rf \"%s\" \"%s\"", selected[i], curwin.path); 1412 break; 1413 case REMOVE: 1414 for (size_t i = 0; i <selc; i++) 1415 run_command(cmdsize, "rm -rf \"%s\"", selected[i]); 1416 break; 1417 case MOVE: 1418 for (size_t i = 0; i <selc; i++) 1419 run_command(cmdsize, "mv \"%s\" \"%s\"", selected[i], curwin.path); 1420 break; 1421 default: 1422 break; 1423 } 1424 1425 clear_selected(); 1426 curwin.highlight = 0; 1427 set_win_files(&curwin); 1428 set_win_files(&parwin); 1429 update_child_win(); 1430 } 1431 1432 void 1433 select_file(const char *path) 1434 { 1435 if (selc == 0 && ((selected = (char**) malloc(sizeof(char *)))) == NULL) 1436 fatal("Fatal: failed to malloc selected.\n"); 1437 1438 if (remove_selected(path)) 1439 return; 1440 1441 if ((selected = (char**) realloc(selected, (selc+1) * sizeof(char *))) == NULL) 1442 fatal("Fatal: failed to realloc.\n"); 1443 1444 selected[selc] = strdup(path); 1445 (selc)++; 1446 } 1447 1448 void 1449 clear_selected(void) 1450 { 1451 for (size_t i = 0; i < selc; i++) 1452 free(selected[i]); 1453 1454 free(selected); 1455 selected = NULL; 1456 selc = 0; 1457 } 1458 1459 void 1460 clear_search(void) 1461 { 1462 for (size_t i = 0; i < SEARCHLEN; i++) 1463 searchq[i] = '\0'; 1464 searchc = 0; 1465 } 1466 1467 char * 1468 cpstr(char *dest, const char *src) 1469 { 1470 size_t cplen = strlen(src); 1471 memcpy(dest, src, cplen); 1472 dest[cplen] = '\0'; 1473 return dest; 1474 } 1475 1476 char * 1477 get_fullpath(char *buf, DirWin *dirwin, size_t index) 1478 { 1479 index = MIN(index, dirwin->filecount-1); 1480 index = MAX(index, 0); 1481 1482 if (strcmp(dirwin->path, "/") == 0) 1483 snprintf(buf, PATH_MAX, "/%s", dirwin->winfiles[index].d_name); 1484 else 1485 snprintf(buf, PATH_MAX, "%s/%s", dirwin->path, dirwin->winfiles[index].d_name); 1486 1487 return buf; 1488 } 1489 1490 char * 1491 replace_home(char *buf, const char *path) 1492 { 1493 char *envv = "$HOME"; 1494 1495 if ((strstr(path, envv) != NULL)) { 1496 cpstr(buf, userhome); 1497 strcat(buf, path + strlen(envv)); 1498 } 1499 return buf; 1500 } 1501 1502 char * 1503 get_dirname(char *buf, const char *path) 1504 { 1505 cpstr(buf, path); 1506 dirname(buf); 1507 return buf; 1508 }