/* __ _ _ ___ / _(_) | ___ ___ / __| |_| | |/ _ \/ __| | (__| _| | | __/\__ \ \___|_| |_|_|\___||___/ */ ///////////// // HEADERS // ///////////// #include #include #include #include #include #include #include #include #include #include ////////////////////// // GLOBAL VARIABLES // ////////////////////// /* Base directory to be used for sorting `dir` for current_win `next_dir` for preview_win */ char sort_dir[250]; /* Stores the path for the cache directory */ char cache_path[250]; /* stores the path for the clipboard file */ char clipboard_path[250]; /* stores the path for the temp clipboard file */ char temp_clipboard_path[250]; /* stores the path for trash */ char trash_path[250]; ////////////////////// // HELPER FUNCTIONS // ////////////////////// /* Checks if `path` is a file or directory */ int is_regular_file(const char *path) { struct stat path_stat; stat(path, &path_stat); return S_ISREG(path_stat.st_mode); } /* For qsort */ int compare (const void * a, const void * b ) { // They store the full paths of the arguments char temp_filepath1[250]=""; char temp_filepath2[250]=""; // Generate full paths strcat(temp_filepath1,sort_dir); strcat(temp_filepath1,"/"); strcat(temp_filepath1,*(char **)a); strcat(temp_filepath2,sort_dir); strcat(temp_filepath2,"/"); strcat(temp_filepath2,*(char **)b); if(is_regular_file(temp_filepath1) == 0 && is_regular_file(temp_filepath2) == 1) return -1; else if(is_regular_file(temp_filepath1) == 1 && is_regular_file(temp_filepath2) == 0) return 1; else return strcasecmp(*(char **)a, *(char **)b); } /* Opens a file using xdg-open */ void openFile(char *filepath) { pid_t pid; pid = fork(); if (pid == 0) { execl("/usr/bin/xdg-open", "xdg-open", filepath, (char *)0); exit(1); } } /* Checks if path is in clipboard */ int checkClipboard(char *filepath) { FILE *f = fopen(clipboard_path, "r"); char buf[250]; char temp[250]; sprintf(temp,"%s", filepath); temp[strlen(temp)]='\0'; if(f == NULL) { return 0; } while(fgets(buf, 250, (FILE*) f)) { buf[strlen(buf)-1] = '\0'; if(strcmp(temp,buf) == 0) return 1; } fclose(f); return 0; } /* Writes to clipboard */ void writeClipboard(char *filepath) { if( checkClipboard(filepath) == 1 ) return; FILE *f = fopen(clipboard_path,"a+"); if (f == NULL) { endwin(); printf("Error accessing clipboard!\n"); exit(1); } fprintf(f, "%s\n", filepath); fclose(f); } /* Empties Clipboard */ void emptyClipboard() { if( remove(clipboard_path) == -1) { return; } } /* Gets previews of files */ void getPreview(char *filepath, int maxy, int maxx) { pid_t pid; FILE *fp; char buf[64]; pid = fork(); if (pid == 0) { // Stores shell command for getting image dimensions through w3mimgdisplay char getdimensions_command[250]; // Stores shell command for displaying image through w3mimgdisplay char imgdisplay_command[250]; int width; int height; // Get dimensions of image and store it as a string in `buf` sprintf(getdimensions_command,"echo -e '5;%s' | /usr/lib/w3m/w3mimgdisplay",filepath); if((fp = popen(getdimensions_command,"r")) == NULL) { exit(0); } while(fgets(buf,64,fp) != NULL){} // Get Dimensions from `buf` and store them `width` and `height` sscanf(buf,"%d %d", &width, &height); // Set appropriate maxx and maxy so that image displays within the preview_win maxx = maxx * 5; maxy = maxy * 5; // Scale the image if dimensions are bigger than preview_win if(width > maxx) { height = height * maxx/width; width = maxx; } if(height > maxy) { width = width * maxy/height; height = maxy; } // Run the w3mimgdisplay command with appropriate arguments sprintf(imgdisplay_command,"echo -e '0;1;%d;%d;%d;%d;;;;;%s\n4;\n3;' | /usr/lib/w3m/w3mimgdisplay",maxx+maxx/5,8,width,height,filepath); system(imgdisplay_command); exit(1); } } /* Gets path of parent directory */ void getParentPath(char *path) { char *p; p = strrchr(path,'/'); path[p-path] = '\0'; // Parent directory is root if(path[0] != '/') { path[0] = '/'; path[1] = '\0'; } } /* Creates a new window with dimensions `height` and `width` starting at `starty` and `startx` */ WINDOW *create_newwin(int height, int width, int starty, int startx) { WINDOW *local_win; local_win = newwin(height, width, starty, startx); return local_win; } /* Returns number of files in `char* directory` */ int getNumberofFiles(char* directory) { int len=0; DIR *pDir; struct dirent *pDirent; pDir = opendir (directory); if (pDir == NULL) { return -1; } while ((pDirent = readdir(pDir)) != NULL) { // Skip hidden files if(pDirent->d_name[0] != '.' ) len++; } return len; } /* Stores all the file names in `char* directory` to `char *target[]` */ void getFiles(char* directory, char* target[]) { int i = 0; DIR *pDir; struct dirent *pDirent; pDir = opendir (directory); if (pDir == NULL) { return; } while ((pDirent = readdir(pDir)) != NULL) { // Skip hidden files if(pDirent->d_name[0] != '.') target[i++] = strdup(pDirent->d_name); } closedir (pDir); } /* Copy files in clipboard to `present_dir` */ void copyFiles(char *present_dir) { FILE *f = fopen(clipboard_path, "r"); char buf[250]; pid_t pid; int status; if(f == NULL) { return; } while(fgets(buf, 250, (FILE*) f)) { buf[strlen(buf)-1] = '\0'; pid = fork(); if(pid == 0) { execl("/usr/bin/cp","cp","-r",buf,present_dir,(char *)0); exit(1); } } fclose(f); } /* Rename files in clipboard */ void renameFiles() { // For opening clipboard and temp_clipboard FILE *f = fopen(clipboard_path, "r"); FILE *f2; // For storing shell commands char cmd[250]; // Buffers for reading clipboard and temp_clipboard char buf[250]; char buf2[250]; // Counters used when reading clipboard and copylock_path int count = 0; int count2 = 0; // Make `temp_clipboard` sprintf(cmd,"cp %s %s",clipboard_path,temp_clipboard_path); system(cmd); // Exit curses mode and open temp_clipboard_path in vim endwin(); sprintf(cmd,"vim %s",temp_clipboard_path); system(cmd); // Open clipboard and temp_clipboard and mv path from clipboard to adjacent entry in temp_clipboard while(fgets(buf, 250, (FILE*) f)) { count2=-1; f2 = fopen(temp_clipboard_path,"r"); while(fgets(buf2, 250, (FILE*) f2)) { count2++; if(buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = '\0'; if(count2 == count) { if(buf2[strlen(buf2)-1] == '\n') buf2[strlen(buf2)-1] = '\0'; sprintf(cmd,"mv \"%s\" \"%s\"",buf,buf2); system(cmd); } } count++; fclose(f2); } fclose(f); // Remove clipboard and temp_clipboard sprintf(cmd,"rm %s %s",temp_clipboard_path,clipboard_path); system(cmd); // Start curses mode refresh(); } /* Move files in clipboard to `present_dir` */ void moveFiles(char *present_dir) { FILE *f = fopen(clipboard_path, "r"); char buf[250]; pid_t pid; int status; if(f == NULL) { return; } while(fgets(buf, 250, (FILE*) f)) { buf[strlen(buf)-1] = '\0'; pid = fork(); if(pid == 0) { execl("/usr/bin/mv","mv",buf,present_dir,(char *)0); exit(1); } } fclose(f); } /////////////////// // MAIN FUNCTION // /////////////////// int main(int argc, char* argv[]) { // To store number of files in directory int len=0; // Counter variable int i = 0; // Direcotry to be opened char* dir; // Get UID of user uid_t uid = getuid(); // Get home directory of user from UID struct passwd *info = getpwuid(uid); // Make the cache directory struct stat st = {0}; sprintf(cache_path,"%s/.cache/cfiles",info->pw_dir); if (stat(cache_path, &st) == -1) { mkdir(cache_path, 0751); } // Set the path for the clipboard file sprintf(clipboard_path,"%s/clipboard",cache_path); // Set the path for the temp clipboard file sprintf(temp_clipboard_path,"%s/clipboard.tmp",cache_path); // Set the path for trash sprintf(trash_path,"%s/.local/share/Trash/files",info->pw_dir); // No Path is given in arguments // Set Path as $HOME if(argc == 1) { dir = info->pw_dir; } // Path is given in arguments // Set Path as the argument else if(argc == 2) { dir = argv[1]; // Relative Path Given if(dir[0] != '/') { // Add path of $HOME before the Relative Path char temp[250] = ""; strcat(temp,info->pw_dir); strcat(temp,"/"); strcat(temp,dir); dir = temp; } } // Incorrect Useage else { printf("Incorrect Useage\n"); exit(0); } // Stores number of files in the present directory len = 0; // ncurses initialization initscr(); noecho(); curs_set(0); // Shows current directory WINDOW *current_win; // Shows child directory preview WINDOW *preview_win; // Shows status bar WINDOW *status_win; int startx, starty, midx, midy, maxx, maxy; // Index of currently selected item in `char* directories` int selection = 0; // For Storing user keypress char ch; // Index to start printing from `directories` array int start = 0; // Flag to clear preview_win int clearFlag = 0; // Flag is set to 1 when returning from `fzf` int searchFlag = 0; // Flag is set to 1 when user goes up a directory int backFlag = 0; // Stores the last token in the path. For eg, it will store 'a' is path is /b/a char *last; // Main Loop do { // char array to work with strtok() char temp_dir[250]; // Clear the preview_win if(clearFlag == 1) { wclear(preview_win); wrefresh(preview_win); clearFlag = 0; } // Get number of files in the home directory len = getNumberofFiles(dir); // Array of all the files in the current directory char* directories[len]; getFiles(dir, directories); // Sort files by name strcpy(sort_dir,dir); qsort (directories, len, sizeof (char*), compare); // Select the file in `last` and set `start` accordingly if(searchFlag == 1) { searchFlag = 0; last[strlen(last)-1] = '\0'; for(i=0; i maxy) { if(selection > maxy-3) start = selection - maxy + 3; } // Resets everything (Prevents unexpected behaviour after quitting fzf) endwin(); refresh(); } // Select the folder in `last` and set start accordingly if(backFlag == 1) { backFlag = 0; for(i=0; i maxy) { if(selection > maxy-3) start = selection - maxy + 3; } } // Get Size of terminal getmaxyx(stdscr, maxy, maxx); // Save last two rows for status_win maxy = maxy - 2; // Make the two windows side-by-side current_win = create_newwin(maxy, maxx/2+3, 0, 0); preview_win = create_newwin(maxy, maxx/2 -1, 0, maxx/2 + 1); status_win = create_newwin(2, maxx, maxy, 0); // Display Status wmove(status_win,1,1); wprintw(status_win, "%s@%s\t%s", getenv("USER"), getenv("HOSTNAME"), dir); wrefresh(status_win); // Print all the elements and highlight the selection int t = 0; for( i=start; i 0) qsort(next_directories, len_preview, sizeof (char*), compare); // Selection is not a directory if(len_preview != -1) for( i=0; i= maxy) if(selection <= start + maxy/2) { if(start == 0) wclear(current_win); else { start--; wclear(current_win); } } break; // Go down case 'j': selection++; selection = ( selection > len-1 ) ? len-1 : selection; // Scrolling if(len >= maxy) if(selection - 1 > maxy/2) { if(start + maxy - 2 != len) { start++; wclear(current_win); } } break; // Go to child directory or open file case 'l': if(len_preview != -1) { strcpy(dir, next_dir); selection = 0; start = 0; } else { // Open file openFile(next_dir); clearFlag = 1; } break; // Go up a directory case 'h': // Copy present directory to temp_dir to work with strtok() strcpy(temp_dir, dir); strcpy(dir, prev_dir); selection = 0; start = 0; backFlag = 1; // Get the last token in `temp_dir` and store it in `last` char *pch; pch = strtok(temp_dir,"/"); while (pch != NULL) { last = pch; pch = strtok(NULL,"/"); } break; // Goto start case 'g': selection = 0; start = 0; break; // Goto end case 'G': selection = len - 1; if(len > maxy - 2) start = len - maxy + 2; else start = 0; break; // Search using fzf case 'F': sprintf(cmd,"cd %s && fzf",info->pw_dir); if((fp = popen(cmd,"r")) == NULL) { exit(0); } while(fgets(buf,250,fp) != NULL){} char path[250]; sprintf(path, "%s/%s",info->pw_dir,buf); // Copy `path` into `temp_dir` to work with strtok. //Then store the last token in `temp_dir` and store it in `last`. strcpy(temp_dir,path); pch = strtok(temp_dir,"/"); while (pch != NULL) { last = pch; pch = strtok(NULL,"/"); } getParentPath(path); strcpy(dir,path); selection = 0; start = 0; clearFlag = 1; searchFlag = 1; break; // Search in the same directory case 'f': sprintf(cmd,"cd %s && ls | fzf",dir); if((fp = popen(cmd,"r")) == NULL) { exit(0); } while(fgets(buf,250,fp) != NULL){} sprintf(path, "%s/%s",info->pw_dir,buf); strcpy(temp_dir,path); pch = strtok(temp_dir,"/"); while (pch != NULL) { last = pch; pch = strtok(NULL,"/"); } getParentPath(path); strcpy(dir,path); selection = 0; start = 0; clearFlag = 1; searchFlag = 1; break; // Opens bash shell in present directory case 'S': strcpy(temp_dir,"cd "); strcat(temp_dir,dir); strcat(temp_dir," && bash"); endwin(); system(temp_dir); start = 0; selection = 0; refresh(); break; // Bulk Rename case 'a': if( access( clipboard_path, F_OK ) == -1 ) { strcpy(temp_dir,dir); strcat(temp_dir,"/"); strcat(temp_dir,directories[selection]); writeClipboard(temp_dir); } renameFiles(); break; // Write to clipboard case ' ': strcpy(temp_dir,dir); strcat(temp_dir,"/"); strcat(temp_dir,directories[selection]); writeClipboard(temp_dir); break; // Empty Clipboard case 'u': emptyClipboard(); break; // Copy clipboard contents to present directory case 'y': copyFiles(dir); break; // Moves clipboard contents to present directory case 'v': moveFiles(dir); break; // Moves clipboard contents to trash case 'd': secondKey = wgetch(current_win); if( secondKey == 'D' ) moveFiles(trash_path); break; // Clear Preview Window case 'r': clearFlag = 1; break; } // Free Memory for( i=0; i